简单工厂模式(静态工厂)和工厂方法模式和抽象工厂模式
首先是简单工厂模式。
一句话描述就是用一个工厂类去封装new的工厂,返回新建的一个对象的引用。如果这个方法是静态的,就称之为静态工厂。一般来说这种做法很常见,尤其在一些JAVA工具类中。它的好处和坏处网上的分析都很多,但我觉得没必要分的这么细。最最最直接的好处是用起来简单,方便和不容易出错。
工厂方法模式则复杂一点。它的做法是在父类声明一个final方法用来真正被外部调用(在子类中被继承但是不允许覆盖)。在这个方法中调用一个抽象方法去具体实现新建对象,可是这个抽象方法本身由子类实现。
举个例子就是:
1 //主类 2 public class Main { 3 public static void main(String[] args) { 4 FruitFactory factory = new AppleFactory(); 5 Fruit product = factory.getProduct(); 6 if(product.getClass().equals(Apple.class)) 7 System.out.println("生产了一个苹果"); 8 } 9 } 10 11 //工厂继承体系 12 abstract class FruitFactory { 13 public final Fruit getProduct() { 14 Fruit o = getSpecificProduct(); 15 if(o != null) 16 System.out.println("水果检查合格!"); 17 return o; 18 } 19 protected abstract Fruit getSpecificProduct(); 20 } 21 22 class AppleFactory extends FruitFactory { 23 @Override 24 protected Fruit getSpecificProduct() { 25 return new Apple(); 26 } 27 } 28 29 class BananaFactory extends FruitFactory { 30 @Override 31 protected Fruit getSpecificProduct() { 32 return new Banana(); 33 } 34 } 35 36 //产品继承体系 37 abstract class Fruit { 38 39 } 40 41 class Apple extends Fruit { 42 43 } 44 45 class Banana extends Fruit { 46 47 }
首先,这个地方的方法调用过程是:
苹果工厂对象调用了继承下来的getProduct()方法。而这个方法是final的,换句话说最终会执行其父类FruitFactory的这个方法(写成final就是为了防止子类覆盖)。接着父类的这个方法调用了一个抽象方法,这个抽象方法在子类具体实现。所以最终还是生产了一个Apple。然后会父类方法执行这个继承体系下所有工厂返回的结果都要执行的部分:检查。
看到这的一个疑惑是:所有的东西都在AppleFactory里面实现的,那为什么要先去找父类,再把皮球踢回子类呢?
其实这个例子比较简单,所以看上去没啥必要,可是在实际使用中有两种容易出现的情况:
1,创建实例对象的过程中有很多“机械而重复”的操作。
2,有很多操作必须这个体系下的所有工厂都去做。
其实这是一个问题的不同角度,也就是有的代码需要复用,有的代码需要分用。把那些可以封装的相同代码都写到父类的方法里面(比如检查是否合格),而那些各自不同的底层实现由底层各自实现(比如具体到底是生产什么东西)。
同时介绍一个设计原则:依赖倒置原则:要依赖抽象,不要依赖具体类。
前面我的“产品”的静态类型(这个词的意思是声明的对象类型)都是Fruit,而不是具体的Apple等,哪怕方法返回的明明就是一个Apple。
这是解耦合的重要一步,其实也可以说是面向对象思想中的最重要的一点。如果没有这种向上转型成基础接口(这个地方的接口未必一定是一个Interface)从而使用多态的做法,那么会出现这个问题:所有地方的静态类型和实际类型一致,程序之间一环扣一环,耦合相当大。
那么抽象工厂方法呢?其实就是先定义一个基础接口,然后用不同的工厂实现这个接口。在具体的类需要创建的时候,传入一个向上转型的工厂,通过调用这个工厂的方法实现创建对象。和前面的继承来实现相比,这里通过组合来实现了解耦合。同时,可以在抽象工厂中创建多个需要的对象。这个做法其实很常见,这里算是个总结。