23 design patterns -- simple factory pattern (python version)

Simple factory mode

In real life, primitive society is self-sufficient (without factories), small workshops in farming society (simple factories, folk wineries), industrial revolution assembly lines (factory methods, self production and self marketing), and modern industrial chain generation factories (Abstract factories, Foxconn). Our project code is also iterated step by step from simplicity to complexity, but it is becoming simpler and simpler for callers.

In daily development, where complex objects need to be generated, we can try to use factory mode instead.
Note: the above complex object refers to the situation that too many constructor parameters of the class have an impact on the construction of the class, because the construction of the class is too complex. If it is used directly in other business classes, the coupling between the two is too heavy, and subsequent business changes need to be changed in any source code referencing the class. Just finding all dependencies is very time-consuming, Not to mention making changes one by one.

Definition of factory pattern: define a factory interface for creating product objects, and postpone the actual creation of product objects to specific sub factory classes. This meets the requirement of "separation of creation and use" in the creation mode.

According to the actual business scenario, there are three different implementation modes of factory mode: simple factory mode, factory method mode and abstract factory mode.

We call the created object "product" and the object that creates the product "factory". If there are not many products to be created, only one factory class can be completed. This mode is called "simple factory mode".

The method of creating instances in the Simple Factory Pattern is usually static, so the Simple Factory Pattern is also called Static Factory Method Pattern.

In short, the simple factory pattern has a specific factory class, which can generate multiple different products. It belongs to the creative design pattern. The simple factory mode is not among the 23 design modes of GOF.

Every time a product is added in the simple factory mode, a specific product class and a corresponding specific factory class will be added, which increases the complexity of the system and violates the "opening and closing principle".
"Factory method mode" is a further abstraction of the simple factory mode. Its advantage is that the system can introduce new products without modifying the original code, that is, meet the opening and closing principle.

merits and demerits

advantage:

The factory class contains the necessary logical judgment to decide when to create an instance of which product. The client can be exempted from the responsibility of directly creating product objects, and it is very convenient to create corresponding products. The responsibilities of factories and products are clearly distinguished.
The client does not need to know the class name of the specific product created, but only needs to know the parameters.
You can also import configuration files to replace and add new specific product classes without modifying the client code.

Disadvantages:

The factory class of simple factory mode is single, which is responsible for the creation of all products. The responsibility is too heavy. Once it is abnormal, the whole system will be affected. Moreover, the factory class code will be very bloated and violate the principle of high aggregation.
Using simple factory mode will increase the number of classes in the system (introducing new factory classes), and increase the complexity and understanding difficulty of the system
It is difficult to expand the system. Once new products are added, the factory logic has to be modified. When there are many product types, the logic may be too complex
The simple factory mode uses the static factory method, which makes the factory role unable to form a hierarchical structure based on inheritance.
Application scenario
For the case of relatively few product types, consider using the simple factory mode. The client using the simple factory mode only needs to pass in the parameters of the factory class, and does not need to care about the logic of how to create objects. It can easily create the required products.
Structure and implementation of pattern

The main roles of the simple factory model are as follows:

Simple factory: it is the core of the simple factory pattern and is responsible for implementing the internal logic of creating all instances. The method of creating product class of factory class can be directly called by the outside world to create the required product object.
Abstract Product: it is the parent class of all objects created by a simple factory and is responsible for describing the common interface shared by all instances.
Concrete product: it is the creation target of simple factory mode.

Its structure is shown in the figure below.

Simple factory mode implementation

Consider an example in the Dahua design pattern below:

Title: implement a calculator console program with any object-oriented language. It is required to input two numbers and operation symbols to get the result.
Topic analysis:
The program should be: (1) maintainable; (2) Reusable; (3) Scalable; (4) Good flexibility.
Maintainable: that is to say, one change of code can not produce chain reaction and affect other places.
Reusable: minimize repetitive code.
Extensibility: if you want to extend new functions and new businesses, you only need to add new classes, which will not affect the existing classes and logic. Pluggable applications.

Object oriented key points:
Three characteristics of object-oriented: encapsulation, inheritance and polymorphism.
Reduce program coupling through encapsulation, inheritance and polymorphism.
Business logic and interface logic are separated.

Structure diagram of class:


Code implementation:

  1. First, find out the changeable parts of the business. In this application, it is required to calculate the operation results of two numbers, so what kind of operation should be carried out is an easy to change part. For example, we only want to realize the operation of addition, subtraction, multiplication and division, and later we want to add the operation of open root or remainder. So how to deal with the changes brought by this demand. In programming, we should consider the maintainability, scalability, code reusability, flexibility and so on.

  2. For example, now this arithmetic unit has only four operations: addition, subtraction, multiplication and division. First, create an Operation class. This class is the parent class of various specific Operation classes (addition, subtraction, multiplication and division). It mainly accepts the values entered by the user. This category is as follows:

class Operation():
	def __init__(self,NumberA=0,NumberB=0):
		self.NumberA = NumberA
		self.NumberB = NumberB
 
	def GetResult(self):
		pass
  1. Then there are specific Operation classes: Add, Sub, Mul and Div. They both inherited the Operation class and overridden the getResult() method. In this way, polymorphism can be used to reduce the coupling degree of different business logic, and modifying any Operation class will not affect other Operation classes. The codes of specific classes are as follows:
class AddOp(Operation):
	def GetResult(self):
		return self.NumberB + self.NumberA
 
class MinusOp(Operation):
	def GetResult(self):
		return self.NumberA - self.NumberB
 
class MultiOp(Operation):
	def GetResult(self):
		return self.NumberA * self.NumberB
 
class DivideOp(Operation):
	def GetResult(self):
		try:
			return 1.0*self.NumberA / self.NumberB
		except ZeroDivisionError:
			raise

  1. So how do I let the calculator know which operation I want to use? In other words, which specific operation class to instantiate, Add? Sub? Mul? Div? At this time, we should consider using a separate class to do the process of creating concrete instances. This class is the factory class. As follows:
class OperationFatory():
	def ChooseOperation(self,op):
		if op == '+':
			return AddOp()
		if op == '-':
			return MinusOp()
		if op == '*':
			return MultiOp()
		if op == '/':
			return DivideOp()
  1. In this way, as long as the user inputs the operator, the factory class can create an appropriate instance and realize the operation result by polymorphism, that is, returning to the parent class. The client code is as follows:
if __name__ == '__main__':
	ch = ''
	while not ch=='q': 
		NumberA = eval(raw_input('Please input number1:  '))
		op = str(raw_input('Please input the operation:  '))
		NumberB = eval(raw_input('Please input number2:  '))
		OPFactory = OperationFatory()
		OPType = OPFactory.ChooseOperation(op)
		OPType.NumberA = NumberA
		OPType.NumberB = NumberB
		print 'The result is:',OPType.GetResult()
		print '\n#--  input q to exit any key to continue'
		try:
			ch = str(raw_input())
		except:
			ch = ''

The full version code is as follows

# -*-coding:UTF-8-*-  
from abc import ABCMeta,abstractmethod
 
class Operation():
	def __init__(self,NumberA=0,NumberB=0):
		self.NumberA = NumberA
		self.NumberB = NumberB
 
	def GetResult(self):
		pass
 
class AddOp(Operation):
	def GetResult(self):
		return self.NumberB + self.NumberA
 
class MinusOp(Operation):
	def GetResult(self):
		return self.NumberA - self.NumberB
 
class MultiOp(Operation):
	def GetResult(self):
		return self.NumberA * self.NumberB
 
class DivideOp(Operation):
	def GetResult(self):
		try:
			return 1.0*self.NumberA / self.NumberB
		except ZeroDivisionError:
			raise
 
class OperationFatory():
	def ChooseOperation(self,op):
		if op == '+':
			return AddOp()
		if op == '-':
			return MinusOp()
		if op == '*':
			return MultiOp()
		if op == '/':
			return DivideOp()
 
if __name__ == '__main__':
	ch = ''
	while not ch=='q': 
		NumberA = eval(raw_input('Please input number1:  '))
		op = str(raw_input('Please input the operation:  '))
		NumberB = eval(raw_input('Please input number2:  '))
		OPFactory = OperationFatory()
		OPType = OPFactory.ChooseOperation(op)
		OPType.NumberA = NumberA
		OPType.NumberB = NumberB
		print 'The result is:',OPType.GetResult()
		print '\n#--  input q to exit any key to continue'
		try:
			ch = str(raw_input())
		except:
			ch = ''

Keywords: Python

Added by catalinus on Tue, 15 Feb 2022 11:56:23 +0200