设计模式之2:简单工厂、工厂模式、抽象工厂
参考:http://icyfenix.iteye.com/blog/575040
一、简单工厂模式
1、基于哪两原则:
简单职责原则:一个类负责一件事情,一个类应该只有一个能引起他变化的因素。”
“变化点隔离原则:如果一块代码不同地方都有可能经常发生改变,则说明它们应该分开封装,同时避免超大类、超大方法的产生。
2、优势:让对象的调用者和对象的创建过程分离,当对象调用者需要对象时,直接向工厂请求即可。避免了对象调用者与对象的实现类以硬编码方式耦合,提高系统的可维护性和可扩展性。
3、缺陷:当产品修改时,工厂类也要相应修改。
4、应用场景:当使用者知道工厂需要的传入参数,并且对工厂如何生产出产品的逻辑并不关心。
简单工厂模式“以固定的业务逻辑应付不断的业务变化是不可能完成任务的”。使用工厂方法模式(Factory Method Pattern)来解决这些问题。
public class Computer { private Output out; public Computer(Output out){ this.out = out; } } public class OutputFactory{ public Output getOutput(){ return new Printer();//修改此次用了控制具体使用哪个Output的实现类 } } public interface Output{ ... } public class Printer implements Output{ ... } public class BetterPrinter implements Output{ ... }
二、工厂方法模式
简单工厂模式的作用和使用场景,它能为我们隔离开产品使用者与产品制造者的职责,使得这两部分代码脱离耦合。在看到简单工厂模式带来的好处的同时,我们也可以预见,随着工厂产品的丰富,每增加一个产品,都将需要修改工厂方法。这是简单工厂的特性——只能处理可预见的情况决定的。然而,频繁的修改代码是我们所不愿意看到的,通过修改代码来增加产品(功能),也违反了设计模式中的“开闭原则” 。
开闭原则:即“开放-闭合原则”,软件架构应该设计成对于修改来说,代码实体是关闭的,而对于扩展来说,代码实体则是开放的。通俗一点讲,开闭原则是鼓励通过“新增”来代替“变化”。鼓励设计者将业务逻辑抽象化,将逻辑的本质设计成抽象类或者接口,逻辑的具体实践操作设计成具体实现类。
现实世界中,业务的本质是相对稳定的,而业务的具体实践则总会不断变化。在逻辑改变的时候,应当做到可以只替换具体实现类,而会不影响到代码的架构。开闭原则是OOAD中的核心原则,在实现功能需求的时候,新增的代码与修改的代码之间的比值,某种程度上可以作为量化衡量一个软件系统的成熟程度的标志。
为了解决简单工厂的这个弊端,需要引入本章的主题:工厂方法模式(又称为工厂模式),主要是对“工厂”的封装,提供一个创建者的接口,将产品实例化的步骤延迟到工厂的子类完成,让子类决定需要创建什么产品。
方法:
定义创建者的抽象接口,延迟产品实例化到其子类之中,创建的产品一般不再通过传入参数控制,而直接决定于使用了哪个实现类作为其子类。我们需要什么产品,不再是靠外界传入参数决定,而是取决于抽象工厂使用的是具体哪个实现类。
值得指出的是,虽然示例代码中是直接通过new来创建具体工厂,但在现实编程中,大部分工厂方法模式都是配合这反射来使用的,既可以考虑把“SoldierFactory”、“MachineFactory”等具体实现类写在配置文件中,通过反射代替直接new一个具体工厂对象,这样当增加一个产品,就可以做到对工厂和使用者都不存在任何修改,只需要添加一个新的工厂类,并且配置到配置文件中即可,这就是开闭原则的一个体现。
工厂方法模式确实给系统架构带来强大而灵活的扩展能力。这种基于多态性设计的模式,使得产品的实例化延迟到具体子类之中,通过替换不同的实现子类就能达到新增和切换产品的目的。
简单工厂、工厂方法两种模式中,主要都是围绕这个体产品的创建进行讨论,看看各种具体对象之间存在互相依赖关系时,如何运用抽象工厂模式(Abstract Factory Pattern)来解决问题。
public class Computer { private Output out; public Computer(Output out){ this.out = out; } } public interface OutputFactory{ public Output getOutput();//使用时OutputFactory of = new PrinterFactory(); } public class PrinterFactory implements OutputFactory{ public Output getOutput(){ return new Printer(); } } public class BetterPrinterFactory implements OutputFactory{ public Output getOutput(){ return new BetterPrinter(); } }
三、抽象工厂模式
每一个具体工厂的目的都不是创造单个的具体产品,而是过把整个产品家族的所有具体产品都创造出来,这样各个具体产品之间的联系就通过一个具体工厂来体现。当需要新增一个产品家族,或者已有产品家族的构成成员发生变化的时候,影响也就局限在了某一个具体工厂之中。业务逻辑所依赖的都是抽象工厂和抽象产品,具体产品变化的影响就这样从业务逻辑中隔离开来。
这种通过依赖抽象类(或者接口)来削弱耦合、隔离变化点的方法,在设计模式中被总结为一个重要原则——依赖倒转原则:业务逻辑应该依赖于抽象类,而不应该依赖于具体实现类。“依赖倒转”中的“倒转”,指得就是人们直观思维之中,高层逻辑应当依赖与他下面的功能点分解,功能点分解又依赖于底层功能这种自上而下层层依赖的开发思想。这种开发思想中,任何底层的变化都有可能导致产生对上层的影响,同时,上层需求有改动的时候,又同样需要下层改动提供支持,这样当软件层次累计得越来越高的时候,变化就越来越可能引起上下层间的反复传递,导致严重的后果——牵一发而动全身。
面向对象中耦合关系有三种:零耦合、抽象耦合、具体耦合。依赖反转原则的本质意图就是将具体耦合削弱为抽象耦合。从下面两张图上可以看出,在互相依赖的模块中加入一层“抽象层”后,上层业务逻辑、下层具体实现的变化都受抽象类的隔离,变化就相对不容易在各层之间扩散。
图3.2 自上而下的依赖关系
图3.3 依赖反转
如果说开闭原则(见第二章)是设计模式追求的目标,那依赖反转原则就是设计模式的最基本、普遍的解决问题思路。从面向具体类编程到面向接口编程的转变,是一个程序员从代码实现者到设计者进化的标志。