本文源碼見:https://github.com/get-set/get-designpatterns/tree/master/factory-method
工廠方法模式同簡單工廠模式一樣,也是創建類模式,又叫做虛擬構造(Virtual Constructor)模式或多態工廠(Polymorphic Factory)模式。其用意是定義一個創建產品對象的工廠接口,將實際創建工作推遲到子類中。
上篇說到,簡單工廠模式并未做到完全的“開閉原則”。回顧一下,“開”即對擴展開放,這點是沒錯的,簡單工廠模式的初衷之一就是方便增加“產品類型”的時候;“閉”即對修改關閉,這點其實并未做到,當需要增刪改“產品類型”的時候,工廠類必須要修改,因為工廠類“知道”如何創建所有的產品類型的對象。
那么工廠方法模式就是為了完全滿足“開閉原則”,即在簡單工廠模式的基礎上,做到當增加產品類型的時候,無需改動現有的代碼。繼續用上篇的例子:
例子
仍然是做一個畫圖軟件,可以畫矩形、三角形和圓形等,每一種圖形都用一個類來管理:
Rectangle
Circle
-
Triangle
每個類都有各自的draw()
方法,共同實現Shape
接口。
Shape.java
public interface Shape {
void draw();
}
Rectangle.java
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Draw a rectangle.");
}
}
Triangle.java
public class Triangle implements Shape {
@Override
public void draw() {
System.out.println("Draw a triangle.");
}
}
Circle.java
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("Draw a circle.");
}
}
以上幾個類都沒有變化,有變化的是工廠類,工廠類也采用基于接口的設計,由不同的具體工廠類負責相應對象的創建:
ShapeFactory.java
public interface ShapeFactory {
Shape getShape();
}
RectangleFactory.java
public class RectangleFactory implements ShapeFactory {
public Shape getShape() {
return new Rectangle();
}
}
CircleFactory.java
public class CircleFactory implements ShapeFactory{
public Shape getShape() {
return new Circle();
}
}
Triangle.Factory.java
public class CircleFactory implements ShapeFactory{
public Shape getShape() {
return new Circle();
}
}
那么在需要某個形狀的時候,就通過相應的具體工廠類創建即可:
Client.java
public class Client {
public static void main(String[] args) {
ShapeFactory factory = new CircleFactory();
Shape c = factory.getShape();
c.draw();
}
}
再來看一下類圖:
與簡單工廠模式對比以下:
區別就在于簡單工廠模式下的一個具體的工廠類轉換成了基于接口ShapeFactory
的三個具體工廠類。
好處也是明顯的:當增加一個新的形狀類型的時候,不需要對現有代碼做任何更改,增加一個相應的實現了ShapeFactory
的具體工廠類即可。可見,工廠方法模式能夠完全做到“開閉原則”。
這個例子仍然是不恰當的,因為無論如何這一套模式設計比原始的實現方式有更加復雜了。所以再次贅述一遍注意事項,以上例子是為了說明工廠方法模式,但并不是一個合理的應用。復雜對象適合使用工廠模式,而簡單對象,特別是只需要通過 new 就可以完成創建的對象,無需使用工廠模式。如果使用工廠模式,就需要引入一個工廠類,會增加系統的復雜度。
Java中的應用
下邊看一下合理的應用場景是如何的,在Java中的實際應用的例子(來自《Java與模式》):
** 1. Java聚集中的應用 **
Java聚集是一套設計精良的數據結構實現,主要的Java聚集都實現自java.util.Collection
接口,這個接口的父接口Iterable
規定所有的Java聚集都必須提供一個iterator()
方法,返還一個Iterator
類型的對象:
java.lang.Iterable.java
public interface Iterable<E> {
... ...
Iterator<E> iterator();
... ...
}
ArrayLis是我們常用的一個Collection實現類,其iterator()方法實現如下:
java.util.ArrayList.java
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
... ...
// 實現iterato接口,返回一個Iterator對象
public Iterator<E> iterator() {
return new Itr();
}
private class Itr implements Iterator<E> {
... ...
}
... ...
}
可見,ArrayList
類的iterator()
方法就是一個具體工廠類的工廠方法,而Collection
就是一個抽象工廠。除了ArrayList
還有LinkedList
等等具體實現類。
總結
從上邊的例子可以看到,工廠方法模式其實是將“面向接口”編程的思路應用在了工廠類上,這樣有幾個方便的地方:
- 做到了完全的“開閉原則”,因為增加新的“產品”和相應的“工廠”均不需修改現有代碼;
- 工廠設計模式通常應用在復雜的對象創建場景中,因此面臨多層的繼承關系,比如
ArrayList
實現了List
接口,而后者繼承自Collect
接口,Collect
又繼承自Iterator
接口。有時候具體工廠方法與具體產品是有層次對應關系的,比如:
這種情況也是只有一個工廠類的簡單工廠模式所無法滿足的。