设计模式-工厂方法模式
在简单工厂模式中,我们发现存在很多问题:
- 由于工厂类集中了所有产品创建逻辑,一旦不能正常工作,整个系统都要受到影响。
- 要新增产品类的时候,就要修改工厂类的代码,违反了开放封闭原则(对扩展的开放,对修改的关闭)。
- 简单工厂模式由于使用了静态工厂方法,造成工厂角色无法形成基于继承的等级结构。
为了解决上述的问题,我们学习一种新的设计模式:工厂方法模式。
模式定义
定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。
设计原则
依赖倒置原则:要依赖抽象,不要依赖具体类。
听起来像是针对接口编程,不针对实现编程,但是这个原则说明了:不能让高层组件依赖底层组件,而且,不管高层或底层组件,两者都应该依赖于抽象。例如,下图中 Pizza 是抽象类,PizzaStore 和 Pizza 子类都依赖于 Pizza 这个抽象类。
模式类图
工厂方法模式实例
问题描述:
每个地区的 PizzaStore 卖的 Pizza 虽然种类相同,但是都有自己的风味。一个客户点了纽约的 cheese 种类的 Pizza 和在芝加哥点的相同种类的 Pizza 是不同的。要求设计出满足条件的 PizzaStore。
问题的解决方案类图
PizzaStore 有 orderPizza() 方法,顾客可以用它来下单。下单之后需要先使用 createPizza() 来制作 Pizza,这里的 createPizza() 就是 factoryMethod(),不同的 PizzaStore 子类实现了不同的 createPizza()。
首先定义产品接口-披萨
package com.wpx.factorymethod; /** * 定义产品接口-披萨 */ public interface Pizza { public void make(); }
再定义具体的产品类-纽约风味奶酪披萨、纽约风味蔬菜披萨、芝加哥风味奶酪披萨、芝加哥风味蔬菜披萨
package com.wpx.factorymethod; /** * 具体的产品类,实现产品接口-纽约风味奶酪披萨 */ public class NYStyleCheesePizza implements Pizza{ @Override public void make() { System.out.println("制作纽约风味奶酪披萨"); } }
package com.wpx.factorymethod; /** * 具体的产品类,实现产品接口-纽约风味蔬菜披萨 */ public class NYStyleVeggiePizza implements Pizza { @Override public void make() { System.out.println("制作纽约风味蔬菜披萨"); } }
package com.wpx.factorymethod; /** * 具体的产品类,实现产品接口-芝加哥风味奶酪披萨 */ public class ChicagoStyleCheesePizza implements Pizza{ @Override public void make() { System.out.println("制作芝加哥风味奶酪披萨"); } }
package com.wpx.factorymethod; /** * 具体的产品类,实现产品接口-芝加哥风味蔬菜披萨 */ public class ChicagoStyleVeggiePizza implements Pizza{ @Override public void make() { System.out.println("制作芝加哥风味蔬菜披萨"); } }
定义工厂接口
package com.wpx.factorymethod; /** * 定义工厂接口 */ public interface PizzaStore { public Pizza orderPizza(String item); }
接着定义两个具体的工厂类,实现工厂接口
package com.wpx.factorymethod; /** * 具体的工厂类,实现工厂接口-纽约工厂 */ public class NYPizzaStore implements PizzaStore { @Override public Pizza orderPizza(String item) { Pizza pizza = null; if (item.equals("乳酪比萨")) { pizza = new NYStyleCheesePizza(); } else if (item.equals("蔬菜披萨")) { pizza = new NYStyleVeggiePizza(); } else { throw new UnsupportedOperationException(); } pizza.make(); return pizza; } }
package com.wpx.factorymethod; /** * 具体的工厂类,实现工厂接口-芝加哥工厂 */ public class ChicagoPizzaStore implements PizzaStore { @Override public Pizza orderPizza(String item) { Pizza pizza = null; if (item.equals("乳酪比萨")) { pizza = new ChicagoStyleCheesePizza(); } else if (item.equals("蔬菜披萨")) { pizza = new ChicagoStyleVeggiePizza(); } else { throw new UnsupportedOperationException(); } pizza.make(); return pizza; } }
最后对工厂方法模式进行测试,先从纽约工厂那里来一份乳酪披萨,再从芝加哥工厂订一份乳酪披萨。
package com.wpx.factorymethod; /** * 测试工厂方法模式 */ public class PizzaDemo { public static void main(String[] args) { PizzaStore nyStore = new NYPizzaStore(); nyStore.orderPizza("乳酪比萨"); PizzaStore chicagoStore = new ChicagoPizzaStore(); chicagoStore.orderPizza("乳酪比萨"); } }
运行结果
制作纽约风味奶酪披萨 制作芝加哥风味奶酪披萨 Process finished with exit code 0
JDK中的工厂方法模式
Collection接口中的一段代码:
Iterator<E> iterator();
继承Collction的List、Set等中有:
Iterator<E> iterator();
接下来再看ArrayList中的代码:
public Iterator<E> iterator() { return new Itr(); } private class Itr implements Iterator<E> { int cursor; // index of next element to return int lastRet = -1; // index of last element returned; -1 if no such int expectedModCount = modCount; public boolean hasNext() { return cursor != size; } @SuppressWarnings("unchecked") public E next() { checkForComodification(); int i = cursor; if (i >= size) throw new NoSuchElementException(); Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) throw new ConcurrentModificationException(); cursor = i + 1; return (E) elementData[lastRet = i]; } public void remove() { if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); try { ArrayList.this.remove(lastRet); cursor = lastRet; lastRet = -1; expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } } final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } }
Itr类实现了Interator接口
public interface Iterator<E> { boolean hasNext(); E next(); void remove(); }
通过查看JDK源码,我们就会明白这里是怎么应用的工厂方法模式了其中Iterator是抽象产品角色,Itr是Iterator下面的一个具体产品角色,List类应该是抽象工厂角色,ArrayList应该是具体工厂角色。如果我们需要在List下自定义一个集合类并给出相应的迭代方式,那么我们只需要添加一个实现List接口的集合类,然后在增加一个迭代类实现Iterator接口就可以了。并不需要去修改JDK源码的内容,满足了开-闭原则。
总结
优点:
- 更符合开-闭原则:新增一种产品时,只需要增加相应的具体产品类和相应的工厂子类即可
- 符合单一职责原则:每个具体工厂类只负责创建对应的产品
- 不使用静态工厂方法,可以形成基于继承的等级结构
缺点:
- 添加新产品时,除了增加新产品类外,还要提供与之对应的具体工厂类,系统类的个数将成对增加,在一定程度上增加了系统的复杂度;同时,有更多的类需要编译和运行,会给系统带来一些额外的开销
- 虽然保证了工厂方法内的对修改关闭,但对于使用工厂方法的类,如果要更换另外一种产品,仍然需要修改实例化的具体工厂类
- 一个具体工厂只能创建一种具体产品
上一篇: 学习这些设计模式,让你写出更优雅的代码
下一篇: 设计模式-简单工厂模式