提供一個建立一系列相關或者相互依賴物件的介面,而無需指定它們具體的類。
簡單來說就是
前一篇用開餐廳的例子介紹了Sample Factory Pattern以及Factory Method Pattern。這裡我們用開好的餐廳,來接著介紹Abstract Factory Pattern。
首先我們先來回顧一下Factory Method的範例
interface CookMeal{
void cook();
void delivery();
}
interface MealFactory{
public CookMeal createMeal();
}
class SteakFactory implements MealFactory{
public CookMeal createMeal(){
return new Steak();
}
}
class Steak implements CookMeal{
@Override
public void cook(){
System.out.println("把牛排煮熟");
};
@Override
public void delivery(){
System.out.println("送牛排");
};
}
class Restaurant{
private MealFactory factory;
public Restaurant (MealFactory factory){
this.factory = factory;
}
public CookMeal mealOrder(){
CookMeal meal;
meal = factory.createMeal();
meal.cook();
meal.delivery();
return meal;
}
}
public class MyRestaurant {
public static void main(String args[]) {
Restaurant rSteak = new Restaurant(new SteakFactory());
rSteak.mealOrder();
}
}
output
把牛排煮熟
送牛排
這是我們前一篇使用的範例,很明顯的工廠方法是針對一種物件在做處理。那假如我想要拿到的是一組多個物件呢?也就是說如果要再細分套餐的內容物,那每種原料,都必須建立一個工廠。如台式牛排配台式牛排醬,義式牛排配義式牛排醬。
項目 | 台式 | 義式 |
---|---|---|
牛排 | 台式牛排 | 義式牛排 |
豬排 | 台式豬排 | 義式豬排 |
所以如果是這樣擴充的話,會增加許多的工廠。而抽象工廠模式便可以解決這類的問題。不需要每種產品都開一個工廠,可以開一間台式餐廳,一間義式餐廳。而兩間餐廳上頭的企業就是餐廳的抽象工廠。
註:如對Factory Method不太熟悉,的可以參考 開一間餐廳 - Factory Pattern
老樣子,在使用Prototype Pattern之前,還是得先認識其中的成員
成員 | 功用 |
---|---|
Abstract Factory | 提供建立產品的接口,包含多個建立產品的方法,可建立不同產品。 |
Concrete Factory | 主要是實作抽象工廠中的抽象方法,完成實體產品的建立。 |
Product | 定義產品的規範、特性及功能,可以有多個抽象產品。 |
ConcreteProduct | 實作抽象產品,實體由工廠建立,和實體工廠是多對一的關係。 |
可以看看抽象工廠的UML圖。
由圖上可以了解,實體工廠實作抽象工廠後,實體工廠去取得實體產品,來完成整體流程。
現在來試試抽象工廠的做法。首先先定義原料(Ptoduct)的抽象類別:
interface Ingredient {
public void getSauce();
}
再來建立台式及義式原料的實體(ConcreteProduct)並實作原料的抽象類別。
class TWIngredient implements Ingredient {
public void getSauce() {
System.out.println("淋上台式醬料!");
}
}
class ITIngredient implements Ingredient {
public void getSauce() {
System.out.println("淋上義式醬料");
}
}
接著我們要修改Restaurant(Abstract Factory)的類別,定義方法及流程。我們要出一個餐,需要肉跟原料。所以寫一個mealOrder的方法製作餐點,定義createMeal以及getIngredient取得肉及原料。
abstract class Restaurant{
public CookMeal mealOrder(String type){
CookMeal meal;
Ingredient ingredient;
meal = createMeal(type);
ingredient = getIngredient();
meal.cook();
ingredient.getSauce();
return meal;
}
abstract CookMeal createMeal(String type); // 定義方法
abstract Ingredient getIngredient(); // 定義方法
}
最後將實體工廠(Concrete Factory)建立起來,分別有台式及義式的餐廳。肉類藉由if-else判斷,而原料就看是哪種風格的餐廳,就建立對應的原料實體。
class TWRestaurant extends Restaurant {
@Override
public CookMeal createMeal(String type){ // 實作方法
CookMeal meal = null;
if("Steak".equals(type)) {
meal = new Steak();
} else if ("Pork".equals(type)){
meal = new Pork();
}
return meal;
}
@Override
public Ingredient getIngredient(){ // 實作方法
Ingredient ingredient = new TWIngredient();
return ingredient;
}
}
class ITRestaurant extends Restaurant {
@Override
public CookMeal createMeal(String type){ // 實作方法
CookMeal meal = null;
if("Steak".equals(type)) {
meal = new Steak();
} else if ("Pork".equals(type)){
meal = new Pork();
}
return meal;
}
public Ingredient getIngredient(){ // 實作方法
Ingredient ingredient = new ITIngredient();
return ingredient;
}
}
public class MyRestaurant {
public static void main(String[] args) {
Restaurant rSteak = new TWRestaurant();
rSteak.mealOrder("Steak");
Restaurant rPork = new ITRestaurant();
rPork.mealOrder("Pork");
}
}
output
把牛排煮熟
淋上台式醬料!
把豬排煮熟
淋上義式醬料
可以看到MyRestaurant內,只要選擇哪種風格特餐聽,並將想要的餐點填入,就可以取得對應的餐點。最後來看一下範例的UML圖。
從圖上可以了解,需要什麼食材,由餐廳自己決定。這樣當有第三間是綜合風味餐廳,他就看已將所有的引進所有的食材,提升了整體開發的靈活性。
抽象工廠:利用物件的合成,集結一些相關的產品,而在實體工廠使用工廠方法來實作要產出的產品。
工廠方法:由繼承來的次類別來決定要產生哪種實體產品,其主要目的是將客戶從實體型態中鬆綁。
優點
1. 建立新產品系列時不需要修改原本的程式碼。
缺點
1. 產品系列要增加新產品時,所有的工廠類別都需要修改。
範例1:Factory Method Pattern
範例2:Abstract Factory Pattern
Java設計模式—工廠方法模式&抽象工廠模式
工廠模式 - 抽象工廠模式 (Abstract Factory Pattern)
設計模式-Abstract Factory抽象工廠模式