Java設計模式——工廠模式
1. 什麼是工廠模式
工廠模式(Factory Pattern)
是 Java 中最常用的設計模式之一,它提供了一種建立物件的最佳方式。
在工廠模式中,我們在建立物件時不會對客戶端暴露建立邏輯,並且是通過使用一個共同的介面來指向新建立的物件。
優點:
-
一個呼叫者想建立一個物件,只要知道其名稱就可以了。
-
擴充套件性高,如果想增加一個產品,只要擴充套件一個工廠類就可以。
-
遮蔽產品的具體實現,呼叫者只關心產品的介面。遮蔽產品的具體實現,呼叫者只關心產品的介面。
缺點:
每次增加一個產品時,都需要增加一個具體類和物件實現工廠,使得系統中類的個數成倍增加,在一定程度上增加了系統的複雜度,同時也增加了系統具體類的依賴。
工廠模式分為三種:簡單工廠模式
、工廠方法模式
和抽象工廠模式
。
這裡以製造coffee的例子開始工廠模式設計之旅。我們知道coffee只是一種泛舉,在點購咖啡時需要指定具體的咖啡種類:美式咖啡、卡布奇諾、拿鐵等等。
abstract class Coffee {
public abstract String getName();
}
class Americano extends Coffee {
@Override
public String getName() {
return "美式咖啡";
}
}
class Cappuccino extends Coffee {
@Override
public String getName() {
return "卡布奇諾";
}
}
class Latte extends Coffee {
@Override
public String getName() {
return "拿鐵";
}
}
2. 簡單工廠模式
簡單工廠實際不能算作一種設計模式,它引入了建立者的概念,將例項化的程式碼從應用程式碼中抽離,在建立者類的靜態方法中只處理建立物件的細節,後續建立的例項如需改變,只需改造建立者類即可,但由於使用靜態方法來獲取物件,使其不能在執行期間通過不同方式去動態改變建立行為,因此存在一定侷限性。
class SimpleFactory {
public static Coffee createInstance(String type) {
if ("americano".equals(type)) {
return new Americano();
} else if ("cappuccino".equals(type)) {
return new Cappuccino();
} else if ("latte".equals(type)) {
return new Latte();
} else {
throw new RuntimeException("type[" + type + "]型別不可識別,沒有匹配到可例項化的物件!");
}
}
}
public class Demo {
public static void main(String[] args) {
Coffee latte = SimpleFactory.createInstance("latte");
System.out.println("建立的咖啡例項為:" + latte.getName());
Coffee cappuccino = SimpleFactory.createInstance("cappuccino");
System.out.println("建立的咖啡例項為:" + cappuccino.getName());
}
}
/* output
建立的咖啡例項為:拿鐵
建立的咖啡例項為:卡布奇諾
*/
3. 工廠方法模式
定義了一個建立物件的介面,但由子類決定要例項化的類是哪一個,工廠方法讓類把例項化推遲到了子類。
場景延伸:不同地區咖啡工廠受制於環境、原料等因素的影響,製造出的咖啡種類有限。中國咖啡工廠僅能製造卡布奇諾、拿鐵,而美國咖啡工廠僅能製造美式咖啡、拿鐵。
abstract class CoffeeFactory {
public abstract Coffee[] createCoffee();
}
// 中國咖啡工廠
class ChinaCoffeeFactory extends CoffeeFactory {
@Override
public Coffee[] createCoffee() {
return new Coffee[]{new Cappuccino(), new Latte()};
}
}
// 美國咖啡工廠
class AmericaCoffeeFactory extends CoffeeFactory {
@Override
public Coffee[] createCoffee() {
return new Coffee[]{new Americano(), new Latte()};
}
}
public class Demo {
public static void main(String[] args) {
CoffeeFactory chinaCoffeeFactory = new ChinaCoffeeFactory();
Coffee[] chinaCoffees = chinaCoffeeFactory.createCoffee();
System.out.println("中國咖啡工廠可以生產的咖啡有:");
print(chinaCoffees);
CoffeeFactory americaCoffeeFactory = new AmericaCoffeeFactory();
Coffee[] americaCoffees = americaCoffeeFactory.createCoffee();
System.out.println("美國咖啡工廠可以生產的咖啡有:");
print(americaCoffees);
}
static void print(Coffee[] c) {
for (Coffee coffee : c) {
System.out.println(coffee.getName());
}
}
}
/* output
中國咖啡工廠可以生產的咖啡有:
卡布奇諾
拿鐵
美國咖啡工廠可以生產的咖啡有:
美式咖啡
拿鐵
*/
4. 抽象工廠模式
提供一個介面,用於建立相關或依賴物件的家族,而不需要明確指定具體類。
在上述的場景上繼續延伸:咖啡工廠做大做強,引入了新的飲品種類:茶、 碳酸飲料。中國工廠只能製造咖啡和茶,美國工廠只能製造咖啡和碳酸飲料。
如果用上述工廠方法方式,除去對應的產品實體類還需要新增2個抽象工廠(茶製造工廠、碳酸飲料製造工廠),4個具體工廠實現。隨著產品的增多,會導致類爆炸。
所以這裡引出一個概念產品家族,在此例子中,不同的飲品就組成我們的飲品家族, 飲品家族開始承擔建立者的責任,負責製造不同的產品。
abstract class Drink {
public abstract String getName();
}
abstract class Coffee extends Drink {}
abstract class Tea extends Drink {}
abstract class Sodas extends Drink {}
class Latte extends Coffee {
@Override
public String getName() {
return "拿鐵";
}
}
class MilkTea extends Tea {
@Override
public String getName() {
return "奶茶";
}
}
class CocaCola extends Sodas {
@Override
public String getName() {
return "可口可樂";
}
}
interface AbstractDrinksFactory {
Coffee createCoffee();
Tea createTea();
Sodas createSodas();
}
class ChinaDrinksFactory implements AbstractDrinksFactory {
@Override
public Coffee createCoffee() {
return new Latte();
}
@Override
public Tea createTea() {
return new MilkTea();
}
@Override
public Sodas createSodas() {
return null;
}
}
class AmericaDrinksFactory implements AbstractDrinksFactory {
@Override
public Coffee createCoffee() {
return new Latte();
}
@Override
public Tea createTea() {
return null;
}
@Override
public Sodas createSodas() {
return new CocaCola();
}
}
public class Demo {
public static void main(String[] args) {
AbstractDrinksFactory chinaDrinksFactory = new ChinaDrinksFactory();
Coffee coffee = chinaDrinksFactory.createCoffee();
Tea tea = chinaDrinksFactory.createTea();
Sodas sodas = chinaDrinksFactory.createSodas();
System.out.println("中國飲品工廠有如下產品:");
print(coffee);
print(tea);
print(sodas);
AbstractDrinksFactory americaDrinksFactory = new AmericaDrinksFactory();
coffee = americaDrinksFactory.createCoffee();
tea = americaDrinksFactory.createTea();
sodas = americaDrinksFactory.createSodas();
System.out.println("美國飲品工廠有如下產品:");
print(coffee);
print(tea);
print(sodas);
}
static void print(Drink drink) {
if (drink == null) {
System.out.println("產品:--");
} else {
System.out.println("產品:" + drink.getName());
}
}
}
/* output
中國飲品工廠有如下產品:
產品:拿鐵
產品:奶茶
產品:--
美國飲品工廠有如下產品:
產品:拿鐵
產品:--
產品:可口可樂
*/
5. 模式對比
如果產品單一,最合適用工廠模式,但是如果有多個業務品種、業務分類時,通過抽象工廠模式產生需要的物件是一種非常好的解決方式。
工廠方法模式 | 抽象工廠模式 |
---|---|
針對的是一個產品等級結構 | 針對的是面向多個產品等級結構 |
一個抽象產品類 | 多個抽象產品類 |
可以派生出多個具體產品類 | 每個抽象產品類可以派生出多個具體產品類 |
一個抽象工廠類,可以派生出多個具體工廠類 | 一個抽象工廠類,可以派生出多個具體工廠類 |
每個具體工廠類只能建立一個具體產品類的例項 | 每個具體工廠類可以建立多個具體產品類的例項 |
參考資料: