动机(motivation)
- 在软件系统中,经常面临着“一系列相互依赖的对象工作”;同时,由于需求的变化,往往存在更多系列对象的创建工作。
- 如何应对这种变化?如何绕过常规的对象创建方法(new),提供一种“封装机制”来避免客户程序和这种“多系列具体对象创建工作”的紧耦合。
模式定义
提供一个接口,让该接口负责创建一系列”相关或者相互依赖的对象“,无需指定它们具体的类。
——《设计模式》GoF
要点总结
- 如果没有应对”多系列对象创建“的需求变化,则没有必要使用Abstract Factory模式,这时候使用简单的工厂即可。
- ”系列对象“指的是在某一个特定系列的对象之间有相互依赖、或作用的关系。不同系列的对象之间不能相互依赖。
- Abstract Factory模式主要在于应用”新系列“的需求变动。其缺点在与难以应对”新对象“的需求变动。
UML
红色框表示稳定的部分,即抽象工厂基类,各个产品的基类。
例子-访问数据库
在访问数据库的例子中,由于DBAccess
依赖了SqlConnection
、SqlCommand
,因此当业务需求,需要支持Oracle
、MySql
等数据库的时候,就需要修改DBAccess
,违背了开闭原则、依赖导致原则。因此这里使用工厂模式、抽象工厂模式分别进行改进。
# -*- coding:utf-8 -*-
class SqlConnection(object):
def set_connection(self):
print('SqlConnection set_connection')
class SqlDataReader(object):
def read(self):
print('SqlDataReader read')
class SqlCommand(object):
def start_connection(self, obj):
print('SqlCommand start connection')
class DBAccess():
# 访问数据库
def get_employees(self):
sql_connection = SqlConnection()
sql_connection.set_connection()
sql_command = SqlCommand()
sql_command.start_connection(sql_connection)
if __name__ == '__main__':
db = DBAccess()
db.get_employees()
工厂方法实现
# -*- coding:utf-8 -*-
class DBConnectionBase(object):
def set_connection(self):
pass
class DBConnectionFactoryBase(object):
def create_connection(self):
pass
class DBCommandBase(object):
def start_connection(self, obj):
pass
class DBCommandFactoryBase(object):
def create_command(self):
pass
# 支持SQL
class SqlConnection(DBConnectionBase):
def set_connection(self):
print('SqlConnection set_connection')
class SqlConnectionFactory(DBConnectionFactoryBase):
def create_connection(self):
return SqlConnection()
class SqlCommand(DBCommandBase):
def start_connection(self, obj):
print('SqlCommand start connection')
class SqlCommandFactory(DBCommandFactoryBase):
def create_command(self):
return SqlCommand()
# 支持 Oracle
class OracleConnection(DBConnectionBase):
def set_connection(self):
print('OracleConnection set_connection')
class OracleConnectionFactory(DBConnectionFactoryBase):
def create_connection(self):
return OracleConnection()
class OracleCommand(DBCommandBase):
def start_connection(self, obj):
print('OracleCommand start connection')
class OracleCommandFactory(DBCommandFactoryBase):
def create_command(self):
return OracleCommand()
class DBAccess():
# 访问数据库
def get_employees(self, connection_factory, command_factory):
db_connection = connection_factory.create_connection()
db_connection.set_connection()
db_command = command_factory.create_command()
db_command.start_connection(db_connection)
if __name__ == '__main__':
db = DBAccess()
db.get_employees(SqlConnectionFactory(), SqlCommandFactory())
该例子中,用工厂方法虽然可以解决问题,但是有以下问题:
- 某个数据库相关的
connection
、command
操作必须配套,因此如果有人这样调用,会出现不可预料的后果,因此必须进行类型检查,增加了代码的复杂度,和接口使用的心智负担。
db.get_employees(SqlConnectionFactory(), OracleCommand())
- 当一种数据库有n个行为,比如不仅仅有
connection
、command
,还有read
,write
,那么要对应创建n个工厂,类的数量巨大、维护负担也随之而来。
因此需要使用抽象工厂模式,创建一系列相关或者相互依赖的对象
抽象工厂方法实现
# -*- coding:utf-8 -*-
class DBConnectionBase(object):
def set_connection(self):
pass
class DBCommandBase(object):
def start_connection(self, obj):
pass
class DBFactoryBase(object):
def create_connection(self):
pass
def create_command(self):
pass
# 支持SQL
class SqlConnection(DBConnectionBase):
def set_connection(self):
print('SqlConnection set_connection')
class SqlCommand(DBCommandBase):
def start_connection(self, obj):
print('SqlCommand start connection')
class SqlFactory(DBFactoryBase):
def create_connection(self):
return SqlConnection()
def create_command(self):
return SqlCommand()
# 支持 Oracle
class OracleConnection(DBConnectionBase):
def set_connection(self):
print('OracleConnection set_connection')
class OracleCommand(DBCommandBase):
def start_connection(self, obj):
print('OracleCommand start connection')
class OracleFactory(DBFactoryBase):
def create_connection(self):
return OracleConnection()
def create_command(self):
return OracleCommand()
class DBAccess():
# 访问数据库
def get_employees(self, db_factory):
db_connection = db_factory.create_connection()
db_connection.set_connection()
db_command = db_factory.create_command()
db_command.start_connection(db_connection)
if __name__ == '__main__':
db = DBAccess()
db.get_employees(SqlFactory())
抽象工厂中,将相互依赖的对象connection
、command
封装到了一起,从而使用起来的时候会成对创建,无需再担心connection
和command
不是一个数据库对象的问题,而且大大减少了类的数量。
对照UML分析
对照UML, 该例子中,
DBFactoryBase
对应AbstractFactory
SqlFactory
对应ConcreteFactory1
OracleFactory
对应ConcreteFactory2
AbstractProductA
对应DBConnectionBase
AbstractProductB
对应DBCommandBase
SqlConnection
对应ConcreteProductA1
SqlCommand
对应ConcreteProductB1
问题
- 当添加新的命令的时候,比如
read
,那么很多类都需要改变,那么类就不稳定了。
答: 抽象工厂模式使用的前提是假设工厂中相互依赖的对象的数量是稳定的,即DBFactoryBase
是稳定的。而抽象工厂模式适用于经常变换数据库需求,比如新支持mysql
,mogodb
等等, 这时候只需要扩展新的数据库工厂和具体实现类即可。