Neil 啃设计模式(0x3)抽象工厂模式

抽象工厂模式(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()

应用场景

  1. 不适用与产品族这种纵向扩展需求,即一台汽车就这些组件,产品族中不增加组件,因为如果产品族中增减组件,那么所有的汽车型号都要扩展,所以是纵向扩展能力不强。
  2. 适用于横向产品扩展,比如:增加一个新的汽车类型 C,那么只需要新增一个 C 产品文件,实现响应的产品族接口和工厂即可
  3. 具体举例:我们系统中对时间的格式化,对货币形式的格式化,每个国家可能不同。货币+时间格式化组成产品族,而可以横向扩展不同的国家 Chain,USA...
  4. 个人理解产品族是一个不经常变的东西,所以放在一起用一个工厂类给包装起来
《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)

你可能感兴趣的:(python,设计模式,uml)