Java设计模式圣经连载(02)-工厂方法(Factory Method)模式
工厂方法模式
概念
工厂方法是简单工厂的进一步的延伸,这样说是因为简单工厂违反了开放——封闭的原则,而此时工厂方法却可以完美的解决这个问题!
工厂方法模式是类的创建模式,又叫虚拟构造子(Virtual Constructor)模式或者多态性工厂(Polymorphic Factory)模式。
工厂方法模式的用意是定义一个创建产品对象的工厂接口,将实际工作推迟到子类中。
简单类图
工厂方法模式是简单工厂模式的进一步抽象和推广。由于使用了多态性,工厂方法模式保持了简单工厂模式的优点,而且克服了它的缺点。
在工厂方法模式中,核心的工厂类不再负责所有产品的创建,而是将具体创建的工作交给子类去做。这个核心工厂则变为抽象工厂角色,仅负责给出具工厂子类必须实现的接口,而不接触哪一产品创建的细节。
这种抽象的结果,使这种工厂方法模式可以用来允许系统不修改具体工厂角色的情况下引进新产品,这一特点无疑使得工厂模式具有超过简单工厂模式的优越性。
工厂方法模式的结构
在工厂方法模式中,一般都有一个平行的等级结构,也就是说工厂和产品是对应的的。抽象工厂对应抽象产品,具体工厂对应具体产品。如下:
从上图可以看出,工厂方法模式的系统涉及到了以下角色:
抽象工厂角色:与应用程序无关,任何在模式中创建对象的工厂必须实现这个接口。
具体工厂角色:实现了抽象工厂接口的具体Java类,含有与引用密切相关的逻辑,并且受到应用程序的调用以创建产品对象。
抽象产品角色:工厂方法所创建产品对象的超类型,也就是产品对象的共同父类或共同拥有的接口。
具体产品角色:这个角色实现了抽象产品角色所声名的接口。工厂方法所创建的每个具体产品对象都是某个具体产品角色的实例。
例子
在简单工厂模式中,有个全能的园丁,控制所有作物的种植、生长和收获。现在农场规模变大了,管理更加专业化了。过去全能的园丁没有了,每一种作物都有专门的园丁管理,形成了规模化和专业化生产。
// 水果产品接口Fruit.java public interface Fruit { /** * 种植 */ void plant(); /** * 生长 */ void grow(); /** * 收获 */ void harvest(); }
// 具体产品苹果Apple.java public class Apple implements Fruit { private int treeAge; /** * 种植 */ public void plant() { System.out.println("Apple has been planted."); } /** * 生长 */ public void grow() { System.out.println("Apple is growing..."); } /** * 收获 */ public void harvest() { System.out.println("Apple has been harvested."); } /** * @return 返回树龄 */ public int getTreeAge() { return treeAge; } /** * 设置树龄 */ public void setTreeAge(int treeAge) { this.treeAge = treeAge; } }
// 具体产品葡萄:Grape.java public class Grape implements Fruit { private boolean seedless; //是否有籽 /** * 种植 */ public void plant() { System.out.println("Grape has been planted."); } /** * 生长 */ public void grow() { System.out.println("Grape is growing..."); } /** * 收获 */ public void harvest() { System.out.println("Grape has been harvested."); } /** * @return 是否有籽 */ public boolean getSeedless() { return seedless; } /** * 有无籽的赋值方法 */ public void setSeedless(boolean seedless) { this.seedless = seedless; } /** * 辅助方法 */ public static void log(String msg) { System.out.println(msg); } }
// 具体产品草莓:Strawberry.java public class Strawberry implements Fruit { /** * 生长 */ public void grow() { System.out.println("Strawberry is growing..."); } /** * 收获 */ public void harvest() { System.out.println("Strawberry has been harvested."); } /** * 种植 */ public void plant() { System.out.println("Strawberry has been planted."); } /** * 辅助方法 */ public static void log(String msg) { System.out.println(msg); } }
// 水果工厂接口:FruitGardener.java public interface FruitGardener { /** * 工厂方法 * * @return 水果 */ public Fruit factory(); }
// 苹果工厂:AppleGardener.java public class AppleGardener implements FruitGardener { /** * 工厂方法 * * @return 苹果 */ public Fruit factory() { Fruit f = new Apple(); System.out.println("水果工厂(AppletGardener)成功创建一个水果:苹果!"); return f; } }
// 葡萄工厂:GrapeGardener.java public class GrapeGardener implements FruitGardener { /** * 工厂方法 * * @return 葡萄 */ public Fruit factory() { Fruit f = new Grape(); System.out.println("水果工厂(GrapeGardener)成功创建一个水果:葡萄!"); return f; } }
// 草莓工厂:StrawberryGardener.java public class StrawberryGardener implements FruitGardener { /** * 工厂方法 * * @return 草莓 */ public Fruit factory() { Fruit f = new Strawberry(); System.out.println("水果工厂(StrawberryGardener)成功创建一个水果:草莓!"); return f; } }
// 测试类(客户端):TestApp.java public class TestApp { private FruitGardener f1, f2, f3; private Fruit p1, p2, p3; private void test() { //实力化水果工厂 f1 = new AppleGardener(); f2 = new GrapeGardener(); f3 = new StrawberryGardener(); //从水果工厂生产水果 p1 = f1.factory(); p2 = f2.factory(); p3 = f3.factory(); } public static void main(String args[]) { TestApp test = new TestApp(); test.test(); } }
// 测试运行结果 水果工厂(AppletGardener)成功创建一个水果:苹果! 水果工厂(GrapeGardener)成功创建一个水果:葡萄! 水果工厂(StrawberryGardener)成功创建一个水果:草莓! Process finished with exit code 0
工厂方法模式的优缺点
优点
(1)工厂方法用来创建客户所需要的产品,同时隐藏了哪种具体产品类将被实例化的细节,用户只需要要关注工厂,不需要关注创建的细节!从客户端代码就可以看出!只知道对应的工厂就好!
(2)在增加修改新的运算类的时候不用修改代码,只需要增加对应的工厂就好,完全符合开放——封闭性原则!
(3)创建对象的细节完全封装在具体的工厂内部,而且有了抽象的工厂类,所有的具体工厂都继承了自己的父类!完美的体现了多态性!
缺点
(1)在增加新的产品(对应UML图的算法)时,也必须增加新的工厂类,会带来额外的开销
(2)抽象层的加入使得理解程度加大