1 概述
創建型模式,提供了一種創建對象的最佳實踐。工廠方法模式的核心思想,是通過統一的工廠類來獲取對象,而不需要調用類的構造方法。
2 優點
- 可以將類的實例化過程延緩到子類。調用者無需知道接口/抽象類的具體實現是什麼,利用工廠方法即可獲取類的實例,降低與調用者的耦合度。
- 隱藏類的構造細節,降低類創建的複雜度,提高程序可讀性。
- 可以根據不同環境/參數,從工廠構造不同的方法。
3 案例
有一個飯店的接口,飯店裏有廚師和服務員。我們定義KFC
和PizzaHut
兩個飯店:
interface Restaurant {
void getCook();
void getWaiter();
}
public class KFC implements Restaurant {
@Override
public void getCook() {
System.out.println("I'm KFC cook.");
}
@Override
public void getWaiter() {
System.out.println("I'm KFC waiter.");
}
}
class PizzaHut implements Restaurant {
@Override
public void getCook() {
System.out.println("I'm PizzaHut cook.");
}
@Override
public void getWaiter() {
System.out.println("I'm PizzaHut waiter.");
}
}
3.1 工廠類型1
再定義一個工廠方法RestaurantFactory
,從工廠中,很容易就能根據類型獲取對應的飯店:
public class Test {
public static void main(String[] args) {
RestaurantFactory factory = new RestaurantFactory();
Restaurant kfc = factory.createRestaurant(RestaurantFactory.RestaurantType.KFC);
Restaurant pizzaHut = factory.createRestaurant(RestaurantFactory.RestaurantType.PizzaHut);
kfc.getCook();
pizzaHut.getWaiter();
}
}
class RestaurantFactory {
enum RestaurantType {
KFC, PizzaHut
}
Restaurant createRestaurant(RestaurantType type) {
switch (type) {
case KFC: return new KFC();
case PizzaHut: return new PizzaHut();
default: System.out.format("Invalid restaurant %s", type); return null;
}
}
}
輸出:
I'm KFC cook.
I'm PizzaHut waiter.
比如Spring
中的BeanFactory使用的就是這種模式:getBean
方法用Bean
的類型/名字作爲參數,返回對應的Bean
。
JDK
中的Calendar
類,也是用的這種模式:
public static Calendar getInstance(TimeZone zone, Locale aLocale) {
...
// 根據參數創建不同的Calendar實例
if (aLocale.hasExtensions()) {
String caltype = aLocale.getUnicodeLocaleType("ca");
if (caltype != null) {
switch (caltype) {
case "buddhist":
cal = new BuddhistCalendar(zone, aLocale);
break;
case "japanese":
cal = new JapaneseImperialCalendar(zone, aLocale);
break;
case "gregory":
cal = new GregorianCalendar(zone, aLocale);
break;
}
}
}
...
}
如果實例類型相對固定,那麼上述模式能很好的滿足需求。但是如果實例類型不確定,那麼每當需要新增類型的時候,都需要改動原先的方法,對開閉原則遵循得不好。於是有了第二種類型。
3.2 工廠類型2
依然是飯店的例子,我們通過如下方式定義工廠:
public class Test {
public static void main(String[] args) {
KFCFactory kfcFactory = new KFCFactory();
PizzaHutFactory pizzaHutFactory = new PizzaHutFactory();
Restaurant kfc = kfcFactory.createRestaurant();
Restaurant pizzaHut = pizzaHutFactory.createRestaurant();
kfc.getCook();
pizzaHut.getWaiter();
}
}
class KFCFactory {
Restaurant createRestaurant() {
return new KFC();
}
}
class PizzaHutFactory {
Restaurant createRestaurant() {
return new PizzaHut();
}
}
輸出:
I'm KFC cook.
I'm PizzaHut waiter.
上述方式,每新增一個類別,只需要新增一個對應的工廠即可,原先的工廠方法以及實例無需做任何修改。如LoggerFactory就是這種類型的工廠模式:
public static Logger getLogger(String name) {
// getLogger最終是委託給ILoggerFactory去做的。
// 新增Logger獲取方式,只需新增ILoggerFactory的實現類,擴展性很強。
ILoggerFactory iLoggerFactory = getILoggerFactory();
return iLoggerFactory.getLogger(name);
}
但是這種方式,對每一種類別,都要有一個工廠類,複雜度相對較高。實際中,還是第一種類型使用比較頻繁。
4 總結
工廠方法模式是使用很廣泛的一種創建型模式,幾乎能在所有的開源框架中見到。很典型的特點就是,工廠類以Factory
字樣結尾:-)。