一起来学设计模式-----创建型模式之抽象工厂
学习了简单工厂,工厂方法模式,那什么是抽象工厂呢?工厂方法模式是在超类(IFactory)中定义一个工厂的抽象接口(CreateOperation),然后由子类负责创建具体对象;而抽象工厂则是维护一个产品家族,由子类定义产品被产生的方法,客户根据超类的接口开发。目前简单的理解,抽象工厂更多的就是基于多种产品的抽象和对象的创建。
假设系统里有一个用户表,需求是插入和选择用户时根据用户的需求,可以保存在mysql 数据库或者Access数据库中,这时用工厂方法模式应该怎么样实现呢?
首先使用面向对象的概念进行业务抽象,用户是一个类,使用mysql操作用户表是一个具体实践类,使用Access操作用户表是一个具体的实践类,操作类可以抽象成一个接口IUser类,根据工厂方法模式,则每个实践类都需要一个实际工厂是实例化,则需要2个具体工厂,这2个具体的工厂又能抽象一个接口IFactory类。客户端就可以根据实际情况,选择使用具体的工厂,创造出具体的类,操作user表。代码实现如下:
from abc import ABCMeta, abstractmethod
class User:
def __int__(self,id):
self.id = id
class IUser:
@abstractmethod
def InsertUser(self,user):
pass
@abstractmethod
def GetUser(self,id):
pass
class IFactory:
@abstractmethod
def CreateUser(self):
pass
class AccessUser(IUser):
def InsertUser(self,user):
print ("在access中为User表增加一条记录")
def GetUser(self,id):
print ("在access中为User表查询一条记录")
class MysqlUser(IUser):
def InsertUser(self,user):
print ("在mysql中为User表增加一条记录")
def GetUser(self,id):
print ("在mysql中为User表查询一条记录")
class MysqlFactory(IFactory):
def CreateUser(self):
return MysqlUser()
class AccessFactory(IFactory):
def CreateUser(self):
return AccessUser()
客户端调用的代码如下:
if __name__ == '__main__': user = User() factory = MysqlFactory() iu = factory.CreateUser() iu.InsertUser(user) iu.GetUser(1)
UML类图:
如果此时,再加一个Department表,业务类似User一样的增加,在工厂类中,增加工厂创建实例对象的方法即可。则UML类图应该是下面这样的:
代码实现如下:
class User: def __int__(self,id): self.id = id class Department: def __int__(self,depName): self.depName = depName class IUser: @abstractmethod def InsertUser(self,user): pass @abstractmethod def GetUser(self,id): pass class IDepartment: @abstractmethod def InsertDepartment(self,dep): pass @abstractmethod def GetDepartment(self,depname): pass class IFactory: @abstractmethod def CreateUser(self): pass @abstractmethod def CreateDepartment(self): pass class AccessUser(IUser): def InsertUser(self,user): print ("在access中为User表增加一条记录") def GetUser(self,id): print ("在access中为User表查询一条记录") class MysqlUser(IUser): def InsertUser(self,user): print ("在mysql中为User表增加一条记录") def GetUser(self,id): print ("在mysql中为User表查询一条记录") class AccessDepartment(IDepartment): def InsertDepartment(self,dep): print ("在access中为Department表增加一条记录") def GetDepartment(self,id): print ("在access中为Department表查询一条记录") class MysqlDepartment(IDepartment): def InsertDepartment(self,dep): print ("在mysql中为Department表增加一条记录") def GetDepartment(self,id): print ("在mysql中为Department表查询一条记录") class MysqlFactory(IFactory): def CreateUser(self): return MysqlUser() def CreateDepartment(self): return MysqlDepartment() class AccessFactory(IFactory): def CreateUser(self): return AccessUser() def CreateDepartment(self): return AccessDepartment()
客户端代码如下:
if __name__ == '__main__': user = User() factory = MysqlFactory() iu = factory.CreateUser() iu.InsertUser(user) iu.GetUser(1) dep = Department() asFactory = AccessFactory() idep = asFactory.CreateDepartment() idep.InsertDepartment(dep) idep.GetDepartment('loleina')
这么一看,就已经实现了抽象工厂方法,工厂方法模式是定义一个用于创建对象的接口(IFactory),让子类(MysqlFactory,AccessFactory)去决定实例化哪一个类。而抽象工厂,是提供一个创建一系列相关或相互依赖对象的接口(IFactory内可以有多个产品的抽象),而无须指定它们的具体的类。
抽象工厂最大的好处就是,客户端可以根据自己的需求,从不同的工厂中获取不同的产品对象,从而得到不同的产品。但是这个模式实际也是有很多缺点的,比如思考下此时加入一个新的表,要改动哪些呢?
下面以UML类图变化为例。灰色部分表示需要修改的地方。工厂类就得全部修改一遍。。。
相比如果增加一个工厂,代价是相对还少点。
实际在真正的项目使用上,根据我进鹅厂看了这么多的业务代码经验,这类设计模式一般是不会被才采用到的,一般都是使用反射 + 配置文件(DB配置)+ 简单工厂来实现,这样增加产品类型时,只需要增加响应的产品,增加相关的配置,而无须修改任何代码。使用这个思路修改这个例子。
代码实现如下:
class DateAccess: @staticmethod def CreateUser():
#DBType和DBTableName来自于配置文件或者DB配置 DBType = 'Mysql' DBTableUser = 'User' instanceName = DBType +DBTableUser return eval(instanceName)() @staticmethod def CreatDepartment(): DBType = 'Mysql' DBTableDep = 'Department' instanceName = DBType + DBTableDep return eval(instanceName)()
客户端调用如下:
if __name__ == '__main__': user = User() iu = DateAccess.CreateUser() iu.InsertUser(user) iu.GetUser(1) dep = Department() idep =DateAccess.CreatDepartment() idep.InsertDepartment(dep) idep.GetDepartment('test')