Python factory method pattern

The Factory Method Design Pattern in Python

In this article, we’ll be diving into the Factory Method Design Pattern, implemented in Python.

Design Patterns define tried and tested solutions to various recurring problems in software development. They do not represent actual code, but rather ways in which we can organize our code for the optimum results.

In a world of limited resources, Design Patterns help us achieve the most results with the least amount of used resources. It is also important to note that Design Patterns do not apply to all situations and it is crucial to assess the problem at hand in order to choose the best approach for that particular scenario.

Design Patterns are divided into a few broad categories, though mainly into Creational Patterns, Structural Patterns, and Behavioral Patterns.

The Factory Method pattern is a Creational Design Pattern.

The Factory Method Design Pattern

Definition

The Factory Method is used in object-oriented programming as a means to provide factory interfaces for creating objects. These interfaces define the generic structure, but don’t initialize objects. The initialization is left to more specific subclasses.

Читайте также:  Css background color fill margin

The parent class/interface houses all the standard and generic behaviour that can be shared across subclasses of different types. The subclass is in turn responsible for the definition and instantiation of the object based on the superclass.

Motivation

The main motivation behind the Factory Method Design Pattern is to enhance loose coupling in code through the creation of an abstract class that will be used to create different types of objects that share some common attributes and functionality.

This results in increased flexibility and reuse of code because the shared functionality will not be rewritten having been inherited from the same class. This design pattern is also known as a Virtual Constructor.

The Factory Method design pattern is commonly used in libraries by allowing clients to choose what subclass or type of object to create through an abstract class.

A Factory Method will receive information about a required object, instantiate it and return the object of the specified type. This gives our application or library a single point of interaction with other programs or pieces of code, thereby encapsulating our object creation functionality.

Factory Method Implementation

Our program is going to be a library used for handling shape objects in terms of creation and other operations such as adding color and calculating the area of the shape.

Users should be able to use our library to create new objects. We can start by creating single individual shapes and availing them as is but that would mean that a lot of shared logic will have to be rewritten for each and every shape we have available.

The first step to solving this repetition would be to create a parent shape class that has methods such as calculate_area() and calculate_perimeter() , and properties such as dimensions.

The specific shape objects will then inherit from our base class. To create a shape, we will need to identify what kind of shape is required and create the subclass for it.

We will start by creating an abstract class to represent a generic shape:

import abc class Shape(metaclass=abc.ABCMeta): @abc.abstractmethod def calculate_area(self): pass @abc.abstractmethod def calculate_perimeter(self): pass 

This is the base class for all of our shapes. Let’s go ahead and create several concrete, more specific shapes:

class Rectangle(Shape): def __init__(self, height, width): self.height = height self.width = width def calculate_area(self): return self.height * self.width def calculate_perimeter(self): return 2 * (self.height + self.width) class Square(Shape): def __init__(self, width): self.width = width def calculate_area(self): return self.width ** 2 def calculate_perimeter(self): return 4 * self.width class Circle(Shape): def __init__(self, radius): self.radius = radius def calculate_area(self): return 3.14 * self.radius * self.radius def calculate_perimeter(self): return 2 * 3.14 * self.radius 

So far, we have created an abstract class and extended it to suit different shapes that will be available in our library. In order to create the different shape objects, clients will have to know the names and details of our shapes and separately perform the creation.

This is where the Factory Method comes into play.

The Factory Method design pattern will help us abstract the available shapes from the client, i.e. the client does not have to know all the shapes available, but rather only create what they need during runtime. It will also allow us to centralize and encapsulate the object creation.

Let us achieve this by creating a ShapeFactory that will be used to create the specific shape classes based on the client’s input:

class ShapeFactory: def create_shape(self, name): if name == 'circle': radius = input("Enter the radius of the circle: ") return Circle(float(radius)) elif name == 'rectangle': height = input("Enter the height of the rectangle: ") width = input("Enter the width of the rectangle: ") return Rectangle(int(height), int(width)) elif name == 'square': width = input("Enter the width of the square: ") return Square(int(width)) 

Free eBook: Git Essentials

Check out our hands-on, practical guide to learning Git, with best-practices, industry-accepted standards, and included cheat sheet. Stop Googling Git commands and actually learn it!

This is our interface for creation. We don’t call the constructors of concrete classes, we call the Factory and ask it to create a shape.

Our ShapeFactory works by receiving information about a shape such as a name and the required dimensions. Our factory method create_shape() will then be used to create and return ready objects of the desired shapes.

The client doesn’t have to know anything about the object creation or specifics. Using the factory object, they can create objects with minimal knowledge of how they work:

def shapes_client(): shape_factory = ShapeFactory() shape_name = input("Enter the name of the shape: ") shape = shape_factory.create_shape(shape_name) print(f"The type of object created: type(shape)>") print(f"The area of the is: ") print(f"The perimeter of the is: ") 

Running this code will result in:

Enter the name of the shape: circle Enter the radius of the circle: 7 The type of object created: The area of the circle is: 153.86 The perimeter of the circle is: 43.96 

Or, we could build another shape:

Enter the name of the shape: square Enter the width of the square: 5 The type of object created: The area of the square is: 25 The perimeter of the square is: 20 

What’s worth noting is that besides the client not having to know much about the creation process — when we’d like to instantiate an object, we don’t call the constructor of the class. We ask the factory to do this for us based on the info we pass to the create_shape() function.

Pros and Cons

Pros

One of the major advantages of using the Factory Method design pattern is that our code becomes loosely coupled in that the majority of the components of our code are unaware of other components of the same codebase.

This results in code that is easy to understand and test and add more functionality to specific components without affecting or breaking the entire program.

The Factory Method design pattern also helps uphold the Single Responsibility Principle where classes and objects that handle specific functionality resulting in better code.

Cons

Creation of more classes eventually leads to less readability. If combined with an Abstract Factory (factory of factories), the code will soon become verbose, though, maintainable.

Conclusion

In conclusion, the Factory Method Design Pattern allows us to create objects without specifying the exact class required to create the particular object. This allows us to decouple our code and enhances its reusability.

It is important to note that, just like any other design pattern, it is only suitable for specific situations and not every development scenario. An assessment of the situation at hand is crucial before deciding to implement the Factory Method Design Pattern to reap the benefits of the pattern.

Источник

Factory Pattern in Python

Learn Algorithms and become a National Programmer

In this article, we have explained the idea of Factory Pattern in Python with a complete Python implementation example.

Table of contents:

  1. Introduction to Factory Pattern
  2. Reasons for using Factory Method
  3. Implementations without using Factory Method
  4. Problem with using the non-factory method
  5. Implementations using Factory Method
  6. Solution offered by using Factory Method

Introduction to Factory Pattern

Factory method also known as Virtual Constructor is a Creational Design Pattern which allow reusing of existing code and increased flexibilty. This define an interface to create objects in a superclass but it is the subclasses that determine the type of object that will be created.

Reasons for using Factory Method

The Factory Method Design Pattern seek to loosen the coupling in the code. This is done by creating an abstract class which will be then be used to create various different types of objects.

The objects created will share some common attributes and functionality. Thus leading to the reuse of existing code and increased flexibilty because the shared functionality will inherited from the same class.

Implementations without using Factory Method

class BeefBurger: def cook(self): print('Beef Burgers') class ChickenBurger: def cook(self): print('Chicken Burger') class FishBurger: def cook(self): print('Fish Burger') if __name__ == '__main__': bb = BeefBurger() cb = ChickenBurger() fb = FishBurger() # Create burger "Beef burger" bb.cook() # Create burger "Chicken burger" cb.cook() # Create burger "Fish burger" fb.cook() 

Output

Beef Burgers Chicken Burger Fish Burger 

Problem with using the non-factory method

Let say a new class CheeseBurger is required, the common attributes and functionality among the other Burger classes will need to be repeated in the codes. If there is an update in the common attributes and functionality, all the Burger classes will need to be updated.

Implementations using Factory Method

We start by creating the Object Interface, the common attributes and functionality among the various Burgers classes are written and stored in the Burger class.

# Object Interface class Burger(object): def cook(self): pass class BeefBurgers(Burger): def cook(self): print('Beef Burgers') class ChickenBurger(Burger): def cook(self): print('Chicken Burger') class FishBurger(Burger): def cook(self): print('Fish Burger') 

Next the Factory Object is created. The library’s Factory (BurgerStoreFactory) will accept an input (i.e. what burger) and decide which object type (burger) to create.

# Factory Object class BurgerStoreFactory(object): @staticmethod def getBurger(name): if name == 'Beef': return BeefBurgers() elif name == 'Chicken': return ChickenBurger() elif name == 'Fish': return FishBurger() 

In the client code portion, we will make a call to library’s Factory (BurgerStoreFactory) create method and pass the type of object (burger) we want without having to worry about how the creation of objects is actually implemented

# Client Code if __name__ == '__main__': # Create burger "Beef burger" b = BurgerStoreFactory() burger = b.getBurger('Beef') burger.cook() # Create burger "Chicken burger" burger = b.getBurger('Chicken') burger.cook() # Create burger "Fish burger" burger = b.getBurger('Fish') burger.cook() 

Output

Beef Burgers Chicken Burger Fish Burger 

Factory-Method-Python-UML

Solution offered by using Factory Method

In this instance, the selection of type for object creation is decoupled from the Client code. The common attributes and functionality among the other Burger classes will not repeated in the codes.

Here is the entire code block for reference

# Object Interface class Burger(object): def cook(self): pass class BeefBurgers(Burger): def cook(self): print('Beef Burgers') class ChickenBurger(Burger): def cook(self): print('Chicken Burger') class FishBurger(Burger): def cook(self): print('Fish Burger') # Factory Object class BurgerStoreFactory(object): @staticmethod def getBurger(name): if name == 'Beef': return BeefBurgers() elif name == 'Chicken': return ChickenBurger() elif name == 'Fish': return FishBurger() # Client Code if __name__ == '__main__': # Create burger "Beef burger" b = BurgerStoreFactory() burger = b.getBurger('Beef') burger.cook() # Create burger "Chicken burger" burger = b.getBurger('Chicken') burger.cook() # Create burger "Fish burger" burger = b.getBurger('Fish') burger.cook() 

With this article at OpenGenus, you must have the complete idea of Factory Pattern in Python.

CHEW Chee Keng

Источник

Оцените статью