詳解JAVA面向物件的設計模式 (三)、工廠模式
工廠模式 Factory
介紹
工廠模式(Factory Pattern)是 Java 中最常用的設計模式之一。這種型別的設計模式屬於建立型模式,它提供了一種建立物件的最佳方式。
在工廠模式中,我們在建立物件時不會對客戶端暴露建立邏輯,並且是通過使用一個共同的介面來指向新建立的物件。
前言
工廠模式主要分為三種形式
-
簡單工廠模式(不屬於GoF的23種設計模式中)
-
工廠方法模式
-
抽象工廠模式
簡單來說,本質上工廠模式是對建立物件過程的一種封裝。通過封裝,讓客戶端無需在意物件建立的過程和邏輯。看看下面的例子。
本文部分解釋摘錄自 http://c.biancheng.net/view/1351.html
簡單工廠模式
比如我們去一家餐廳。我們想要點餐吃飯。
那麼可以看作是客戶得到了飲料,食物,餐具,然後一一使用。
不使用工廠模式時是這樣的
public static void main(String[] args) {
// 普通建立物件
Hamburger hamburger = new Hamburger();
Steak steak = new Steak();
hamburger.eat();
steak.eat();
}
然而這樣的建立物件,語義上不清晰且需要在客戶端中初始化物件。
畢竟給我們製作食物的是餐廳,所以我們可以把餐廳理解成一種工廠。我們客戶向餐廳點單就行了。
在例子中實現
首先,餐廳生產的食品Food,都可以食用,建立介面eat。
public interface Food {
void eat();
}
那麼使用漢堡和牛扒就應該如下實現
public class Hamburger implements Food { @Override public void eat() { System.out.println("吃漢堡,嘎嘎嘎"); } } public class Steak implements Food { @Override public void eat() { System.out.println("吃牛扒,嘎嘎嘎"); } }
那麼餐廳,就可以作為生產這些產品的工廠
public class RestaurantFactory {
public Food getFood(String type) {
if (type == null)
return null;
else if (type.equalsIgnoreCase("hamburger"))
return new Cock();
else if (type.equalsIgnoreCase("steak"))
return new Hamburger();
return null;
}
}
有了餐廳工廠以後,客戶下單就不需要自己去new食物了。直接問餐廳拿!
public static void main(String[] args) {
// 普通建立物件
Hamburger hamburger = new Hamburger();
Steak steak = new Steak();
hamburger.eat();
steak.eat();
// 利用簡單工廠建立物件
RestaurantFactory rpf = new RestaurantFactory();
Food hamburger = rpf.getFood("hamburger");
Food steak = rpf.getFood("steak");
hamburger.eat();
steak.eat();
}
上面是普通建立,下面是簡單工廠建立,最後會輸出2次
吃漢堡,嘎嘎嘎
吃牛排,嘎嘎嘎
上面我們可以看到,我們把建立物件都寫進了一個方法裡。其實我們也可以分開寫,一個方法獲得一種物件也可以,看具體需求。
public class RestaurantFactory {
public Food getHamburger() {
return new Hamburger();
}
public Food getSteak() {
return new Steak();
}
}
靜態工廠
靜態工廠只是在普通工廠上做一些小小的改變,意味著,工廠裡的建立物件方法改寫為靜態方法,如此以來,就不需要new Factory 再操作工廠生成物件了。
public class RestaurantFactory {
public static Food getHamburger() {
return new Hamburger();
}
public static Food getSteak() {
return new Steak();
}
}
缺點
每次需要建立新的對線的時候,就得去修改工廠類,這明顯違背了面向物件設計中的“開閉原則”。
工廠方法模式
工廠方法在簡單工廠上做了抽象處理。
把具體工廠抽象出了一個超級工廠,所有的具體工廠都實現這個抽象超級工廠。客戶端通過訪問具體工廠的工廠方法來建立產品。
結構
工廠方法模式的主要角色如下。
- 抽象工廠(Abstract Factory):提供了建立產品的介面,呼叫者通過它訪問具體工廠的工廠方法 newProduct() 來建立產品。
- 具體工廠(ConcreteFactory):主要是實現抽象工廠中的抽象方法,完成具體產品的建立。
- 抽象產品(Product):定義了產品的規範,描述了產品的主要特性和功能。
- 具體產品(ConcreteProduct):實現了抽象產品角色所定義的介面,由具體工廠來建立,它同具體工廠之間一一對應。
在例子中實現
// 抽象工廠
public abstract class FoodAbstractFactory {
public abstract Food getFood();
}
// 具體工廠 麥當勞, Hamburger實現Food介面
public class McDonaldFactory extend FoodAbstractFactoty {
@Override
public Food getFood() {
return new Hamburger();
}
}
// 具體工廠 咖啡廳, Steak實現Food介面
public class CafeFactory extend FoodAbstractFactoty {
@Override
public Food getFood() {
return new Steak();
}
}
// Main函式
public static void main(String[] args) {
// 工廠方法建立物件
FoodAbstractFactory mcDonald = new McDonaldFactory();
FoodAbstractFactory cafe = new CafeFactory();
Food hamburger = mcDonald.getFood();
Food steak = cafe.getFood();
hamburger.eat();
steak.eat();
}
適用場景
工廠方法模式通常適用於以下場景。
- 客戶只知道建立產品的工廠名,而不知道具體的產品名。如 TCL 電視工廠、海信電視工廠等。
- 建立物件的任務由多個具體子工廠中的某一個完成,而抽象工廠只提供建立產品的介面。
- 客戶不關心建立產品的細節,只關心產品的品牌。
優缺點
工廠方法模式的主要優點有:
- 使用者只需要知道具體工廠的名稱就可得到所要的產品,無須知道產品的具體建立過程;
- 在系統增加新的產品時只需要新增具體產品類和對應的具體工廠類,無須對原工廠進行任何修改,滿足開閉原則;
其缺點是:
- 每增加一個產品就要增加一個具體產品類和一個對應的具體工廠類,這增加了系統的複雜度。
模式的擴充套件
當需要生成的產品不多且不會增加,一個具體工廠類就可以完成任務時,可刪除抽象工廠類。這時工廠方法模式將退化到簡單工廠模式
抽象工廠模式
介紹
前面介紹的工廠方法模式中考慮的是一類產品的生產,如畜牧場只養動物、電視機廠只生產電視機、計算機軟體學院只培養計算機軟體專業的學生等。
同種類稱為同等級,也就是說:工廠方法模式只考慮生產同等級的產品,但是在現實生活中許多工廠是綜合型的工廠,能生產多等級(種類) 的產品,如農場裡既養動物又種植物,電器廠既生產電視機又生產洗衣機或空調,大學既有軟體專業又有生物專業等。
本節要介紹的抽象工廠模式將考慮多等級產品的生產,將同一個具體工廠所生產的位於不同等級的一組產品稱為一個產品族
舉例解釋
抽象工廠的意義是確實比較抽象。。。它主要用於建立 物件家族 。下面舉例說明,更好理解它主要解決的問題。
比如我現在手機上安裝了一款App,我想在這個App上實現換膚功能。(包括配色和ui風格)
那麼,我們就可以理解為,換膚功能就是一個抽象工廠,而功能中提供的每一種面板,就是一個具體工廠類。
每當我們修改了面板主題,一般都會需要我們重啟App,於是App會根據已選的面板主題,找到對應的具體面板主題工廠,進行UI初始化,包括ui配色,button風格,slider特效等等......
在以上的換膚過程裡,我們可以發現,面板主題工廠最終初始化出來的物件們都是不同類的,比如 Color,Button,Slider,Bar,Table。那麼以上這些類例項就可以看作是主題工廠產生的物件家族。
在例子中實現
上面的解釋如果不太明白,就直接看程式碼吧。還是餐廳例子。餐廳除了食物,還會生產飲品,餐具等等。也就是說,食物,飲品和餐具就是餐廳抽象工廠定義的物件家族。看程式碼
// 抽象餐廳工廠
public abstract class RestaurantFactory {
public abstract Food getFood();
public abstract Drinks getDrinks();
public abstract Item getItem();
}
// 具體工廠 麥當勞, Hamburger實現Food介面,Cock實現Drinks,Tissue實現Item
public class McDonaldFactory extend FoodAbstractFactoty {
@Override
public Food getFood() {
return new Hamburger();
}
@Override
public Drinks getDrinks() {
return new Cock();
}
@Override
public Item getItem() {
return new Tissue();
}
}
// 具體工廠 咖啡廳, Steak實現Food介面,Coffee實現Drinks,Knife實現Item
public class CafeFactory extend FoodAbstractFactoty {
@Override
public Food getFood() {
return new Steak();
}
@Override
public Drinks getDrinks() {
return new Coffee();
}
@Override
public Item getItem() {
return new Knife();
}
}
// Main函式
public static void main(String[] args) {
// 抽象工廠建立物件
FoodAbstractFactory mcDonald = new McDonaldFactory();
FoodAbstractFactory cafe = new CafeFactory();
Food hamburger = mcDonald.getFood();
Drinks cock = mcDonald.getDrinks();
Item tissue = mcDonald.getItem();
Food steak = cafe.getFood();
Drinks coffee = cafe.getDrinks();
Item knife = cafe.getItem();
hamburger.eat();
steak.eat();
cock.drink();
coffee.drink();
tissue.use();
knift.use();
}
模式的應用場景
抽象工廠模式最早的應用是用於建立屬於不同作業系統的視窗構件。如 java 的 AWT 中的 Button 和 Text 等構件在 Windows 和 UNIX 中的本地實現是不同的。
抽象工廠模式通常適用於以下場景:
- 當需要建立的物件是一系列相互關聯或相互依賴的產品族時,如電器工廠中的電視機、洗衣機、空調等。
- 系統中有多個產品族,但每次只使用其中的某一族產品。如有人只喜歡穿某一個品牌的衣服和鞋。
- 系統中提供了產品的類庫,且所有產品的介面相同,客戶端不依賴產品例項的建立細節和內部結構。
模式的擴充套件
抽象工廠模式的擴充套件有一定的“開閉原則”傾斜性:
- 當增加一個新的產品族時只需增加一個新的具體工廠,不需要修改原始碼,滿足開閉原則。
- 當產品族中需要增加一個新種類的產品時,則所有的工廠類都需要進行修改,不滿足開閉原則。
另一方面,當系統中只存在一個等級結構的產品時,抽象工廠模式將退化到工廠方法模式。