《Head First 设计模式》读书笔记04 工厂模式
问题的提出
如何将实例化具体类的代码从应用中抽离,或者封装起来,使它们不会干扰应用的其他部分?
假设你有一个披萨店,那么代码最初可能是这样:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Pizza oderPizza(String type) { //Pizza是一个接口 Pizza pizza; //随着时间改变的地方 if(type.equals("cheese")) { pizza = new CheesePizza(); } else if(type.equals("greel")) { pizza = new GreekPizza(); } else if(type.equals("pepperoni")) { pizza = new PepperoniPizza(); } //不改变的地方 pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; }
披萨类型可能随着时间各种改变,那么找到了改变的部分和不变的部分之后,使用封装将创建对象的部分移到外面去。
封装创建对象的代码:
新的实现——简单工厂
利用工厂封装后,新的实现代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class SimplePizzaFactory { public Pizza createPizza(String type) { Pizza pizza = null; if(type.equals("cheese")) { pizza = new CheesePizza(); } else if(type.equals("greel")) { pizza = new GreekPizza(); } else if(type.equals("pepperoni")) { pizza = new PepperoniPizza(); } return pizza; } } public class PizzaStore { SimplePizzaFactory factory; public PizzaStore(SimplePizzaFactory factory) { this.factory = factory; } public Pizza orderPizza(String type) { Pizza pizza; pizza = factory.createPizza(type); pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; } }
类图:
重要提醒
在设计模式中,所谓的“实现一个接口”并不一定表示“写一个类,并用implements关键字来实现某个Java接口”。
“实现一个接口”泛指“实现某个超类型(可以是类或接口)的某个方法”。
有更多不同地区的加盟披萨店
一种做法:设计出不同的工厂类
每个地区用自己的工厂类加工披萨,每个地区也有自己的PizzaStore对象,建立对象时将自己的工厂类对象加入。框架不变。
想要更多的质量控制
把createPizza()方法放回到PizzaStore中,设置成抽象方法,然后为每个区域风味创建一个PizzaStore的子类。
这种方法实现起来代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public abstract class PizzaStore { public Pizza orderPizza(String type) { Pizza pizza; pizza = createPizza(type); pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; } protected abstract Pizza createPizza(String type); }
工厂方法
上面的例子中,实例化披萨的责任被移到一个方法中,这个方法就如同是一个“工厂”。
工厂方法用来处理对象的创建,并将这样的行为封装在子类中。
这样,客户程序中关于超类的代码就和子类对象创建代码解耦了。
abstract Product factoryMethod(String type);
工厂方法是抽象的,所以依赖子类来处理对象的创建。
工厂方法必须返回一个产品。超类中定义的方法,通常使用到工厂方法的返回值。
工厂方法将客户(也就是超类中的代码,如orderPizza())和实际创建具体产品的代码分隔开来。
工厂方法可能需要参数(也可能不需要)来指定所需要的产品。
工厂方法模式
工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。
工厂方法让类把实例化推迟到子类。
见类图:
ConcreteCreator可以有一个或多个,选择了子类,则选择了实际创建的工厂方法。
这个具体的工厂可以只创建一种对象,也可以根据传入的参数创建不同的对象。
工厂方法和Creator并不总是抽象的,也可以提供默认的具体实现。
设计原则——依赖倒置原则Dependency Inversion Principle
要依赖抽象,不要依赖具体类。
不能让高层组件依赖底层组件。
前面所说的做法就体现了这个设计原则,倒置依赖后:让高层组件PizzaStore依赖于一个抽象的Pizza,而各种具体的披萨也依赖于这个抽象的Pizza。
几个指导方针帮助你遵循此原则:
变量不可以持有具体类的引用。
如果使用new,就会持有具体类的引用,可以使用工厂来避开这样的做法。
不要让类派生自具体类。
如果派生自具体类,就会依赖具体类。要派生自抽象类或接口。
不要覆盖基类中已实现的方法。
如果覆盖基类已实现的方法,那么基类不是一个真正适合被继承的抽象。基类中已实现的方法应该由所有的子类共享。
这些原则应尽量达到,而不是随时都要遵循,有时候有足够的理由,就可以违反这些方针。(Orz)。