• Python设计模式——工厂模式


    1.前言
      工厂模式,顾名思义就是我们可以通过一个指定的“工厂”获得需要的“产品”,在设计模式中主要用于抽象对象的创建过程,让用户可以指定自己想要的对象而不必关心对象的实例化过程。这样做的好处是用户只需通过固定的接口而不是直接去调用类的实例化方法来获得一个对象的实例,隐藏了实例创建过程的复杂度,解耦了生产实例和使用实例的代码,降低了维护的复杂性。

    2.简单工厂
    首先,我们先看一个简单工厂的例子:

    #coding=utf-8
    class Mercedes(object):
    """梅赛德斯
    """
    def __repr__(self):
    return "Mercedes-Benz"
    
    class BMW(object):
    """宝马
    """
    def __repr__(self):
    return "BMW"
    
    #假设我们有两个“产品”分别是Mercedes和BMW的汽车,如果没有“工厂”来生产它们,我们就要在代码中自己进行实例化,如:
    mercedes = Mercedes()
    bmw = BMW()


    但现实中,你可能会面对很多汽车产品,而且每个产品的构造参数还不一样,这样在创建实例时会遇到麻烦。这时就可以构造一个“简单工厂”把所有汽车实例化的过程封装在里面。

    class SimpleCarFactory(object):
    """简单工厂
    """
    @staticmethod
    def product_car(name):
    if name == 'mb':
    return Mercedes()
    elif name == 'bmw':
    return BMW()
    
    #有了SimpleCarFactory类后,就可以通过向固定的接口传入参数获得想要的对象实例,如下:
    
    simple_factory = SimpleCarFactory()
    benz = simple_factory.product_car('mb')
    bmw = SimpleCarFactory.product_car('bmw')
    print(benz) # Mercedes-Benz
    print(bmw) # BMW

    3.工厂方法
      定义一个用于创建对象的接口,但是让子类决定将哪一个类实例化。工厂方法模式让一个类的实例化延迟到其子类。

      工厂方法模式就是简单工厂模式的进一步抽象。由于面向对象多态性,工厂方法模式保持了简单工厂的优点同时克服了他的缺点。工厂方法模式中,核心的工厂被提升为一个抽象类,将具体的创建工作交给他的子类完成。这个抽象的工厂类仅规定具体工厂实现的接口,而不明确指出如何实例化一个产品类,这使得工厂方法模式允许系统在不修改原有产品结构的情况下轻松的引进新产品。

      虽然有了一个简单的工厂,但在实际使用工厂的过程中,我们会发现新问题:如果我们要新增一个“产品”,例如Audi的汽车,我们除了新增一个Audi类外还要修改SimpleCarFactory内的product_car方法。这样就违背了软件设计中的开闭原则:即在扩展新的类时,尽量不要修改原有代码。所以我们在简单工厂的基础上把SimpleCarFactory抽象成不同的工厂,每个工厂对应生成自己的产品,这就是工厂方法。

    from abc import ABCMeta, abstractmethod
    
    
    class AbstractFactory(metaclass=ABCMeta):
    """
    抽象工厂,核心的工厂被提升为一个抽象类,将具体的创建工作交给他的子类完成。
    这个抽象的工厂类仅规定具体工厂实现的接口,而不明确指出如何实例化一个产品类,
    这使得工厂方法模式允许系统在不修改原有产品结构的情况下轻松的引进新产品。
    """
    
    @abstractmethod
    def product_car(self):
    pass
    
    
    class Mercedes(object):
    """梅赛德斯"""
    
    def __repr__(self):
    return "Mercedes-Benz"
    
    
    class BMW(object):
    """宝马"""
    
    def __repr__(self):
    return "BMW"
    
    
    class Audi(object):
    """奥迪"""
    
    def __repr__(self):
    return "Audi-A8L"
    
    
    class MercedesFactory(AbstractFactory):
    """梅赛德斯工厂"""
    
    def product_car(self):
    return Mercedes()
    
    
    class BMWFactory(AbstractFactory):
    """宝马工厂"""
    
    def product_car(self):
    return BMW()
    
    
    class AudiFactory(AbstractFactory):
    """奥迪工厂"""
    
    def product_car(self):
    return Audi()
    
    
    class Factory_Method:
    """工厂方法"""
    # 可以维护一个字典,对应工厂类和产品名,避免使用过多if-else判断
    factory_dict = {'audi': AudiFactory, 'bmw': BMWFactory, 'benz': MercedesFactory}
    
    def product(self, name):
    if name in self.factory_dict.keys():
    return self.factory_dict[name]().product_car()
    else:
    return None
    
    
    factory = Factory_Method()
    bmw = factory.product('bmw')
    print(bmw)
    benz = factory.product('benz')
    print(benz)
    audi = factory.product('audi')
    print(audi)
    haval = factory.product('haval')
    print(haval)

      我们把工厂抽象出来用abc模块实现了一个抽象的基类AbstractFactory,这样就可以通过特定的工厂来获得特定的产品实例了.

      每个工厂负责生产自己的产品也避免了我们在新增产品时需要修改工厂的代码,而只要增加相应的工厂即可。如新增一个Audi产品,只需新增一个Audi类和AudiFactory类。

    4.抽象工厂
      提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。

      抽象工厂模式是所有形式的工厂模式中最为抽象和最具一般性的一种形式。当系统所提供的工厂生产的具体产品并不是一个简单的对象,而是多个位于不同产品等级结构、属于不同类型的具体产品时就可以使用抽象工厂模式 ,抽象工厂模式中的具体工厂不只是创建一种产品,它负责创建一族产品 当一个工厂等级结构可以创建出分属于不同产品等级结构的一个产品族中的所有对象时,抽象工厂模式比工厂方法模式更为简单、更有效率

      抽象工厂模式包含以下4个角色: AbstractFactory(抽象工厂) ConcreteFactory(具体工厂) AbstractProduct(抽象产品) ConcreteProduct(具体产品)


      工厂方法虽然解决了我们“修改代码”的问题,但如果我们要生产很多产品,就会发现我们同样需要写很多对应的工厂类。比如如果MercedesFactory和BMWFactory不仅生产小汽车,还要生产SUV,那我们用工厂方法就要再多构造两个生产SUV的工厂类。所以为了解决这个问题,我们就要再更进一步的抽象工厂类,让一个工厂可以生产同一类的多个产品,这就是抽象工厂。

    import abc
    
    
    # 3种小汽车
    class Mercedes_C63(object):
    """梅赛德斯 C63"""
    
    def __repr__(self):
    return "Mercedes-Benz: C63"
    
    
    class BMW_M3(object):
    """宝马 M3"""
    
    def __repr__(self):
    return "BMW: M3"
    
    
    class Audi_A8L:
    """奥迪A8L"""
    
    def __repr__(self):
    return "Audi: A8L"
    
    
    #  3种SUV
    class Mercedes_G63(object):
    """梅赛德斯 G63"""
    
    def __repr__(self):
    return "Mercedes-Benz: G63"
    
    
    class BMW_X5(object):
    """宝马 X5"""
    
    def __repr__(self):
    return "BMW: X5"
    
    
    class Audi_Q5:
    """奥迪Q5"""
    
    def __repr__(self):
    return "Audi: Q5"
    
    
    class AbstractFactory(object):
    """
    提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。
    抽象工厂可以生产小汽车外,还可以生产SUV
    """
    __metaclass__ = abc.ABCMeta
    
    @abc.abstractmethod
    def product_car(self):
    pass
    
    @abc.abstractmethod
    def product_suv(self):
    pass
    
    # 具体工厂不只是创建一种产品,它负责创建一族产品。
    class MercedesFactory(AbstractFactory):
    """梅赛德斯工厂"""
    
    def product_car(self):
    return Mercedes_C63()
    
    def product_suv(self):
    return Mercedes_G63()
    
    
    class BMWFactory(AbstractFactory):
    """宝马工厂"""
    
    def product_car(self):
    return BMW_M3()
    
    def product_suv(self):
    return BMW_X5()
    
    
    class AudiFactory(AbstractFactory):
    """奥迪工厂"""
    
    def product_car(self):
    return Audi_A8L()
    
    def product_suv(self):
    return Audi_Q5()
    
    
    class ABC_Factory:
    # 可以维护一个字典,对应工厂类和产品名,避免使用过多if-else判断
    factory_dict = {'Audi': AudiFactory, 'BMW': BMWFactory, 'Benz': MercedesFactory}
    car_type = {'car': ['Benz-C63', 'BMW-M3', 'Audi-A8L'], 'suv': ['Benz-G63', 'BMW-X5', 'Audi-Q5']}
    
    def product(self, name):
    factory_name = name.split('-')[0]
    if factory_name in self.factory_dict.keys():
    if name in self.car_type['suv']:
    return self.factory_dict[factory_name]().product_suv()
    elif name in self.car_type['car']:
    return self.factory_dict[factory_name]().product_car()
    else:
    return None
    
    
    factory = ABC_Factory()
    bmw = factory.product('BMW-X5')
    print(bmw)
    benz = factory.product('Benz-C63')
    print(benz)
    audi = factory.product('Audi-Q5')
    print(audi)
    haval = factory.product('Haval-H6')
    print(haval)

      我们让基类AbstractFactory同时可以生产汽车和SUV,然后令MercedesFactory和BMWFactory继承AbstractFactory并重写product_car和product_suv方法即可。

    抽象工厂模式与工厂方法模式最大的区别在于,抽象工厂中的一个工厂对象可以负责多个不同产品对象的创建 ,这样比工厂方法模式更为简单、有效率。

    5.结论
    初学设计模式时会对三种工厂模式的实际应用比较困惑,其实三种模式各有优缺点,应用的场景也不尽相同:

    简单工厂模式:

      适用于需创建的对象较少,不会造成工厂方法中的业务逻辑太过复杂的情况下,而且用户只关心那种类型的实例被创建,并不关心其初始化过程时,比如多种数据库(MySQL/MongoDB)的实例,多种格式文件的解析器(XML/JSON)等。
    工厂方法模式:继承了简单工厂模式的优点又有所改进,其不再通过一个工厂类来负责所有产品的创建,而是将具体创建工作交给相应的子类去做,这使得工厂方法模式可以允许系统能够更高效的扩展。实际应用中可以用来实现系统的日志系统等,比如具体的程序运行日志,网络日志,数据库日志等都可以用具体的工厂类来创建。
    抽象工厂模式:在工厂方法基础上扩展了工厂对多个产品创建的支持,更适合一些大型系统,比如系统中有多于一个的产品族,且这些产品族类的产品需实现同样的接口,像很多软件系统界面中不同主题下不同的按钮、文本框、字体等等。

    简单工厂模式

      优点:实现了对象创建和使用的分离 客户端无须知道所创建的具体产品类的类名,只需要知道具体产品类所对应的参数即可 通过引入配置文件,可以在不修改任何客户端代码的情况下更换和增加新的具体产品类,在一定程度上提高了系统的灵活性

      缺点:工厂类集中了所有产品的创建逻辑,职责过重,一旦不能正常工作,整个系统都要受到影响 增加系统中类的个数(引入了新的工厂类),增加了系统的复杂度和理解难度 系统扩展困难,一旦添加新产品不得不修改工厂逻辑 由于使用了静态工厂方法,造成工厂角色无法形成基于继承的等级结构,工厂类不能得到很好地扩展,也就是说简单工厂模式不符合六大设计模式原则之一的开放封闭原则。

    适用环境
      工厂类负责创建的对象比较少,由于创建的对象较少,不会造成工厂方法中的业务逻辑太过复杂 客户端只知道传入工厂类的参数,对于如何创建对象并不关心

    工厂方法模式

      优点工厂方法用来创建客户所需要的产品,同时还向客户隐藏了哪种具体产品类将被实例化这一细节 能够让工厂自主确定创建何种产品对象,而如何创建这个对象的细节则完全封装在具体工厂内部 在系统中加入新产品时,完全符合开闭原则

      缺点系统中类的个数将成对增加,在一定程度上增加了系统的复杂度,会给系统带来一些额外的开销 增加了系统的抽象性和理解难度

    适用环境
      客户端不知道它所需要的对象的类(客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可,具体产品对象由具体工厂类创建) 抽象工厂类通过其子类来指定创建哪个对象

    抽象工厂模式:

      优点:隔离了具体类的生成,使得客户端并不需要知道什么被创建 当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象 增加新的产品族很方便,无须修改已有系统,符合开闭原则

      缺点:增加新的产品等级结构麻烦,需要对原有系统进行较大的修改,甚至需要修改抽象层代码,这显然会带来较大的不便,违背了开闭原则

    适用环境
      一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节 系统中有多于一个的产品族,但每次只使用其中某一产品族 属于同一个产品族的产品将在一起使用,这一约束必须在系统的设计中体现出来 产品等级结构稳定,设计完成之后,不会向系统中增加新的产品等级结构或者删除已有的产品等级结构

  • 相关阅读:
    Linux客户/服务器程序设计范式——阿帕奇服务器(多进程)
    Linux客户/服务器程序设计范式2——并发服务器(进程池)
    封装readn
    C++学习之路: 函数适配器
    C++学习之路: 智能指针入门
    C++学习之路: 单例模板
    C++学习之路: 左值&右值 的讨论 和 ”move“ 值传递方式
    C++学习之路: 特殊操作符->的重载
    C++学习之路: 模板函数
    C++学习之路: 时间戳 封装成类
  • 原文地址:https://www.cnblogs.com/john-xiong/p/12673586.html
Copyright © 2020-2023  润新知