본문 바로가기
James Cooper - Python Programming with D

The Factory Pattern

by 자동매매 2023. 3. 23.

Creational Patterns

All creational patterns deal with ways to create instances of objects. This is important because your program should not depend on how objects are created and arranged. In Python, of course, the simplest way to create an instance of an object is by creating a variable of that class type.

 

fred = Fred()	# instance of Fred class

 

However, this really amounts to hard coding, depending on how you create the object within your program. In many cases, the exact nature of the object that is created could vary with the needs of the program. Abstracting the creation process into a special “creator” class can make your program more flexible and general.


The Factory method provides a simple decision-making(의사결정) class that returns one of several possible subclasses of an abstract base class, depending on the data provided.
The Abstract Factory method provides an interface to create and return one of several families of related objects.
The Builder pattern separates the construction of a complex object from its representation so that several different representations can be created, depending on the needs of the program.
The Prototype pattern starts with an instantiated class and copies or clones it to make new instances. These instances can then be further tailored using their public methods.
The Singleton pattern defines a class that can have no more than one instance. It provides a single global point of access to that instance.


The Factory Pattern

One type of pattern that we see again and again in OO programs is the Simple Factory pattern. A Simple Factory pattern returns an instance of one of several possible classes, depending on the data provided to it. Usually all the classes it returns have a common parent class and common methods, but each one performs a task differently and is optimized for different kinds of data. This Simple Factory is not one of the 23 GoF patterns, but it serves here as an introduction to the somewhat more subtle Factory Method GoF pattern we’ll discuss shortly.

 

 

How a Factory Works


Let’s consider a simple case where we could use a Factory class. Suppose we have an entry form and want to allow users to enter their names either as “firstname lastname” or as “lastname, firstname”. To simplify this example,
assume that we will always be able to decide the name order by whether there is a comma between the last and first name. Figure 5-1 shows a class diagram for this simple case.

 

Figure 5-1 LastFirst and FirstFirst are derived from Namer. The NameFactory produces one or the other

 

In Figure 5-1, Namer is a base class and classes LastFirst and FirstFirst are derived from it. The NameFactory class decides which of these subclasses to return, depending on the arguments you give it. On the right, a getClass method is defined to be one that passes in some value arg and returns some instance of the class Namer. The programmer doesn’t care which one it returns because they all have the same methods but different implementations. How it decides which one to return is entirely up to the factory. It could be a very complex function, but it is often quite simple.

 

Sample Code

 

Deciding on which of the two cases we just described is a pretty simple sort of decision to make; you could do so with a simple if statement in a single class. But let’s use it here to illustrate how a factory works and what it can produce. We’ll start by defining a simple base class that takes a string and splits it (somehow) into two names:

 

#base Namer class
class Namer():
    def __init__(self):
        self.last=""
        self.first=""

 

In this base class, we don’t compute any names, as such, but we do provide place holders for the first and last names. We’ll store the split first and last names in the Strings first and last, which the subclasses can then access. In this simple example, there is no need for getter and setter methods to access the class instance variables first and last.



The Two Subclasses


Now we can write two very simple subclasses that split the name into two parts in the constructor. In the FirstFirst class, we assume that everything before the last space is part of the first name:

#derived namer class for First <space> Last
class FirstFirst(Namer):
    def __init__(self, namestring):
        super().__init__()
        i = namestring.find(" ")    #find space between names
        if i > 0 :
            names = namestring.split()
            self.first = names[0]
            self.last = names[1]
        else:
            self.last = namestring

 

In the LastFirst class, we assume that a comma delimits the last name. In both classes, we also provide error recovery, in case the space or comma does not exist.


In the LastFirst class, we assume that a comma delimits the last name. In both classes, we also provide error recovery, in case the space or comma does not exist.

 

 

#derived Namer class for Last <comma> First
class LastFirst(Namer):
    def __init__(self, namestring):
        super().__init__()
        i = namestring.find(",")  # find comma between names
        if i > 0 :
            names = namestring.split(",")
            self.last = names[0]
            self.first = names[1]
        else:
            self.last = namestring

 


Building the Simple Factory


Now our simple Factory class is extremely simple. We just test for the existence of a comma and then return an instance of one class or the other:

 

 

 

"""The NameFactory returns an instance of the Namer class that separates first and last names
depending on whether a comma is present"""
class NamerFactory():
    def __init__(self, namestring):
        self.name = namestring
    def getNamer(self):
        i = self.name.find(",") # if it finds a comma
        if i>0:
            return LastFirst(self.name) # get the LastFirst class
        else:
            return FirstFirst(self.name) # else get the FirstFirst

 


Using the Factory


Let’s see how to put this together. In this example, we create a little program to ask for the name string and then ask the factory for the right Namer, right from the console.

 

 

class Builder:
    def compute(self):
        name = ""
        while name != 'quit':
            name = input("Enter name: ")  # get the name string
            # get the Namer Factory and then the namer class
            namerFact = NamerFactory(name)  # get namer factory
            namer = namerFact.getNamer()  # get namer
            print(namer.first, namer.last)

def main():
    bld = Builder()
    bld.compute()

 

And the actual program works just as expected, finding the comma or space and splitting into two names:

 

 

Enter name: Sandy Smith 
Sandy Smith
Enter name: Jones, Doug 
Doug Jones
Enter name: quit 
quit

 

 


You type in a name and then click the Compute button, and the divided name appears in the next line. The crux of this program is the compute method that fetches the text, obtains an instance of a Namer class, and prints the results.

 

test.py
0.00MB

 

 

 

A Simple GUI


We have also constructed a simple user interface using tkinter that enables you to enter the names in either order and see the two names separately displayed. You can see this program in Figure 5-2.

 

Figure 5-2 Simple name factory display

 

And that’s the fundamental principle of the Simple Factory pattern. You create an abstraction that decides which of several possible classes to return and returns one. Then you call the methods of that class instance without ever knowing which subclass you are actually using. This approach keeps the issues of data dependence separated from the classes’ useful methods.

 

Factory Patterns in Math Computation

 

Most people who use Factory patterns tend to think of them as tools for simplifying tangled programming classes. But it is perfectly possible to use them in programs that simply perform mathematical computations. For example, in the Fast Fourier Transform (FFT), you evaluate the following four equations repeatedly for a large number of point pairs over many passes through the array you are transforming. Because of the way the graphs of these computations are drawn, these equations constitute one instance of the FFT “butterfly.” These are shown as Equations 1–4.

 


However, there are a number of times during each pass through the data, where the angle y is 0. In this case, your complex math evaluation reduces to Equations 5–8.

 


Then, we can make a simple factory class that decides which class instance to return. Since we are making Butterflies, we’ll call our Factory a Cocoon:

 

 

 

class Butterfly():
    def addSub(self,x,y): pass

class TrigButterfly(Butterfly):
    def addSub(self,x,y):pass

class AddButterfly(Butterfly):
    def addSub(self, x, y):pass

class Cocoon():
    def getButterfly(self, y:float):
        if y !=0:
            return TrigButterfly(y)
        else:
            return AddButterfly(y)

 



Programs on GitHub

  • NamerConsole.py: Console version of Namer factory
  • NameUI.py: Illustrates the Namer factory with a UI
  • Cocoon.py: Simple prototype of a Factory pattern

Cocoon.py
0.00MB
NamerConsole.py
0.00MB
NameUi.py
0.00MB

 

 


Thought Questions

1. Quicken 같은 개인 수표 관리 프로그램을 고려하십시오. 여러 은행 계좌와 투자를 관리하고 청구서 지불을 처리할 있습니다. 이와 같은 프로그램을 설계 Factory 패턴을 어디에서 사용할 있습니까?

2. 주택 소유자가 주택에 증축을 설계하는 도움이 되는 프로그램을 작성하고 있다고 가정해 보겠습니다. 팩토리는 어떤 오브젝트를 사용하여 생산할 있습니까?

'James Cooper - Python Programming with D' 카테고리의 다른 글

The Abstract Factory Pattern  (0) 2023.03.23
The Factory Method Pattern  (0) 2023.03.23
What Are Design Patterns?  (0) 2023.03.23
Visual Programming of Tables of Data  (0) 2023.03.23
Visual Programming in Python  (0) 2023.03.23

댓글