抽象工厂模式(Abstract Factory Pattern)
定义
Provide an interface for creating families of related or dependent objects without specifying their concrete classes.(为创建一组相关或相互依赖的对象提供一个接口,而且无须指定它们的具体类。)
《设计模式之禅》
个人理解抽象工厂模式与工厂模式的最大区别在于定义中的关键词 一组相关或者相互依赖的对象。例如,我们要生产一辆汽车,那么汽车的各个必要组件就是一组相关的或相互依赖的对象,每个组件就是一个产品对象,而组合在一起就是一个产品族,一辆汽车的三大件(发动机+底盘+变速箱)。每个种型号的汽车都要有这一系列的产品族。只是所用的型号不同罢了。
那么,我们每个型号的汽车就是一个工厂,都继承了一个抽象工厂,抽象工厂负责定义产品族中每个产品的制造。
当然,每个产品有自己的属性,且能够创造出不同的型号。
UML 示例
@startuml
Interface Engine
Interface Underpan
Interface Gearbox
Interface abstractFactory
class EngineA
Engine <-- EngineA
class EngineB
Engine <-- EngineB
class UnderpanA
Underpan <-- UnderpanA
class UnderpanB
Underpan <-- UnderpanB
class GearboxA
Gearbox <-- GearboxA
class GearboxB
Gearbox <-- GearboxB
class FactoryA{
+createEngine()
+createUnderpan()
+createGearbox()
}
abstractFactory <-- FactoryA
class FactoryB{
+createEngine()
+createUnderpan()
+createGearbox()
}
abstractFactory <-- FactoryB
FactoryA ..>EngineA
FactoryA ..>UnderpanA
FactoryA ..>GearboxA
FactoryB ..>EngineB
FactoryB ..>UnderpanB
FactoryB ..>GearboxB
@enduml
解释: Engine 发动机,Underpan 底盘和 Gearbox 变速箱共同组成了一个产品族
A、B 分别表示两种车的型号
代码实现
还是要强调一点,python 中代码实现是可以弃用接口实现类的,所以,我们的代码中,关于 Engine、Underpan、Gearbox、AbstractFactory 的接口定义就不写了
# 型号A的汽车组件
class EngineA:
def get_engin(self):
print("A: Engine")
class UnderpanA:
def get_underpan(self):
print("A: Underpan")
class GearboxA:
def get_gearbox(self):
print("A: GerboxA")
# 型号B的汽车组件
class EngineB:
def get_engin(self):
print("B: Engine")
class UnderpanB:
def get_underpan(self):
print("B: Underpan")
class GearboxB:
def get_gearbox(self):
print("B: GerboxA")
# factory A
class FactoryA:
def create_engine(self):
return EngineA()
def create_underpan(self):
return UnderpanA()
def create_gearbox(self):
return GearboxA()
class FactoryB:
def create_engine(self):
return EngineB()
def create_underpan(self):
return UnderpanB()
def create_gearbox(self):
return GearboxB()
if __name__ == "__main__":
productA = FactoryA()
productB = FactoryB()
productA.create_engine().get_engin()
productA.create_gearbox().get_gearbox()
productA.create_underpan().get_underpan()
productB.create_engine().get_engin()
productB.create_gearbox().get_gearbox()
productB.create_underpan().get_underpan()
应用场景
- 不适用与产品族这种纵向扩展需求,即一台汽车就这些组件,产品族中不增加组件,因为如果产品族中增减组件,那么所有的汽车型号都要扩展,所以是纵向扩展能力不强。
- 适用于横向产品扩展,比如:增加一个新的汽车类型 C,那么只需要新增一个 C 产品文件,实现响应的产品族接口和工厂即可
- 具体举例:我们系统中对时间的格式化,对货币形式的格式化,每个国家可能不同。货币+时间格式化组成产品族,而可以横向扩展不同的国家 Chain,USA...
- 个人理解产品族是一个不经常变的东西,所以放在一起用一个工厂类给包装起来
《python 面向对象编程》中的例子
class FranceDateFormatter:
def format_date(self, y, m, d):
y, m, d = (str(x) for x in (y, m, d))
y = '20' + y if len(y) == 2 else y
m = '0' + m if len(m) == 1 else m
d = '0' + d if len(d) == 1 else d
return ("{0}/{1}/{2}".format(d, m, y))
class USADateFormatter:
def format_date(self, y, m, d):
y, m, d = (str(x) for x in (y, m, d))
y = '20' + y if len(y) == 2 else y
m = '0' + m if len(m) == 1 else m
d = '0' + d if len(d) == 1 else d
return ("{0}-{1}-{2}".format(m, d, y))
class FranceCurrencyFormatter:
def format_currency(self, base, cents):
base, cents = (str(x) for x in (base, cents))
if len(cents) == 0:
cents = '00'
elif len(cents) == 1:
cents = '0' + cents
digits = []
for i, c in enumerate(reversed(base)):
if i and not i % 3:
digits.append(' ')
digits.append(c)
base = ''.join(reversed(digits))
return "{0} {1}".format(base, cents)
class USACurrencyFormatter:
def format_currency(self, base, cents):
base, cents = (str(x) for x in (base, cents))
if len(cents) == 0:
cents = '00'
elif len(cents) == 1:
cents = '0' + cents
digits = []
for i, c in enumerate(reversed(base)):
if i and not i % 3:
digits.append(',')
digits.append(c)
base = ''.join(reversed(digits))
return "{0} {1}".format(base, cents)
class USAFormatterFactory:
def create_date_formatter(self):
return USADateFormatter()
def create_currency_formatter(self):
return USACurrencyFormatter()
class FranceFormatterFactory:
def create_date_formatter(self):
return FranceDateFormatter()
def create_currency_formatter(self):
return FranceFormatterFactory()
country_code = "US"
factory_map = {
"US": USAFormatterFactory,
"FR": FranceFormatterFactory
}
formatter_factory = factory_map.get(country_code)
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。