下面介绍三种设计模式,简单工厂模式,工厂方法模式,抽象工厂模式
思考如下场景:
有一天,林同学准备去买笔记本,他到商城发现有两款电脑他特别喜欢, 一款是 Macbook Pro, 另一款是 Surface Pro。
根据以上的场景,类图可以如下表示:
interface Computer { public void printComputer(); } class MacbookProComputer implements Computer { public void printComputer() { System.out.println("This is a macbook pro"); } } class SurfaceBookComputer implements Computer { public void printComputer() { System.out.println("This is a surface book"); } } public class Client { public void buy(Computer c){ c.printComputer(); } public static void main(String[] args) { Client c = new Client(); c.buy(new SurfaceBookComputer()); } }
这时候问题就来了,客户只关心得到电脑,并不关心电脑是如何被生产出来的,假设这里电脑是由鼠标,键盘和显示器组成,那么我们还需要先创建这些类实例,才能创建电脑。
Keyboard keyboard = new Keyboard(); Display display = new Display(); Mouse mouse = new Mouse(); Client c = new Client(); c.buy(new SurfaceBookComputer(keyboard, display, mouse));
显然商家在卖电脑的时候不会要求客户自己把电脑组装好的吧。
简单工厂模式
下面就先介绍简单工厂模式。
简单工厂模式:专门定义一个类用来创建其它类的实例,被创建的实例通常都具有共同的父类。
这里我们相当于是创建生产电脑的工厂,客户需要购买什么样的电脑,只要输入类型编号就可以获取该电脑,而无需知道该电脑是如何被生产出来的。
类图如下表示:
Java 代码:
interface Computer { public void printComputer(); } class MacbookProComputer implements Computer { public void printComputer() { System.out.println("This is a macbook pro"); } } class SurfaceBookComputer implements Computer { public void printComputer() { System.out.println("This is a surface book"); } } class ComputerFactory { public Computer createComputer(String type) { Computer c = null; if(type.equals("macbook")) { c = new MacbookProComputer(); }else if(type.equals("surface")) { c = new SurfaceBookComputer(); } return c; } } public class Client { public void buy(Computer c){ System.out.println("I buy a computer"); c.printComputer(); } public static void main(String[] args) { Client c = new Client(); ComputerFactory cf = new ComputerFactory(); Computer computer = cf.createComputer("macbook"); c.buy(computer); } }
工厂模式的角色一般包括:
- 工厂角色: 如上图的 ComputerFactory,它可以被客户端调用,其内部用于负责创建具体的对象。
- 抽象产品类:如上图的 Computer,它描述了所有实例的公共接口。
- 具体产品类:如上图的 MacbookProComputer,实现抽象产品的接口,是工厂角色中要创建的具体实例。
简单工厂模式的优点从上面两种方式对比可以看出,工厂角色负责产生具体的实例对象,所以在工厂类中需要有必要的逻辑,通过客户的输入能够得到具体创建的实例;所以客户端就不需要感知具体对象是如何产生的,只需要将必要的信息提供给工厂即可。
这时候负责这个工厂的产品经理说该工厂需要生产新的产品 Macbook Air, 那么该方法的缺点就暴露显现,一般工厂生产方法和具体产品实现一般是由不同工程师开发的,那么如果实现产品的工程师早早实现了新产品而工厂方法却一直没有更新,那么该产品就一直无法上架。其次代码耦合度太高,如果工厂方法由新来的工程师去修改的话,那么他又得先读懂源代码,效率显得低下。
所以简单工厂模式的缺点:简单工厂模式是违反“开闭原则”,即对扩展开放,对修改关闭;因为如果要新增具体产品,就需要修改工厂类的代码。
针对简单工厂模式暴露出来的弊端,我们需要对代码再进行改进,由此延伸出工厂方法模式。
工厂方法模式
工厂方法模式:定义一个用来创建对象的接口,让子类决定实例化哪一个类,让子类决定实例化延迟到子类。
工厂方法模式是针对每个产品提供一个工厂类,在客户端中判断使用哪个工厂类去创建对象。
我们将之前的 ComputerFactory 抽象成一个接口,那么创建相应具体的工厂类去实现该接口的方法。
具体类图的实现:
Java 代码实现:
interface Computer { public void printComputer(); } class MacbookProComputer implements Computer { public void printComputer() { System.out.println("This is a macbook pro"); } } class SurfaceBookComputer implements Computer { public void printComputer() { System.out.println("This is a surface book"); } } interface ComputerFactory { public Computer createComputer(); } class MsFactory implements ComputerFactory { public Computer createComputer(){ return new SurfaceBookComputer(); } } class AppleFactory implements ComputerFactory { public Computer createComputer() { return new MacbookProComputer(); } } public class Client { public void buy(Computer c){ System.out.println("I buy a computer"); c.printComputer(); } public static void main(String[] args) { Client c = new Client(); ComputerFactory cf = new AppleFactory(); Computer computer = cf.createComputer(); c.buy(computer); } }
工厂方法模式是针对每个产品提供一个工厂类,在客户端中判断使用哪个工厂类去创建对象。
对比简单工厂模式和工厂方法模式:
对于简单工厂模式而言,创建对象的逻辑判断放在了工厂类中,客户不感知具体的类,但是其违背了开闭原则,如果要增加新的具体类,就必须修改工厂类。
对于工厂方法模式而言,是通过扩展来新增具体类的,符合开闭原则,但是在客户端就必须要感知到具体的工厂类,也就是将判断逻辑由简单工厂的工厂类挪到客户端。
工厂模式横向扩展很方便,假如该工厂又有新的产品 Macbook Air 要生产,那么只需要创建相应的工厂类和产品类去实现抽象工厂接口和抽象产品接口即可,而不用去修改原有已经存在的代码。
抽象工厂模式:
这时候负责该工厂的产品经理说要生产新的一类产品操作系统 Mac Os 和 Windows 8,这时候就引申出了抽象工厂模式。
抽象工厂模式:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
工厂方法模式和抽象工厂模式基本类似,可以这么理解:当工厂只生产一个产品的时候,即为工厂方法模式,而工厂如果生产两个或以上的商品即变为抽象工厂模式。
我们在抽象工厂接口中新增创建系统的方法,并由实例工厂类去实现。
类图可由下表示:
interface Computer { public void printComputer(); } class MacbookProComputer implements Computer { public void printComputer() { System.out.println("This is a macbook pro"); } } class SurfaceBookComputer implements Computer { public void printComputer() { System.out.println("This is a surface book"); } } interface OperatingSystem { public void printSystem(); } class MacOsSystem implements OperatingSystem { public void printSystem() { System.out.println("This is a mac os"); } } class Windows8System implements OperatingSystem { public void printSystem() { System.out.println("This is a window 8"); } } interface ProductionFactory { public Computer createComputer(); public OperatingSystem createSystem(); } class AppleFactory implements ProductionFactory { public Computer createComputer() { return new MacbookProComputer(); } public OperatingSystem createSystem() { return new MacOsSystem(); } } class MsFactory implements ProductionFactory { public Computer createComputer() { return new SurfaceBookComputer(); } public OperatingSystem createSystem() { return new Windows8System(); } } public class Client { public void buy(Computer c){ System.out.println("I buy a computer"); c.printComputer(); } public void use(OperatingSystem s) { System.out.println("Operating System"); s.printSystem(); } public static void main(String[] args) { ProductionFactory pf = new AppleFactory(); Computer c = pf.createComputer(); OperatingSystem s = pf.createSystem(); Client client = new Client(); client.buy(c); client.use(s); } }
抽象工厂模式的缺点在于产品类的扩展,将会是十分费力的,假如在需要加入新的产品,那么几乎所有的工厂类都需要进行修改,所以在使用抽象工厂模式时,对产品等级结构的划分是十分重要的。
适用场景:
无论是简单工厂模式,工厂方法模式还是抽象工厂模式,它们都具有类似的特性,适用场景也十分类似。
无论是简单工厂模式,工厂方法模式,还是抽象工厂模式,他们都属于工厂模式,在形式和特点上也是极为相似的,他们的最终目的都是为了解耦。在使用时,我们不必去在意这个模式到底工厂方法模式还是抽象工厂模式,因为他们之间的演变常常是令人琢磨不透的。经常你会发现,明明使用的工厂方法模式,当新需求来临,稍加修改,加入了一个新方法后,由于类中的产品构成了不同等级结构中的产品族,它就变成抽象工厂模式了;而对于抽象工厂模式,当减少一个方法使的提供的产品不再构成产品族之后,它就演变成了工厂方法模式。
所以,在使用工厂模式时,只需要关心降低耦合度的目的是否达到了。使用工厂方法后,调用端的耦合度大大降低了。并且对于工厂来说,是可以扩展的,以后如果想组装其他的产品,只需要再增加一个工厂类的实现就可以。无论是灵活性还是稳定性都得到了极大的提高。