設計模式-工廠方法模式
在簡單工廠模式中,我們發現存在很多問題:
- 由於工廠類集中了所有產品創建邏輯,一旦不能正常工作,整個系統都要受到影響。
- 要新增產品類的時候,就要修改工廠類的代碼,違反了開放封閉原則(對擴展的開放,對修改的關閉)。
- 簡單工廠模式由於使用了靜態工廠方法,造成工廠角色無法形成基於繼承的等級結構。
為了解決上述的問題,我們學習一種新的設計模式:工廠方法模式。
模式定義
定義一個用於創建對象的接口,讓子類決定實例化哪一個類。工廠方法使一個類的實例化延遲到其子類。
設計原則
依賴倒置原則:要依賴抽象,不要依賴具體類。
聽起來像是針對接口編程,不針對實現編程,但是這個原則說明了:不能讓高層組件依賴底層組件,而且,不管高層或底層組件,兩者都應該依賴於抽象。例如,下圖中 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源碼的內容,滿足了開-閉原則。
總結
優點:
- 更符合開-閉原則:新增一種產品時,只需要增加相應的具體產品類和相應的工廠子類即可
- 符合單一職責原則:每個具體工廠類只負責創建對應的產品
- 不使用靜態工廠方法,可以形成基於繼承的等級結構
缺點:
- 添加新產品時,除了增加新產品類外,還要提供與之對應的具體工廠類,系統類的個數將成對增加,在一定程度上增加了系統的復雜度;同時,有更多的類需要編譯和運行,會給系統帶來一些額外的開銷
- 雖然保證了工廠方法內的對修改關閉,但對於使用工廠方法的類,如果要更換另外一種產品,仍然需要修改實例化的具體工廠類
- 一個具體工廠只能創建一種具體產品
設計模式-工廠方法模式