在简单工厂模式中,我们发现存在很多问题:
- 由于工厂类集中了所有产品创建逻辑,一旦不能正常工作,整个系统都要受到影响。
- 要新增产品类的时候,就要修改工厂类的代码,违反了开放封闭原则(对扩展的开放,对修改的关闭)。
- 简单工厂模式由于使用了静态工厂方法,造成工厂角色无法形成基于继承的等级结构。
为了解决上述的问题,我们学习一种新的设计模式:工厂方法模式。
模式定义
定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。
设计原则
依赖倒置原则:要依赖抽象,不要依赖具体类。
听起来像是针对接口编程,不针对实现编程,但是这个原则说明了:不能让高层组件依赖底层组件,而且,不管高层或底层组件,两者都应该依赖于抽象。例如,下图中 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源码的内容,满足了开-闭原则。
总结
优点:
- 更符合开-闭原则:新增一种产品时,只需要增加相应的具体产品类和相应的工厂子类即可
- 符合单一职责原则:每个具体工厂类只负责创建对应的产品
- 不使用静态工厂方法,可以形成基于继承的等级结构
缺点:
- 添加新产品时,除了增加新产品类外,还要提供与之对应的具体工厂类,系统类的个数将成对增加,在一定程度上增加了系统的复杂度;同时,有更多的类需要编译和运行,会给系统带来一些额外的开销
- 虽然保证了工厂方法内的对修改关闭,但对于使用工厂方法的类,如果要更换另外一种产品,仍然需要修改实例化的具体工厂类
- 一个具体工厂只能创建一种具体产品