본문 바로가기
Learning Python Design Patterns

The Factory Pattern

by 자동매매 2023. 3. 29.

The Factory Pattern – Building Factories to Create Objects

In the previous chapter, you learned about Singleton design patterns—what they are and how they are used in the real world along with the Python implementation. The Singleton design pattern is one of the Creational design patterns. In this chapter, we move ahead and learn about another creational pattern, the Factory pattern.

The Factory pattern is arguably the most used design pattern. In this chapter, we will understand the concept of Factory and go through the Simple Factory pattern. You will then learn about the Factory method pattern and Abstract Factory pattern with a UML diagram, real-world scenarios, and Python v3.5 implementations. We'll also compare the Factory method and Abstract Factory method.

In this chapter, we will cover the following topics in brief:

  • Understanding the Simple Factory design pattern
  • Discussing the Factory method and Abstract Factory method and their differences
  • Implementing real-world scenarios with the Python code implementation
  • Discussing the advantages and disadvantages of the patterns and their comparisons

Understanding the Factory pattern

In object-oriented programming, the term factory means a class that is responsible for creating objects of other types. Typically, the class that acts as a factory has an object and methods associated with it. The client calls this method with certain parameters; objects of desired types are created in turn and returned to the client by the factory.

[ 25 ]
Chapter 35*

So the question here really is, why do we need a factory when the client can directly create an object? The answer is, a factory provides certain advantages that are listed here:

  • The first advantage is loose coupling in which object creation can be independent of the class implementation.
  • The client need not be aware of the class that creates the object which, in turn, is utilized by the client. It is only necessary to know the interface, methods, and parameters that need to be passed to create objects of the desired type. This simplifies implementations for the client.
  • Adding another class to the factory to create objects of another type can be easily done without the client changing the code. At a minimum, the client needs to pass just another parameter.
  • The factory can also reuse the existing objects. However, when the client does direct object creation, this always creates a new object.

Let's consider the situation of a manufacturing company that manufactures toys— cars or dolls. Let's say that a machine in the company is currently manufacturing toy cars. Then, the CEO of the company feels that there is an urgent need to manufacture dolls based on the demand in the market. This situation calls for the Factory pattern. In this case, the machine becomes the interface and the CEO is the client. The CEO

is only bothered about the object (or the toy) to be manufactured and knows the interface—the machine—that can create the object.

There are three variants of the Factory pattern:

  • Simple Factory pattern: This allows interfaces to create objects without exposing the object creation logic.
  • Factory method pattern: This allows interfaces to create objects, but defers the decision to the subclasses to determine the class for object creation.
  • Abstract Factory pattern: An Abstract Factory is an interface to create related objects without specifying/exposing their classes. The pattern provides objects of another factory, which internally creates other objects.

The Simple Factory pattern

For some, Simple Factory is not a pattern in itself. It is more of a concept that developers need to know before they know more about the Factory method and Abstract Factory method. The Factory helps create objects of different types rather than direct object instantiation.

Let's understand this with the help of the following diagram. Here, the client class uses the Factory class, which has the create_type() method. When the client calls

the create_type() method with the type parameters, based on the parameters passed, the Factory returns Product1 or Product2:

A UML Diagram of Simple Factory

Let's now understand the Simple Factory pattern with the help of a Python v3.5 code example. In the following snippet, we create an Abstract product called Animal. Animal is an abstract base class (ABCMeta is Python's special metaclass to make a class Abstract) and has the do_say() method. We create two products (Cat and

Dog) from the Animal interface and implement do_say() with appropriate sounds

that these animals make. ForestFactory is a factory that has the make_sound() method. Based on the type of argument passed by the client, an appropriate Animal instance is created at runtime and the right sound is printed out:

from abc import ABCMeta, abstractmethod

class Animal(metaclass = ABCMeta): @abstractmethod

def do_say(self):

pass

class Dog(Animal):

def do_say(self):

print("Bhow Bhow!!")

class Cat(Animal):

def do_say(self):

print("Meow Meow!!")

## forest factory defined

class ForestFactory(object):

def make_sound(self, object_type):

return eval(object_type)().do_say()

## client code

if __name__ == '__main__':

ff = ForestFactory()

animal = input("Which animal should make_sound Dog or Cat?") ff.make_sound(animal)

The following is the output of the preceding code snippet:

The Factory Method pattern

The following points help us understand the factory method pattern:

  • We define an interface to create objects, but instead of the factory being responsible for the object creation, the responsibility is deferred to the subclass that decides the class to be instantiated.
  • The Factory method creation is through inheritance and not through instantiation.
  • The Factory method makes the design more customizable. It can return the same instance or subclass rather than an object of a certain type (as in the simple factory method).

A UML diagram for the Factory method

In the preceding UML diagram, we have an abstract class, Creator, that contains factoryMethod(). The factoryMethod() method has the responsibility of creating objects of a certain type. The ConcreteCreator class has factoryMethod() that implements the Creator abstract class, and this method can change the created object at runtime. ConcreteCreator creates ConcreteProduct and makes sure that the object it creates implements the Product class and provides implementation for all the methods in the Product interface.

In brief, factoryMethod() of the Creator interface and the ConcreteCreator class decides which subclass of Product to create. Thus, the Factory method pattern defines an interface to create an object, but defers the decision ON which class to instantiate to its subclasses.

Implementing the Factory Method

Let's take a real-world scenario to understand the Factory method implementation. Consider that we would like to create profiles of different types on social networks such as LinkedIn and Facebook for a person or company. Now, each of these profiles would have certain sections. In LinkedIn, you would have a section on patents that an individual has filed or publications s/he has written. On Facebook, you'll see sections in an album of pictures of your recent visit to a holiday place. Additionally, in both these profiles, there'd be a common section on personal information. So, in brief, we want to create profiles of different types with the right sections being added to the profile.

Let's now take a look at the implementation. In the following code example, we will start by defining the Product interface. We will create a Section abstract class that defines how a section will be. We will keep it very simple and provide an abstract method, describe().

We now create multiple ConcreteProduct classes, PersonalSection, AlbumSection, PatentSection, and PublicationSection. These classes implement the describe() abstract method and print their respective section names:

from abc import ABCMeta, abstractmethod

class Section(metaclass=ABCMeta): @abstractmethod

def describe(self): pass

class PersonalSection(Section):

def describe(self):

print("Personal Section")

class AlbumSection(Section):

def describe(self):

print("Album Section")

class PatentSection(Section):

def describe(self):

print("Patent Section")

class PublicationSection(Section):

def describe(self):

print("Publication Section")

We create a Creator abstract class that is named Profile. The Profile [Creator] abstract class provides a factory method, createProfile(). The createProfile() method should be implemented by ConcreteClass to actually create the profiles

with appropriate sections. The Profile abstract class is not aware of the sections

that each profile should have. For example, a Facebook profile should have personal information and album sections. So we will let the subclass decide this.

We create two ConcreteCreator classes, linkedin and facebook. Each of these classes implement the createProfile() abstract method that actually creates (instantiates) multiple sections (ConcreteProducts) at runtime:

class Profile(metaclass=ABCMeta):

def __init__(self):

self.sections = [] self.createProfile() @abstractmethod

def createProfile(self): pass

def getSections(self):

return self.sections

def addSections(self, section): self.sections.append(section)

class linkedin(Profile):

def createProfile(self): self.addSections(PersonalSection()) self.addSections(PatentSection()) self.addSections(PublicationSection())

class facebook(Profile):

def createProfile(self): self.addSections(PersonalSection()) self.addSections(AlbumSection())

We finally write client code that determines which Creator class to instantiate in order to create a profile of the desired choice:

if __name__ == '__main__':

profile_type = input("Which Profile you'd like to create? [LinkedIn or FaceBook]")

profile = eval(profile_type.lower())()

print("Creating Profile..", type(profile).__name__)

print("Profile has sections --", profile.getSections())

If you now run the complete code, it'll first ask you to enter the name of the profile that you'd like to create. In the following screenshot, we say Facebook. It then instantiates the facebook [ConcreateCreator] class. This internally creates ConcreteProduct(s), that is, it instantiates PersonalSection and AlbumSection.

If Linkedin is chosen, then PersonalSection, PatentSection,

and PublicationSection are created.

The following is the output of the preceding code snippet:

Advantages of the Factory method pattern

As you have now learned the Factory method pattern and how to implement Factory methods, let's see the advantages of the Factory method pattern:

  • It brings in a lot of flexibility and makes the code generic, not being tied to a certain class for instantiation. This way, we're dependent on the interface (Product) and not on the ConcreteProduct class.
  • There's loose coupling, as the code that creates the object is separate from the code that uses it. The client need not bother about what argument to pass and which class to instantiate. The addition of new classes is easy and involves low maintenance.

The Abstract Factory pattern

The main objective of the Abstract Factory pattern is to provide an interface to create families of related objects without specifying the concrete class. While the factory method defers the creation of the instance to the subclasses, the goal of Abstract Factory method is to create families of related objects:

A UML Diagram for the Abstract Factory pattern

As shown in the diagram, ConcreteFactory1 and ConcreteFactory2 are created from the AbstractFactory interface. This interface has methods to create multiple products.

ConcreteFactory1 and ConcreteFactory2 implement AbstractFactory and create instances of ConcreteProduct1, ConcreteProduct2, AnotherConcreteProduct1, and AnotherConcreteProduct2.

ConcreteProduct1 and ConcreteProduct2 are in turn created from

the AbstractProduct interface, and AnotherConcreteProduct1 and AnotherConcreteProduct2 are created from the AnotherAbstractProduct interface.

In effect, Abstract Factory patterns make sure that the client is isolated from the creation of objects but allowed to use the objects created. The client has the ability to access objects only through an interface. If products of one family

are to be used, Abstract Factory pattern helps the client use the objects from one/ family at a time. For example, if an application under development is supposed to be platform-independent, then it needs to abstract dependencies such as OS, file system calls, among others. Abstract Factory pattern takes care of creating the required services for the entire platform so that the client doesn't have to create platform objects directly.

Implementing the Abstract Factory pattern

Consider the case of your favorite pizza place. It serves multiple types of pizzas, right? Wait, hold on, I know you want to order one right away, but let's just get back to the example for now!

Now, imagine that we create a pizza store where you are served with delicious Indian and American pizzas. For this, we first create an abstract base class, PizzaFactory (AbstractFactory in the preceding UML diagram). The PizzaFactory class has two

abstract methods, createVegPizza() and createNonVegPizza(), that need to be implemented by ConcreteFactory. In this example, we create two concrete factories, namely, IndianPizzaFactory and USPizzaFactory. Look at the following code implementation for the concrete factories:

from abc import ABCMeta, abstractmethod class PizzaFactory(metaclass=ABCMeta):

@abstractmethod

def createVegPizza(self): pass

@abstractmethod

def createNonVegPizza(self): pass

class IndianPizzaFactory(PizzaFactory):

def createVegPizza(self):

return DeluxVeggiePizza()

def createNonVegPizza(self): return ChickenPizza()

class USPizzaFactory(PizzaFactory):

def createVegPizza(self):

return MexicanVegPizza()

def createNonVegPizza(self): return HamPizza()

Now, let's move ahead and define AbstractProducts. In the following code, we create two abstract classes, VegPizza and NonVegPizza (AbstractProduct and AnotherAbstractProduct in the preceding UML diagram]. They individually have a method defined, prepare() and serve().

The thought process here is that vegetarian pizzas are prepared with an appropriate crust, vegetables, and seasoning, and nonvegetarian pizzas are served with nonvegetarian toppings on top of vegetarian pizzas.

We then define ConcreteProducts for each of the AbstractProducts. Now, in this case, we create DeluxVeggiePizza and MexicanVegPizza and implement the prepare() method. ConcreteProducts1 and ConcreteProducts2 would represent these classes from the UML diagram.

Later, we define ChickenPizza and HamPizza and implement the serve() method— these represent AnotherConcreteProducts1 and AnotherConcreteProducts2:

class VegPizza(metaclass=ABCMeta): @abstractmethod

def prepare(self, VegPizza): pass

class NonVegPizza(metaclass=ABCMeta): @abstractmethod

def serve(self, VegPizza): pass

class DeluxVeggiePizza(VegPizza):

def prepare(self):

print("Prepare ", type(self).__name__)

class ChickenPizza(NonVegPizza): def serve(self, VegPizza):

print(type(self).__name__, " is served with Chicken on ", type(VegPizza).__name__)

class MexicanVegPizza(VegPizza):

def prepare(self):

print("Prepare ", type(self).__name__)

class HamPizza(NonVegPizza):

def serve(self, VegPizza):

print(type(self).__name__, " is served with Ham on ", type(VegPizza).__name__)

When an end user approaches PizzaStore and asks for an American nonvegetarian pizza, USPizzaFactory is responsible for preparing the vegetarian pizza as the base and serving the nonvegetarian pizza with ham on top!

class PizzaStore:

def __init__(self):

pass

def makePizzas(self):

for factory in [IndianPizzaFactory(), USPizzaFactory()]: self.factory = factory

self.NonVegPizza = self.factory.createNonVegPizza() self.VegPizza = self.factory.createVegPizza() self.VegPizza.prepare() self.NonVegPizza.serve(self.VegPizza)

pizza = PizzaStore() pizza.makePizzas()

The following is the output of the preceding code example:

The Factory method versus Abstract Factory method

Now that you have learned the Factory method and Abstract Factory method, let's see the comparison of the two:

Factory method Abstract Factory method
This exposes a method to the client to create the objects Abstract Factory method contains one or more factory methods to create a family of related objects
This uses inheritance and subclasses to decide which object to create This uses composition to delegate responsibility to create objects of another class
The factory method is used to create one product Abstract Factory method is about creating families of related products

[ 35 ]
Chapter 37*

Summary

In this chapter, you learned about the Factory design pattern and the context in which it's used. We understood the basics of the Factory, and how it is effectively used in software architecture.

We looked at Simple Factory, where an appropriate instance is created at runtime based on the type of the argument passed by the client.

We also discussed the Factory method pattern, which is a variation of Simple Factory. In this pattern, we defined an interface to create objects, but the creation of objects is deferred to the subclass.

We went on to explore the Abstract Factory method, which provides an interface to create families of related objects without specifying the concrete class.

We also worked out a real-world Python implementation for all the three patterns, and compared the Factory method with Abstract Factory method.

At the end of this chapter, we're now ready to take the next step and study other types of patterns, so stay tuned.

[ 37 ]

'Learning Python Design Patterns' 카테고리의 다른 글

The Proxy Pattern  (0) 2023.03.29
The Façade Pattern  (0) 2023.03.29
The Singleton Design Pattern  (0) 2023.03.29
Introduction to Design Patterns  (0) 2023.03.29
Index  (0) 2023.03.29

댓글