GitHub Address: Https://github.com/cheesezh/python_design_patterns
Topic
How to make a program that can flexibly replace the database?
Base version
class User(): """ 用户类,模拟用户表,假设只有ID和name两个字段 """ def __init__(self): self.id = None self.name = None class SqlServerUser(): """ sqlserveruser类,用于操作User表 """ def insert(self, user): print("向SQL Server中添加一个User") def get_user(self, id): print("从SQL Server中搜索User", id) def main(): user = User() su = SqlServerUser() su.insert(user) su.get_user(1) main()
向SQL Server中添加一个User从SQL Server中搜索User 1
Comments
The reason why the database is not flexibly replaced is because the su = SqlServerUser()
client and SQL Server are bound together, and if this is polymorphic, then SQL Server or access is not to be considered.
This can be improved with the "factory method mode", where the factory method pattern defines an interface for creating objects, allowing subclasses to decide which class to instantiate.
Improved version 1.0--factory method mode
From ABC import Abcmeta, Abstractmethodclass iuser (): __metaclass__ = Abcmeta @abstractmethod def insert (self , user): Pass @abstractmethod def get_user (self, id): Pass class Sqlserveruser (Iuser): def insert (self, user): Print ("Add a user in SQL Server") def get_user (self, id): Print ("from SQL Serv Er search user ", id) class Accessuser (Iuser): def insert (self, user): Print (" Add a user in Access ") def get_user (self, id): Print ("Search for user from Access", ID) class Ifactory (): __metaclass__ = Abcmet A @abstractmethod def create_user (self): Pass class Sqlserverfactory (ifactory): Def create_user (sel f): Return Sqlserveruser () class Accessfactory (ifactory): def create_user (self): return Accessuser () def main (): User = User () factory = Sqlserverfactory () Iuser = Factory.create_user () Iuser.insert (US ER) iuser.get_user (1) Main ()
在SQL Server中添加一个User从SQL Server中搜索User 1
Comments
Now if you want to replace the database, just factory = SqlServerFactory()
change it factory = AccessFactory()
. Because of the polymorphic relationship, the object that declares the Iuser interface Iuser does not know in advance which database to access, but can do a good job at run time, that is, the business logic and data access decoupling.
However, it is not possible to have only one user table in the database, and there may be other tables, such as department, that require a lot of new classes to be added.
Class Department (): Def __init__ (self): Self.id = None Self.name = None Class Idepartment ( ): __metaclass__ = Abcmeta @abstractmethod def insert (self, Department): Pass @abstractmethod def get_department (self, id): Pass class Sqlserverdepartment (idepartment): def insert (self, Department) : Print ("Add a department in SQL Server") def get_department (self, id): Print ("Search from SQL Server Departme NT ", ID) class Accessdepartment (idepartment): def insert (self, Department): print (" Add a department in Access ") def get_department (self, id): Print (" Search department from Access ", ID) class Ifactory (): _ _metaclass__ = Abcmeta @abstractmethod def create_user (self): pass @abstractmethod def create_d Epartment (self): Pass class Sqlserverfactory (ifactory): def create_user (self): return Sqlserveruser () def Create_department (self): return sqlserverdepartment () class Accessfactory (ifactory): def create_user (self): RET Urn Accessuser () def create_department (self): return accessdepartment () def main (): User = User () Dept = Department () factory = Sqlserverfactory () Iuser = Factory.create_user () iuser.insert (user) I User.get_user (1) idept = Factory.create_department () idept.insert (dept) idept.get_department (1) Main ()
在SQL Server中添加一个User从SQL Server中搜索User 1在SQL Server中添加一个Department从SQL Server中搜索Department 1
Comments
This can be done, just need to change factory = SqlServerFactory()
, you can easily switch the database.
When there is only one user class and user operation class, only the factory method pattern is required. But there are obviously a lot of tables in the database, and SQL Server and acess are two different classes, so solving this problem involving multiple product families requires the use of abstract Factory mode.
Abstract Factory mode
Abstract Factory mode, which provides an interface to create a series of related or interdependent objects without specifying their specific classes.
In the above questions:
- User and Department equivalent to two abstract products;
- Sqlserveruser and Accessuser are abstract product user's specific product realization;
- Ifactory is an abstract factory interface that contains all the abstract methods of product creation;
- Sqlserverfactory and accessfactory are specific factories;
The usual process is to create an instance of the Concretfactory class at run time, and the specific factory creates a product object with a specific implementation, that is, to create different product objects, the client should use a different specific factory.
What are the advantages of the abstract factory model?
The biggest benefit is the easy-to-exchange product line, which makes it very easy to change the specific factory of an application by simply changing the specific plant to use different product configurations, since the specific factory class needs to occur only once during initialization.
The second advantage is that the specific creation process and client separation, the client is through their abstract interface to manipulate the instance, the specific class name of the product is also separated from the implementation of the specific factory, will not appear in the client code.
What are the drawbacks of the abstract factory model?
Abstract Factory mode makes it easy to switch access codes for two databases, but when you need to add functionality, such as adding project table projects, you need to add three classes of Iproject,sqlserverproject,accessproject, Also change ifactory,sqlserverfactory and accessfactory. If there are 100 classes that call data access, it is very ugly to change it 100 times to switch databases.
Improve abstract factory with simple factory
Remove the Ifactory,sqlserverfactory and accessfactory three factory classes, instead of the DataAccess class.
class DataAcess(): # 类变量,通过`类名.变量名`访问 db = "sql_server" @classmethod def create_user(self): if DataAcess.db == "sql_server": return SqlServerUser() elif DataAcess.db == "access": return AccessUser() @classmethod def create_department(self): if DataAcess.db == "sql_server": return SqlServerDepartment() elif DataAcess.db == "access": return AccessDepartment() def main(): user = User() dept = Department() iu = DataAcess.create_user() iu.insert(user) iu.get_user(1) idept = DataAcess.create_department() idept.insert(dept) idept.get_department(1) main()
在SQL Server中添加一个User从SQL Server中搜索User 1在SQL Server中添加一个Department从SQL Server中搜索Department 1
Comments
In all areas where simple factories are used, it is possible to consider the use of reflective techniques to remove swith or if-else, and to contact the coupling caused by the branch.
Reflection version
import sysdef createInstance(module_name, class_name, *args, **kwargs): class_meta = getattr(module_name, class_name) obj = class_meta(*args, **kwargs) return objdef main(): db = "Access" # load from config file user = User() dept = Department() iuser = createInstance(sys.modules[__name__], "{}User".format(db)) iuser.insert(user) iuser.get_user(1) idept = createInstance(sys.modules[__name__], "{}Department".format(db)) idept.insert(dept) idept.get_department(1) main()
在Access中添加一个User从Access中搜索User 1在Access中添加一个Department从Access中搜索Department 1
[Python design mode] 15th how to be compatible with various db--abstract factory modes