在策略模式曾提到过,我们需要面向接口编程,而不是具体类。但是无法避免的是,我们需要使用new方法来创建对象,这时我们不得不使用具体类。例如:
1 | if(picnic) { |
这样一旦有变化或扩展,我们需要修改所有这些引用了具体类new对象的代码,造成系统难以维护和修改。代码无法对修改关闭。
同样的,我们应该分离变化的部分。在这里new对象部分是可能发生变化的部分,例如新增具体累,删除具体类,或者构造函数变化等。
简单工厂模式
最直接的方法就是将创建对象移到一个专职创建披萨的对象中,称这个对象为工厂。工厂(factory)处理创建对象的细节,这个如果客户需要使用产品,只需要通过工厂来获得产品直接使用,不需要自己使用具体类来进行创建。创建代码集中到一个工厂类中,也方便集中同一管理。
1 | public class SimplePizzaFactory { |
1 | public class PizzaStore { |
定义简单工厂
上面用到的是简单工厂,其实并不是一个设计模式。因为其只是将问题搬到了另一个对象,当需要新增实现类时,仍然需要修改这个工厂类。但是由于其简单性,在变化有限的情况下仍是很好的选择。
工厂方法模式
在上面的简单工厂模式中,但需要新增产品类时,我们任然需要修改简单工厂类,对修改没有关闭。这里介绍的工厂方法模式可以帮助我们满足开闭原则。
对于工厂方法模式,所有工厂模式都用来封装对象的创建。工厂方法模式(Factory Method Pattern)让子类决定该创建的对象是什么,来达到将对象创建的过程封装的目的。
另一种理解方式是,平行的类层级。
定义工厂方法模式
工厂方法模式定义了一个创建对象的接口,但由其子类决定具体实例化的是哪一个产品。工厂方法让类把实例化推迟到子类。我们选择那个子类工厂,也就是选择了需要获得产品类。
这样当我们需要新增产品时,只需要实现这个产品类(也可是多个产品类,但是同属于一个基类,比如都是pizza),然后实现生产这个产品的工厂类,继承自工厂方法模式。就可以通过这个工厂获得这个新产品,无需修改抽象工厂类和任何其他类的代码。
定义工厂方法:
1 | public abstract class PizzaStore { |
1 | public abstract class Pizza { |
1 | public class NYPizzaStore extends PizzaStore { |
1 | public class ChicagoStylePepperoniPizza extends Pizza { |
1 | public class NYStylePepperoniPizza extends Pizza { |
另外可以对比一下,不使用工厂方法的实现,尽管代码上可能没有上面的抽象复杂,但是其在系统变化是非常难以维护。
1 | public class DependentPizzaStore { |
审视对象依赖
当我们直接实例化一个类是,就是在依赖它的具体类。如果把上面的代码依赖画成一张图:
设计原则:
要依赖抽象,不要依赖具体类。
在应用工厂方法后,类图会变成如下。
抽象工厂模式
抽象工厂本质上就是一中工厂方法模式的加强版。在工厂方法中,一个工厂方法抽象了一种基类型产品的建立,并交给具体子类负责创建产品。也就是一个工厂方法负责一类基类型产品创建。在有的时候,会有一系列基类型产品同属于一个产品家族,并且最好由一个具体工厂来创建。抽象工厂模式就提供了这种解决方案。
抽象工厂模式¥提供了一个接口(设计模式中接口即可以指实际的接口,也可指抽象类),用于创建相关或依赖对象的家族,而不需要明确指定具体类。
也就是抽象工厂申明了几个工厂方法,各个工厂方法创建的类型属于同一个产品族。
定义抽象工厂
1 | public interface PizzaIngredientFactory { |
1 | public class NYPizzaIngredientFactory implements PizzaIngredientFactory { |
1 | public abstract class Pizza { |
1 | public class CheesePizza extends Pizza { |
1 | public class NYPizzaStore extends PizzaStore { |