本文首發于博客園,地址:http://www.cnblogs.com/beniao/archive/2008/08/09/1263318.html?
?
一、模式概述
?????從設計模式的類型上來說,簡單工廠模式是屬于創建型模式,又叫做靜態工廠方法(Static Factory Method)模式,但不屬于23種GOF設計模式之一。簡單工廠模式是由一個工廠對象決定創建出哪一種產品類的實例。簡單工廠模式是工廠模式家族中最簡單實用的模式,可以理解為是不同工廠模式的一個特殊實現,學習了此模式可以為后面的很多中模式打下基礎。那好,我們就來了解下什么是簡單工廠模式?
?
?????我們來分析一個現實生活中的案例,每天早晨起床洗唰后是干什么呢?吃早餐(這里只針對在外吃早餐的上班族)、坐車(我是窮人只有坐公交,當然很多有錢人都自己有車,這里不考慮這些)去公司上班、是這樣的嗎?OK,下面就來分析下吃早餐中的故事,大家先看看下面這個圖:
???????????????? ?????
?????當我們在買早餐的時候,早餐店里都賣得寫什么呢?這點你有注意嗎?眾多食品擺在那里,你只對營業員說你要何種食品,他便會知道給你拿什么樣的食品給你,這說明什么呢?如果用面向對象的思想來理解的話,營業員在這里就充當了一個工廠的角色,他負責根據你的請求返回你需要的食品對象。而這一點正是簡單工廠模式的意圖。
?
二、模式意圖
?????簡單工廠模式根據提供給他的數據,返回幾個可能類中的一個類的實例。
?
三、模式UML圖?????
?????下面是簡單工廠模式的示意性UML圖:
????????????????
?????如上圖,簡單工廠模式UML我畫了兩種,詳細如下:
?????① 只有一個產品對象的簡單工廠模式。?
?????② 帶有一個抽象產品對象的簡單工廠模式。
?
四、模式參與者
??????工廠(Factory)角色:接受客戶端的請求,通過請求負責創建相應的產品對象。
??????抽象產品(AbstractProduct)角色: 是工廠模式所創建對象的父類或是共同擁有的接口。可是抽象類或接口。
??????具體產品(ConcreteProduct)對象:工廠模式所創建的對象都是這個角色的實例。
?
五、模式實現
?????我們通過上面的分析,已經清晰的知道了工廠模式中的各種角色和職責,那工廠模式通過代碼是怎么實現的呢?OK,下面將繼續分析上面的吃早餐中的故事,做一個簡單的示例實現。
?????1、首先我們來看看只有一個產品對象的簡單工廠模式的實現。其實這很好理解,就當店里只賣一種食品,這里以饅頭為例。
?2?///?饅頭
?3?///?</summary>
?4?public?class?SteamedBread
?5?{
?6?????///?<summary>
?7?????///?構造方法
?8?????///?</summary>
?9?????public?SteamedBread()
10?????{?}
11?
12?????///?<summary>
13?????///?銷售價格
14?????///?</summary>
15?????private?double?price=0.5;
16?????public?double?Price
17?????{
18?????????get?{?return?price;?}
19?????????set?{?price?=?value;?}
20?????}
21?}
?????
?????OK,產品對象建立好了,下面就是創建工廠(Factory)對象了。
?2?///?工廠角色
?3?///?</summary>
?4?public?class?Factory
?5?{
?6?????///?<summary>
?7?????///?創建一個饅頭(SteamedBread)對象
?8?????///?</summary>
?9?????///?<returns></returns>
10?????public?static?SteamedBread?CreateInstance()
11?????{
12?????????return?new?SteamedBread();
13?????}
14?}
?
?????此時,客戶端可以這樣來調用:
2?{
3?????public?static?void?Main(string[]?args)
4?????{
5?????????//通過工廠創建一個產品的實例
6?????????SteamedBread?sb?=?Factory.CreateInstance();
7?????????Console.WriteLine("饅頭{0}元一個!",?sb.Price);
8?????}
9?}
?
?????如上就完成了一個簡單工廠模式的簡單實現,一個產品和一個工廠,工廠負責創建這個產品。但是者種實現有一定的缺陷,為每一種產品創建一個靜態方法來完成產品對象的創建,如果有多個產品則需要定義多個靜態方法分別返回不同的對象,用設計原則來說的話這樣的實現不符合依賴倒置原則(DIP)。如果系統里只有一個單獨的產品對象,那么采用這種實現是完全可以的。UML圖如下:
????????????????????????????
?
?????2、帶抽象產品(AbstractProduct)角色的簡單工廠模式實現
?????從上面分析中得知,這種實現得為每一種產品創建一個靜態方法來完成產品對象的創建。雖然帶有簡單工廠的性質,但是又好象不能夠完全體現出簡單工廠模式的意圖。簡單工廠的意圖是:根據提供給他的數據,返回幾個可能類中的一個類的實例。根據意圖出發進行分析,要實現完全滿足簡單工廠模式意圖的程序,也就得根據提供給工廠的數據,讓工廠根據這個數據來進行判斷,然后返回相應的對象。OK,看看是下面這樣的嗎?
??????????????????????????
?????示意性代碼如下:
?2?///?食品接口----扮演抽象產品角色
?3?///?</summary>
?4?public?interface?IFood
?5?{
?6?????///?<summary>
?7?????///?每種食品都有銷售價格,這里應該作為共性提升到父類或是接口來
?8?????///?由于我們只需要得到價格,所以這里就只提供get屬性訪問器
?9?????///?</summary>
10?????double?price{get;}
11?}
12?------------------------------------------------------------------------------------
13?///?<summary>
14?///?饅頭
15?///?</summary>
16?public?class?SteamedBread:IFood
17?{
18?????///?<summary>
19?????///?構造方法
20?????///?</summary>
21?????public?SteamedBread()
22?????{?}
23?
24?????public?double?price
25?????{
26?????????get
27?????????{
28?????????????return??0.5;
29?????????}
30?????}
31?}
32?------------------------------------------------------------------------------------
33?///?<summary>
34?///?包子
35?///?</summary>
36?public?class?SteamedStuffed:IFood
37?{
38?????public?SteamedStuffed()
39?????{?}
40?
41?????///?<summary>
42?????///?銷售價格
43?????///?</summary>
44?????public?double?price
45?????{
46?????????get
47?????????{
48?????????????return??0.6;??//0.6元一個
49?????????}
50?????}
51?}
52?------------------------------------------------------------------------------------
53?///?<summary>
54?///?工廠角色
55?///?</summary>
56?public?class?Factory
57?{
58?????///?<summary>
59?????///?創建一個饅頭(SteamedBread)對象
60?????///?</summary>
61?????///?<returns></returns>
62?????public?static?IFood?CreateInstance(string?key)
63?????{
64?????????if?(key?==?"饅頭")
65?????????{
66?????????????return?new?SteamedBread();
67?????????}
68?????????else
69?????????{
70?????????????return?new?SteamedStuffed();
71?????????}
72?????}
73?}
74?------------------------------------------------------------------------------------
75?public?class?Client
76?{
77?????public?static?void?Main(string[]?args)
78?????{
79?????????//通過工廠創建一個產品的實例
80?????????IFood?food?=?Factory.CreateInstance("饅頭");
81?????????Console.WriteLine("饅頭{0}元一個!",?food.price);
82?
83?????????food?=?Factory.CreateInstance("包子");
84?????????Console.WriteLine("包子{0}元一個!",?food.price);
85?????}
86?}
?
?????此時的設計就已經完全符合簡單工廠模式的意圖了。顧客(Client)對早餐店營業員(Factory)說,我要“饅頭”,于是營業員便根據顧客所提供的數據(饅頭),去眾多食品中找,找到了然后就拿給顧客。
?
?????3、模式的演變實現
?????有些情況下Simple Factory可以由抽象產品角色扮演,一個抽象產品類同時是子類的工廠。也就是說,抽象產品角色扮演兩種角色和職責,出了基本的定義還還兼任工廠角色的職責,負責產品的創建工作。這里我們在上面的例子基礎上適當修改一下OK了,新建立一個抽象類(Evolution):
?2?///?兼任抽象產品角色和工廠角色兩種角色
?3?///?</summary>
?4?public?abstract?class?Evolution
?5?{
?6?????///?<summary>
?7?????///?共性字段
?8?????///?</summary>
?9?????private?double?price;
10?????public?double?Price
11?????{
12?????????get?{?return?price;?}
13?????????set?{?price?=?value;?}
14?????}
15?
16?
17?????public?static?Evolution?CreateInstance(string?key)
18?????{
19?????????if?(key?==?"饅頭")
20?????????{
21?????????????return?new?SteamedBread();
22?????????}
23?????????else
24?????????{
25?????????????return?new?SteamedStuffed();
26?????????}
27?????}
28?}
?
?????那現在,具體的產品對象的設計也應該修改了,把原來實現于IFood接口改為繼承此抽象類,如下:
?2?{
?3?????public?SteamedBread()
?4?????{
?5?????????this.Price?=?0.5;? //在構造方法里初始話屬性的值
?6?????}
?7?}
?8?
?9?public?class?SteamedStuffed?:?Evolution
10?{
11?????public?SteamedStuffed()
12?????{
13?????????this.Price?=?0.6;
14?????}
15?}
?????
?????通過上面的演化,此時客戶端的調用如下:
2?{
3?????public?static?void?Main(string[]?args)
4?????{
5?????????Evolution?el?=?Evolution.CreateInstance("包子");
6?????????Console.WriteLine("包子{0}元一個!",?el.Price);
7?????}
8?}
?
?????UML草圖如下:
????????????????????????????????? ?????
?????在實際的開發中,這種演化是很適用的,可以說是一種編程技巧吧。
?
?????4、模式的其他演變
?????如果系統中只有唯一的一個具體產品對象,那么可以省略抽象產品角色。這一種其實也就是本錢前面的第一種模式實現。
?????如果抽象產品角色省略,那么工廠角色就可以與具體產品角色合并。也就是說一個產品類就是自身的工廠。這樣把原有三個獨立的角色:抽象產品角色、具體產品角色和工廠角色合并為一個,這個類自己負責創建自己的實例。
?
?????注:以上演變可以從上面的程序代碼中直接修改而來,這里我就不貼代碼了。???
?
六、模式優缺點
??????工廠類含有必要的判斷邏輯,可以決定在什么時候創建哪一個產品類的實例,客戶端可以免除直接創建產品對象的責任,而僅僅"消費"產品。簡單工廠模式通過這種做法實現了對責任的分割。
??????當產品有復雜的多層等級結構時,工廠類只有自己,以不變應萬變,就是模式的缺點。因為工廠類集中了所有產品創建邏輯,一旦不能正常工作,整個系統都要受到影響。
??????系統擴展困難,一旦添加新產品就不得不修改工廠邏輯,有可能造成工廠邏輯過于復雜,違背了"開放--封閉"原則(OCP).另外,簡單工廠模式通常使用靜態工廠方法,這使得無法由子類繼承,造成工廠角色無法形成基于繼承的等級結構。
?
七、相關模式
??????工廠方法模式:每個產品由一個專門的工廠來負責創建。是一種只有唯一一個產品的實現,帶有簡單工廠的性質。
??????抽象工廠模式:大致和工廠方法相同。
??????單例模式:單例的實現和上面模式演變中的最后一種很相似,只要把構造器私有便OK。
?
八、參考資料
?????Addison-Wesley,1995,p.185. 中文版:《設計模式:可復用的面向對象軟件的基礎》 李英軍等譯.
?????Alan Sharroway & James r.Trott.中文版:《設計模式精解
?????張逸? 著《軟件設計精要與模式》