工厂模式

 

本文主要论述了以下几方面:

  1.总论

  2.简单工厂模式

  3.工厂方法模式

  4.抽象工厂模式

1.总论

  在工厂设计模式中,客户端可以请求一个对象,而无需知道這个对象来自哪里;也就是说,使用哪个类来生成这个对象。工厂背后的思想是简化对象的创建。与客户端自己基于类实例化直接创建对象相比,基于一个中心化函数来实现,更易于追踪创建了哪些对象。通过将创建对象的代码和使用对象的代码解耦,工厂能够降低应用维护的复杂度。
  工厂通常有两种形式:一种是工厂方法( Factory Method),它是一个方法,对不同的输入参数返回不同的对象;第二种是抽象工厂,它是一组用于创建一系列相关事物对象的工厂方法。
  工厂模式分类:

  • 简单工厂模式
  • 工厂方法
  • 抽象工厂

  这三种模式从上到下逐步抽象,并且更具一般性。
  三者区别:
    工厂方法模式:简单工厂模式可视为工厂方法的一种特例。一个抽象产品类,可以派生出多个具体产品类。 一个抽象工厂类,可以派生出多个具体工厂类。 每个具体工厂类只能创建一个具体产品类的实例。
    抽象工厂模式:多个抽象产品类,每个抽象产品类可以派生出多个具体产品类。 一个抽象工厂类,可以派生出多个具体工厂类。 每个具体工厂类可以创建多个具体产品类的实例。
  总结:
  工厂方法模式只有一个抽象产品类,而抽象工厂模式有多个。
  工厂方法模式的具体工厂类只能创建一个具体产品类的实例,而抽象工厂模式可以创建多个。

2.简单工厂模式

  建立一个一个工厂来制造新的对象。它存在的目的很简单:定义一个用于创建对象的接口。 

  按照面向过程的习惯,可能直接在客户端中根据条件来创建不同的具体产品实例;对于简单工厂模式而言,是把这部分逻辑抽象出来,放在一个工厂类中,由工厂类负责产生具体的对象,也就是将生产者和消费者分离了。

  简单工厂模式,通过专门定义一个类来负责创建具体类型的实例,要创建的实例一般继承自同一个类;

  结构一般如下所示:

      

 

  如上所示,工厂模式中的角色一般包括:

  工厂角色:即上图中的OperationFactory,它可以被客户端调用,其内部负责创建具体的对象;

class OperationFactory:  # 定义一个工厂,根据用户输入来生产实例对象

    operator = {}
    operator["+"] = OperationAdd()
    operator["-"] = OperationSub()
    operator["*"] = OperationMul()
    operator["/"] = OperationDiv()

    def createOperation(self, ch):
        if ch in self.operator:
            op = self.operator[ch]
        else:
            op = OperationUndef()
        return op

  抽象产品类:即上图中的抽象类Operation,它描述了所有实例公共的接口;

class Operation:  # 基类,所有操作类都从此类继承
    def GetResult(self):
        pass

  具体产品类:即上图中的OperationAdd等,实现抽象产品的接口,是工厂角色中要创建的具体实例

class OperationAdd(Operation):  # 加法类
    def GetResult(self):
        return self.op1 + self.op2


class OperationSub(Operation):   # 减法类
    def GetResult(self):
        return self.op1 - self.op2


class OperationMul(Operation):  # 乘法类
    def GetResult(self):
        return self.op1 * self.op2


class OperationDiv(Operation):  # 除法类
    def GetResult(self):
        try:
            result = self.op1 / self.op2
            return result
        except ZeroDivisionError as e:
            print("error:divided by zero.",e)
            return 0

  主函数实现:

if __name__ == "__main__":
    op = input("operator: ")
    opa = int(input("a: "))
    opb = int(input("b: "))
    factory = OperationFactory()
    cal = factory.createOperation(op)
    cal.op1 = opa
    cal.op2 = opb
    print(cal.GetResult())
主函数

  简单工厂优缺点总结:

    优点:从上面的描述可以看出,工厂角色负责产生具体的实例对象,所以在工厂类中需要有必要的逻辑,通过客户的输入能够得到具体创建的实例;所以客户端就不需要感知具体对象是怎么产生的,只需要将必要的信息提供给工厂即可;

    缺点:简单工厂模式是违反“开闭原则”,即对扩展开放,对修改关闭;因为如果要新增具体产品,就需要修改工厂类的代码;

  下面我们从开闭原则上来分析下简单工厂模式。当客户不再满足现有功能的时候,想要一个开方操作,只要这种操作符合抽象产品制定的合同,那么只要通知工厂类知道就可以被客户使用了。所以对产品部分来说,它是符合开闭原则的;但是工厂部分好像不太理想,因为每增加一种操作,都要在工厂类(OperationFactory)中增加相应的创建业务逻辑(方法需要新增case来判断创建什么样的操作),这显然是违背开闭原则的。可想而知对于新产品的加入,工厂类是很被动的。对于这样的工厂类,我们称它为全能类 或者上帝类。 

  我们举的例子是最简单的情况,而在实际应用中,很可能产品是一个多层次的树状结构。由于简单工厂模式中只有一个工厂类来对应这些产品,所以这可能会把我们的上帝累坏了,也累坏了我们这些程序员。于是工厂方法模式作为救世主出现了。 工厂类定义成了接口,而每新增的操作,就增加该种类型对应工厂类的实现,这样工厂的设计就可以扩展了,而不必去修改原来的代码。

3.工厂方法模式

  工厂方法模式:定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法让类把实例化推迟到了子类。

  对于简单工厂而言,创建对象的逻辑判断放在了工厂类中,客户不感知具体的类,但是工厂类违反了开闭原则,如果要新增新的具体类,就必须修改工厂类;

  对于工厂模式而言,是通过扩展来新增具体类的,但是在客户端就必须感知到具体的类了,要通过具体类的创建工厂来创建具体的实例,也就是判断逻辑由简单工厂的工厂类挪到了客户端中;

  工厂模式横向扩展很方便;

  结构一般如下所示:

    

  工厂方法模式组成:
  1)抽象工厂角色: 这是工厂方法模式的核心,它与应用程序无关。是具体工厂角色必须实现的接口或者必须继承的父类。

import abc

class Creator(metaclass=abc.ABCMeta):
    """
    Declare the factory method, which returns an object of type Product.
    Creator may also define a default implementation of the factory
    method that returns a default ConcreteProduct object.
    Call the factory method to create a Product object.
    """

    def __init__(self):
        self.product = self._factory_method()

    @abc.abstractmethod
    def _factory_method(self):
        pass

    def some_operation(self):
        self.product.interface()
抽象工厂

  2)具体工厂角色:它含有和具体业务逻辑有关的代码。由应用程序调用以创建对应的具体产品的对象。

class ConcreteCreator1(Creator):
    """
    Override the factory method to return an instance of a
    ConcreteProduct1.
    """

    def _factory_method(self):
        return ConcreteProduct1()


class ConcreteCreator2(Creator):
    """
    Override the factory method to return an instance of a
    ConcreteProduct2.
    """

    def _factory_method(self):
        return ConcreteProduct2()
具体工厂

  3)抽象产品角色:它是具体产品继承的父类或者是实现的接口。

import abc
class Product(metaclass=abc.ABCMeta):
    """
    Define the interface of objects the factory method creates.
    """

    @abc.abstractmethod
    def interface(self):
        pass
View Code

  4)具体产品角色:具体工厂角色所创建的对象就是此角色的实例。

class ConcreteProduct1(Product):
    """
    Implement the Product interface.
    """

    def interface(self):
        pass


class ConcreteProduct2(Product):
    """
    Implement the Product interface.
    """

    def interface(self):
        pass
具体产品

  主函数实现:

def main():
    concrete_creator = ConcreteCreator1()
    concrete_creator.product.interface()
    concrete_creator.some_operation()


if __name__ == "__main__":
    main()
主函数

  这个和简单工厂有区别,简单工厂模式只有一个工厂,工厂方法模式对每一个产品都有相应的工厂

  

  工厂方法模式使用继承自抽象工厂角色的多个子类来代替简单工厂模式中的“上帝类”。正如上面所说,这样便分担了对象承受的压力;而且这样使得结构变得灵活起来——当有新的产品产生时,只要按照抽象产品角色、抽象工厂角色提供的合同来生成,那么就可以被客户使用,而不必去修改任何已有 的代码。可以看出工厂角色的结构也是符合开闭原则的! 

   工厂方法模式优缺点总结:

    优点:1.增加一个产品,只需要增加产品类和对应的工厂类,而不必修改总的工厂类。可以使代码结构清晰,有效地封装变化。

       2.对调用者屏蔽具体的产品类。如果使用工厂模式,调用者只关心产品的接口就可以了,至于具体的实现,调用者根本无需关心。即使变更了具体的实现,对调用者来说没有任何影响。

       3.降低耦合度。产品类的实例化通常来说是很复杂的,它需要依赖很多的类,而这些类对于调用者来说根本无需知道,如果使用了工厂方法,我们需要做的仅仅是实例化好产品类,然后交给调用者使用。对调用者来说,产品所依赖的类都是透明的。

    缺点:增加新的产品类,会修改客户端代码,工厂方法只是把简单工厂的内部逻辑判断移到了客户端进行。简单对象,特别是只需要通过new就可以完成创建的对象,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。

   可以看出工厂方法的加入,使得对象的数量成倍增长。当产品种类非常多时,会出现大量的与之对应的工厂对象,这不是我们所希望的。因为如果不能避免这种情 况,可以考虑使用简单工厂模式与工厂方法模式相结合的方式来减少工厂类:即对于产品树上类似的种类(一般是树的叶子中互为兄弟的)使用简单工厂模式来实 现。

  工厂方法模式应用场景:

  •   当客户程序不需要知道要使用对象的创建过程。 
  •      客户程序使用的对象存在变动的可能,或者根本就不知道使用哪一个具体的对象。
  •      工厂模式是一种典型的解耦模式,迪米特法则在工厂模式中表现的尤为明显。假如调用者自己组装产品需要增加依赖关系时,可以考虑使用工厂模式。将会大大降低对象之间的耦合度。
  •      当需要系统有比较好的扩展性时,可以考虑工厂模式,不同的产品用不同的实现工厂来组装。

4.抽象工厂模式  

  抽象工厂设计模式是抽象方法的一种泛化。概括来说,一个抽象工厂是(逻辑上的)一组工厂方法,其中的每个工厂方法负责产生不同种类的对象。

  可以说,抽象工厂模式和工厂方法模式的区别就在于需要创建对象的复杂程度上。而且抽象工厂模式是三个里面最为抽象、最具一般性的。 
  抽象工厂模式的用意为:给客户端提供一个接口,可以创建多个产品族中的产品对象 ,而且使用抽象工厂模式还要满足一下条件:

  •      系统中有多个产品族,而系统一次只可能消费其中一族产品。 
  •      同属于同一个产品族的产品以其使用。

 抽象工厂模式与工厂方法模式的区别:

  抽象工厂模式是工厂方法模式的升级版本,他用来创建一组相关或者相互依赖的对象。他与工厂方法模式的区别就在于,工厂方法模式针对的是一个产品等级结构;而抽象工厂模式则是针对的多个产品等级结构。在编程中,通常一个产品结构,表现为一个接口或者抽象类,也就是说,工厂方法模式提供的所有产品都是衍生自同一个接口或抽象类,而抽象工厂模式所提供的产品则是衍生自不同的接口或抽象类。

        在抽象工厂模式中,有一个产品族的概念:所谓的产品族,是指位于不同产品等级结构中功能相关联的产品组成的家族抽象工厂模式所提供的一系列产品就组成一个产品族;而工厂方法提供的一系列产品称为一个等级结构。我们拿生产汽车的例子来说明他们之间的区别。

  

  在上面的类图中,两厢车和三厢车称为两个不同的等级结构;而2.0排量车和2.4排量车则称为两个不同的产品族。再具体一点,2.0排量两厢车和2.4排量两厢车属于同一个等级结构,2.0排量三厢车和2.4排量三厢车属于另一个等级结构;而2.0排量两厢车和2.0排量三厢车属于同一个产品族,2.4排量两厢车和2.4排量三厢车属于另一个产品族。

        明白了等级结构和产品族的概念,就理解工厂方法模式和抽象工厂模式的区别了,如果工厂的产品全部属于同一个等级结构,则属于工厂方法模式如果工厂的产品来自多个等级结构,则属于抽象工厂模式。在本例中,如果一个工厂模式提供2.0排量两厢车和2.4排量两厢车,那么他属于工厂方法模式;如果一个工厂模式是提供2.4排量两厢车和2.4排量三厢车两个产品,那么这个工厂模式就是抽象工厂模式,因为他提供的产品是分属两个不同的等级结构。当然,如果一个工厂提供全部四种车型的产品,因为产品分属两个等级结构,他当然也属于抽象工厂模式了。

  结构一般如下所示:

    

 抽象工厂模式的各个角色(和工厂方法一样): 

  •      抽象工厂角色: 这是工厂方法模式的核心,它与应用程序无关。是具体工厂角色必须实现的接口或者必须继承的父类。
import abc


class AbstractFactory(metaclass=abc.ABCMeta):
    """
    Declare an interface for operations that create abstract product
    objects.
    """

    @abc.abstractmethod
    def create_product_a(self):
        pass

    @abc.abstractmethod
    def create_product_b(self):
        pass
抽象工厂角色
  •   具体工厂角色:它含有和具体业务逻辑有关的代码。由应用程序调用以创建对应的具体产品的对象。
class ConcreteFactory1(AbstractFactory):
    """
    Implement the operations to create concrete product objects.
    """

    def create_product_a(self):
        return ConcreteProductA1()

    def create_product_b(self):
        return ConcreteProductB1()


class ConcreteFactory2(AbstractFactory):
    """
    Implement the operations to create concrete product objects.
    """

    def create_product_a(self):
        return ConcreteProductA2()

    def create_product_b(self):
        return ConcreteProductB2()
具体工厂角色
  •      抽象产品角色:它是具体产品继承的父类或者是实现的接口。
class AbstractProductA(metaclass=abc.ABCMeta):
    """
    Declare an interface for a type of product object.
    """

    @abc.abstractmethod
    def interface_a(self):
        pass
抽象产品角色A
抽象产品角色B
  •      具体产品角色:具体工厂角色所创建的对象就是此角色的实例。
class ConcreteProductA1(AbstractProductA):
    """
    Define a product object to be created by the corresponding concrete
    factory.
    Implement the AbstractProduct interface.
    """

    def interface_a(self):
        pass


class ConcreteProductA2(AbstractProductA):
    """
    Define a product object to be created by the corresponding concrete
    factory.
    Implement the AbstractProduct interface.
    """

    def interface_a(self):
        pass
具体产品角色A
class ConcreteProductB1(AbstractProductB):
    """
    Define a product object to be created by the corresponding concrete
    factory.
    Implement the AbstractProduct interface.
    """

    def interface_b(self):
        pass


class ConcreteProductB2(AbstractProductB):
    """
    Define a product object to be created by the corresponding concrete
    factory.
    Implement the AbstractProduct interface.
    """

    def interface_b(self):
        pass
具体产品角色B

  主函数实现:

def main():
    for factory in (ConcreteFactory1(), ConcreteFactory2()):
        product_a = factory.create_product_a()
        product_b = factory.create_product_b()
        product_a.interface_a()
        product_b.interface_b()


if __name__ == "__main__":
    main()
主函数

 抽象工厂模式的优缺点:

  优点: 抽象工厂模式除了具有工厂方法模式的优点外,最主要的优点就是可以在类的内部对产品族进行约束。所谓的产品族,一般或多或少的都存在一定的关联,抽象工厂模式就可以在类内部对产品族的关联关系进行定义和描述,而不必专门引入一个新的类来进行管理。

  缺点:产品族的扩展将是一件十分费力的事情,假如产品族中需要增加一个新的产品,则几乎所有的工厂类都需要进行修改。所以使用抽象工厂模式时,对产品等级结构的划分是非常重要的。

  适用场景:

   当需要创建的对象是一系列相互关联或相互依赖的产品族时,便可以使用抽象工厂模式。说的更明白一点,就是一个继承体系中,如果存在着多个等级结构(即存在着多个抽象类),并且分属各个等级结构中的实现类之间存在着一定的关联或者约束,就可以使用抽象工厂模式。假如各个等级结构中的实现类之间不存在关联或约束,则使用多个独立的工厂来对产品进行创建,则更合适一点。

 总结,无论是简单工厂模式,工厂方法模式,还是抽象工厂模式,他们都属于工厂模式,在形式和特点上也是极为相似的,他们的最终目的都是为了解耦。在使用时,我们不必去在意这个模式到底工厂方法模式还是抽象工厂模式,因为他们之间的演变常常是令人琢磨不透的。经常你会发现,明明使用的工厂方法模式,当新需求来临,稍加修改,加入了一个新方法后,由于类中的产品构成了不同等级结构中的产品族,它就变成抽象工厂模式了;而对于抽象工厂模式,当减少一个方法使的提供的产品不再构成产品族之后,它就演变成了工厂方法模式。

       所以,在使用工厂模式时,只需要关心降低耦合度的目的是否达到了。

 

posted @ 2017-07-03 19:17  看雪。  阅读(708)  评论(0编辑  收藏  举报