代码改变世界

创建模式 - 3 工厂模式

2018-05-16 00:47  乱月灵猫  阅读(171)  评论(0编辑  收藏  举报
工厂模式包括:
(1)静态工厂模式:产品实体类继承自一个接口,工厂是实体类,通过入参决定具体产品的创建。
(2)工厂方法模式:产品实体类继承自一个接口,工厂实体类继承自一个接口,具体的工厂实体类对应具体的产品实体类。
(3)抽象工厂模式:多个产品族对应多个产品接口,每个产品接口有具体的实现类产品,工厂实体类继承自一个接口,接口定义了产品族的生产方法,工厂实体类实现某一个等级结构的产品族的生产功能
 
个人总结:静态工厂/简单工厂,严格意义上来讲,它并不是一种设计模式,它仅仅是做了一层封装,把new一个对象的过程隐藏起来,交由工厂来控制,这种方法从某种意义上来说是一种封装/抽象/隔离,降低了耦合。工厂方法模式将每个产品用一个单独的工厂来实现生产,符合开闭原则,也就是不修改代码,而是添加代码,就可以实现扩展功能,相比简单工厂的方法,这种设计模式代码量会增大,因此如果不会有什么扩展的情况下,使用简单工厂也不是不可以的。那么抽象工厂不具有两者的缺点,它既符合开闭原则,又能够适当减少代码量。但是有一些限制。这里引入产品族和产品系列(下面叫产品等级结构,有点难以理解,所以这里我起了个名字叫产品系列。比如CPU有型号a,b,c,CPU就是一个产品系列-抽象,a,b,c是产品的型号-具体实例)的概念,可以认为简单工厂和工厂方法都是针对于一个产品系列,它只负责生成不同系列的同类产品,比如生成不同型号的CPU,而抽象工厂模式是针对多个产品系列的,这些产品系列之间是有着关联的,更进一步说是一一对应的,比如产品系列A的产品A1对应产品系列B的产品对应产品B1。A1和B1就组成了一个产品族。它把不同系列的相关产品交由工厂来创建生产,得到一个型号匹配的产品族。
 
举个例子:
 
问题描述:一个公司负责向某自行车厂提供零件,包括四个零件 - 车轮,鞍座,车把,车架。自行车厂要求大小两种型号,因为要生产两种型号的自行车。零件的型号自然是一一对应的,大车轮配小车架是不可以的。
 
简单工厂:老板(创建者)首先做了个简单的归类(抽象)。车轮一个工厂,鞍座一个工厂...4个工厂,分别生产不同的零件,每个工厂生产大小型号的零件。老板觉得这种方式还挺便于管理的,如果当自行车厂要自行车的零件时,他就从各个工厂提出零件,然后组到一起发给自行车厂。这里说一下怎么提,每个厂有个提货板,老板只需要到每个厂写上的型号,工厂的负责人看到后就把货送到老板那里了。
 
问题来了。一天自行车厂说我们发现中型的用户也很多,能不能提供中型的零件啊?不能的话我们就断了合作,以后就不再找你继续提供零件了。公司老板拍拍胸脯,说可以啊,没问题。等他回公司找各个厂的负责人聊了一下,厂里的负责任都面露难色,因为工厂的厂房都布置好了,调试设备确保无误花了不少时间,而且再加设备不好加啊,扩大厂房很麻烦,下次再来个迷你的,那还得折腾一次,不符合我们公司的开闭原则啊。
 
工厂方法:于是老板一拍桌子,我们不是还有地吗,继续建厂,这次多建一些。一个零件的一个型号一个厂,钱有的是,地有的是,就先这么搞了。以后再有新型号需求,就继续建厂。厂长觉得这样应该可以了吧,很解耦,很符合开闭原则。这时候提货的方式变了,老板只有一个提货板,只要写上零件名和型号,就会由公司的一个叫抽象的部门负责通知特定的工厂生产零件。老板写一条,就要有一个抽象部门的人负责点货。
 
问题又来了:自行车厂说还要增两种型号,老板懵了,因为太多的厂,每个厂一个型号的零件,很不便于管理啊。有一次自行车厂说要一批5种型号的的自行车,他就写了15项,结果一下子需要15个抽象部门的人,杂乱的不行。而且事后还得自己组装,每个型号都得自己打包,很容易弄错啊,之前就因为抽象部门两个人长得像弄错过一次就被骂了,结果小车轮配了个超大的车架,差点和自行车厂断了合作关系。
 
抽象工厂:于是老板决定再做改革。这次他决定把同一型号的不同产品放到一个工厂,生产出来的东西可以直接打包给他,也可以单独提。这样工厂少了很多,再有新型号只需要新建一个厂就可以了。反正短期内自行车不会长出翅膀,这些零件基本定了,来了新型号也不用动老厂房。而提货板也进行了分栏,一个型号一个栏,老板只需要在对应的厂上填写要的零件就可以了。而每个厂有一个抽象部门的人负责点货,它只需要把提货单发给厂房,厂房把货给他。这样老板和一个抽象部门的人打交道,就能拿到一个自行车的所有零件,再不会出错了。一方面便于管理(对于开发者而言),另一方面也不用担心拼装出错,只需要找对应的抽象部门的人说要哪些零件就好(对于使用者而言)。两全其美的方法就这么产生了。
 
可以看到,当生产简单的产品时,用工厂方法还是比较合适的。当产品形成了产品族的规模,就要考虑抽象工厂方法了。每个工厂负责管理相互依赖的具体产品的生产,不会出岔子。
 
思考一个问题:什么时候要用工厂模式?
 
问题的根源在于在调用曾不知道想要一个什么样的对象,因为你拿到的是对象的抽象。比如你作为一个工作流程的其中一环,你只知道要A这样的一种对象,至于是A1还是A2还是A3,甚至是A100,你是不清楚的,这个得由上一环的人来告诉你,至于上一环怎么知道的,我们不care。这时候别懵逼,工厂模式就派上用场了。你只需要把上一环给你的参数一传,至于拿到的是什么产品,无所谓,反正是A类的就行,你都能用相同的办法处理。这就是工厂的意义所在,在不知道具体要一个什么对象的时候,它负责提供给一个正确的对象。实际上简单工厂和工厂方法都满足了这个要求,只是简单工厂用一层抽象来搞定,不符合开闭原则,如果涉及到的具体类太多,就比较乱,而工厂方法使用了两层抽象,对具体的产品做了抽象,又对工厂做了抽象,符合了开闭原则,上层的参数不再是决定产品,而是决定工厂,再由工厂决定产品(就一个产品,没得选)。也印证了一个观点,用加一层的方式来解决问题。而抽象工厂是一种另一个维度的扩展,为了保证某种一一对应的关系,便于管理,每个工厂负责生产一个产品族。上层的参数仍然是决定工厂,而至于要使用产品族里的什么产品,就交由这一层的代码逻辑了。
 
当然,比如在调用工厂的这一层明确知道要什么产品,也是可以用工厂模式的,只是没有发挥抽象的作用,很多例子都是在main函数里直接
 
IFactory f1 = new Factory1(**)
IFactory f2 = new Factory2(**)
IProduct p1 = f1.create()
IProduct p2 = f2.create()
 
就结束了,这样是比较奇怪的,没有体现出这个模式的意义,跟直接new一个对象有什么区别?这比静态工厂还low
 
一种方式:对工厂继续工厂
 
FactoryManager fm = new FactoryManager(); // 这又是静态工厂了,不好(所以上面我写的组装自行车的例子并不是很好,不便于理解工厂的意义)
IFactory f = fm.createFactory(**);
IProduct = f.create()
 
另一种方式:把工厂作为参数传给下一层,在下一层调用工厂来create。 这个是很普遍的用法,比如在创建线程池的时候传一个Factory的对象。
 
那么可以理解,在本层就要创建对象,在对对象一无所知的情况下,要么上一层给你一个工厂(动态工厂的常用用法),要么用静态工厂。即便用的是工厂方法模式,也是静态的用法。
 
理解抽象工厂
 
抽象工厂模式与工厂方法模式的区别
        抽象工厂模式是工厂方法模式的升级版本,他用来创建一组相关或者相互依赖的对象。他与工厂方法模式的区别就在于,工厂方法模式针对的是一个产品等级结构;而抽象工厂模式则是针对的多个产品等级结构。在编程中,通常一个产品结构,表现为一个接口或者抽象类,也就是说,工厂方法模式提供的所有产品都是衍生自同一个接口或抽象类,而抽象工厂模式所提供的产品则是衍生自不同的接口或抽象类。
        在抽象工厂模式中,有一个产品族的概念:所谓的产品族,是指位于不同产品等级结构中功能相关联的产品组成的家族。抽象工厂模式所提供的一系列产品就组成一个产品族;而工厂方法提供的一系列产品称为一个等级结构。
  
        明白了等级结构和产品族的概念,就理解工厂方法模式和抽象工厂模式的区别了,如果工厂的产品全部属于同一个等级结构,则属于工厂方法模式;如果工厂的产品来自多个等级结构,则属于抽象工厂模式。
 
抽象工厂模式的优点
        抽象工厂模式除了具有工厂方法模式的优点外,最主要的优点就是可以在类的内部对产品族进行约束。所谓的产品族,一般或多或少的都存在一定的关联,抽象工厂模式就可以在类内部对产品族的关联关系进行定义和描述,而不必专门引入一个新的类来进行管理。
 
抽象工厂模式的缺点
       产品族的扩展将是一件十分费力的事情,假如产品族中需要增加一个新的产品,则几乎所有的工厂类都需要进行修改。所以使用抽象工厂模式时,对产品等级结构的划分是非常重要的。
 
适用场景
       当需要创建的对象是一系列相互关联或相互依赖的产品族时,便可以使用抽象工厂模式。说的更明白一点,就是一个继承体系中,如果存在着多个等级结构(即存在着多个抽象类),并且分属各个等级结构中的实现类之间存在着一定的关联或者约束,就可以使用抽象工厂模式。假如各个等级结构中的实现类之间不存在关联或约束,则使用多个独立的工厂来对产品进行创建,则更合适一点。
 
总结

  工厂方法模式是简单工厂模式的衍生,解决了许多简单工厂模式的问题。首先完全实现‘开-闭 原则’,实现了可扩展。其次更复杂的层次结构,可以应用于产品结果复杂的场合。工厂方法模式的对简单工厂模式进行了抽象。有一个抽象的Factory类(可以是抽象类和接口),这个类将不在负责具体的产品生产,而是只制定一些规范,具体的生产工作由其子类去完成。在这个模式中,工厂类和产品类往往可以依次对应。即一个抽象工厂对应一个抽象产品,一个具体工厂对应一个具体产品,这个具体的工厂就负责生产对应的产品。
  抽象工厂模式是所有形态的工厂模式中最为抽象和最具一般性的一种形态。抽象工厂模式是指当有多个抽象角色时,使用的一种工厂模式。抽象工厂模式可以向客户端提供一个接口,使客户端在不必指定产品的具体的情况下,创建多个产品族中的产品对象。根据LSP原则,任何接受父类型的地方,都应当能够接受子类型。因此,实际上系统所需要的,仅仅是类型与这些抽象产品角色相同的一些实例,而不是这些抽象产品的实例。换言之,也就是这些抽象产品的具体子类的实例。工厂类负责创建抽象产品的具体子类的实例(这句不太好,这句描述适用于任何一种工厂)。
  无论是简单工厂模式,工厂方法模式,还是抽象工厂模式,他们都属于工厂模式,在形式和特点上也是极为相似的,他们的最终目的都是为了解耦。在使用时,我们不必去在意这个模式到底工厂方法模式还是抽象工厂模式,因为他们之间的演变常常是令人琢磨不透的。经常你会发现,明明使用的工厂方法模式,当新需求来临,稍加修改,加入了一个新方法后,由于类中的产品构成了不同等级结构中的产品族,它就变成抽象工厂模式了;而对于抽象工厂模式,当减少一个方法使的提供的产品不再构成产品族之后,它就演变成了工厂方法模式。