Design Pattern 工厂模式

本质上而言, 这种模式就是用类来封装和拆分条件分支(if else, switch case), 带来的问题就是其实用类封装也是很麻烦的.

其实对于更高级的语言, 如动态语言python(通过eval, 如下例), 或FP语言(通过pattern match), 这种模式根本就没有存在的价值.

工厂模式概述

工厂模式, 包括简单工厂, 工厂方法, 抽象工厂, 为什么叫工厂, 因为工厂生产出来的产品是规格一致的(即接口一致的, 具有统一基类的), 否则叫作坊.
规格一致的好处是代码复用, 基于多态和里氏替换原则, 所有的代码都可以基于基类去编写. 当在不同的子类间切换时, 不需要更改任何代码, 因为根据里氏替换原则, 子类是可以完全替换父类的.

简单工厂模式

简单工厂模式是属于创建型模式,又叫做静态工厂方法(StaticFactory Method)模式,但不属于23种GOF设计模式之一。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。
场景, 在客户代码中, 需要根据不同的情况创建不同的对象.
那么直接的做法, 用大量的if... else...来判断, 条件不同创建不同的对象.
这样好不好? 明显不好, 如果需要选择的对象很多, 那在客户代码中需要加上大段的条件判断, 严重影响可读性, 使代码重点不突出. 而且也严重影响维护和扩展性, 每次对象增加都要来修改这段代码.
怎么办? 最简单的办法就是, 把这段代码挪到其他地方去, 这样客户端代码就清晰了, 可读性就强了, 这个就是简单工厂模式.
该模式其实只是对象创建代码做了简单的封装, 把对象创建的职责(大段的if...else...)放到一个简单工厂中, 而在客户代码只需提供简单参数, 就可以得到相应的对象, 而不需要关心对象创建过程.
这个模式的好处就是做到职责单一原则, 把复杂的对象创建代码放到专门的工厂类中去, 这样客户代码的可读性, 可维护性都大大增强.
缺点就是, 当需创建的对象很多, 逻辑复杂时, 工厂类本身的可读性和可维护性仍然比较差.

public class SimpleFactory{

  public static Mobile createMobile(String mobileName){
      if(mobileName.equals("NOKIA")){ 
              new Nokia();
      }else if(mobileName.equals("MOTOROLA")){
          new Motorola();
      }else{
              throw new Exception("还不支持该种类型的手机生产!");
} } }

客户代码,
object = SimpleFactory.createMobile('NOKIA')
客户这样就可以简单的获取对象, 而不关心具体的对象场景过程, 而且通过简单的修改参数就可以得到不同的对象.

工厂方法模式

简单工厂模式其实是把问题转移了, 并没有真正去解决. 把创建对象的逻辑从客户代码移到工厂类后, 工厂类仍然违反开闭原则, 每次增加新的对象都要修改代码, 而且当需要创建的对象很多时, 条件判断会很长很混乱, 严重影响可读性和维护性. 那怎么样才能真正解决这个问题? 思路是把一个工厂类化为许许多多的工厂类, 既然当一长串条件创建语句都放在一个工厂类中时显的混乱,难维护. 那么就把他打散, 将各个对象的创建过程分别抽象为多个具体的工厂类(如下图). 这样就符合开闭原则了,当增加对象时, 我们只需要加上一个具体的工厂类即可, 不需要修改本来的工厂类的代码. 

客户代码就由,
object = SimpleFactory.createMobile('NOKIA' )
换成,
Ifactory factory= new NokiaFactory ()
object = factory.createMobile()
可见对于客户代码而言, 简单工厂和工厂实际没有区别的. 当用户需要切换对象时, 只需要修改一处代码就可以实现, 其他地方都可以利用多态来避免代码的修改.

工厂方法的好处就是让工厂类具有可扩展性, 符合开闭原则.
坏处是, 当要创建的对象很多时, 需要创建大量的具体工厂类.

抽象工厂模式

抽象工厂为了解决工厂模式的工厂类数目膨胀过快的问题, 如果对每个新的类都生成新的工厂类的话, 可想而知...
所以如果新创建的类是有一定层次结构的, 我们就可以用增加类方法的办法来减少工厂类的生成.
下面的例子, 如果Nokia生成很多产品, 如mobile, tv, 用工厂模式, 我们需要创建NokiaMobileF, NokiaTVF…
而对于抽象工厂, 可以把Nokia层抽象出来作为抽象工厂类, 把mobile和tv之类的作为类方法去实现

Ifactory factory= new NokiaFactory ()
object = factory.createMobile()
现在Nokia不光有Mobile, 也生产TV, 那就可以通过增加类函数来解决, 避免增加工厂类 
factory.createTV()
抽象工厂模式的缺点很明显, 当你要增加一个产品时, 相当麻烦. 要在抽象工厂类和所有派生类里面增加create接口, 相当烦人

工厂模式无用?

前面介绍了简单工厂-->工厂方法-->抽象工厂模式这样的改进思路, 但是好像这样是越改越复杂, 虽然增加了可扩展性, 但是付出的代价似乎大了点, 有没有捷径?|
通常而言, 捷径是没有的, 但是这里的情况是有时会有, 因为对于某些语言支持....(不知怎么描述这种特性, 语句动态执行?), 如.Net里面的反射, 或python里面的eval
有了这样的特性, 我们就有一种新的去除大量条件语句的思路,
如下面的例子, .Net我不熟, 就以Python为例子,

class NokiaMobile:
    def __init__(self):
        self.name = 'nokia5500'
class MotoMobile:
    def __init__(self):
        self.name = 'moto1000'
class SimpleFactory:
    def __init__(self):
        self.name = 'Mot'
    def createMobile(self):
        if self.name =='Nokia':
            return NokiaMobile()
        if self.name =='Moto':
            return MotoMobile()
        else:
            print 'No this name'
            return None
    def createMobileEval(self):
        class_name = self.name + 'Mobile()'
        try:
            object = eval(class_name)
        except Exception, e:
            print 'No this class'+ str(e)
            object = None
        return object

很清楚可以看到, 在createMobile函数中, 我们需要使用大量的条件语句, 随着品牌的变多, 这个函数会越来越长, 越来越复杂.
而对于createMobileEval, 无论你增加多少品牌, 现在的代码都已经足够, 有了这种语言特性, 非常简单的就解决了问题.
如果你需要增加产品, 只需要在加个函数就可以, 如createTVEval
为了自由切换品牌或品牌系列而不用改变任何客户代码, 我们还可以把切换开关(需要的品牌名)放到全局变量里, 或配置文件里. 这样就perfect了......
有了这种方法根本就不需要使用工厂方法和抽象工厂, 模式是死的, 技术是活的......

原文地址:https://www.cnblogs.com/fxjwind/p/2893152.html