设计模式的征途—2.简单工厂(Simple Factory)模式
工廠模式是最常用的一種創(chuàng)建型模式,通常所說的工廠模式一般是指工廠方法模式。本篇是是工廠方法模式的“小弟”,我們可以將其理解為工廠方法模式的預(yù)備知識,它不屬于GoF 23種設(shè)計(jì)模式,但在軟件開發(fā)中卻也應(yīng)用地比較頻繁。此外,工廠方法模式還有一位“大哥”—抽象工廠模式,會在后面進(jìn)行介紹。
| 簡單工廠模式(Simple Factory) | 學(xué)習(xí)難度:★★☆☆☆ | 使用頻率:★★★☆☆ |
一、從一個圖表庫談起
M公司想要基于C#語言開發(fā)一套圖表庫,該圖表庫可以為應(yīng)用系統(tǒng)提供各種不同外觀的圖標(biāo),例如柱狀圖、餅狀圖或折線圖等。M公司圖表庫設(shè)計(jì)開發(fā)人員希望為應(yīng)用系統(tǒng)開發(fā)人員提供一套靈活易用的圖表庫,而且可以較為方便地對圖表庫進(jìn)行擴(kuò)展,以便于在將來增加一些新類型的圖表。
M公司的程序員提出了一個初始設(shè)計(jì)方案,將所有圖表的實(shí)現(xiàn)代碼封裝在一個Chart類中,其框架代碼如下所示:
public class Chart{private string type; // 圖表類型public Chart(object[][] data, string type){this.type = type;if (this.type.Equals("histogram", StringComparison.OrdinalIgnoreCase)){// 初始化柱狀圖 }else if (this.type.Equals("pie", StringComparison.OrdinalIgnoreCase)){// 初始化餅狀圖 }else if (this.type.Equals("line", StringComparison.OrdinalIgnoreCase)){// 初始化折線圖 }}public void Display(){if (this.type.Equals("histogram", StringComparison.OrdinalIgnoreCase)){// 顯示柱狀圖 }else if (this.type.Equals("pie", StringComparison.OrdinalIgnoreCase)){// 顯示餅狀圖 }else if (this.type.Equals("line", StringComparison.OrdinalIgnoreCase)){// 顯示折線圖 }}}客戶端代碼通過調(diào)用Chart類的構(gòu)造函數(shù)來創(chuàng)建圖表對象,根據(jù)參數(shù)type的不同可以得到不同類型的圖標(biāo),然后再調(diào)用Display()方法來顯示相應(yīng)的圖表。
但是,不難看出,Chart類是一個巨大的類,存在很多問題:
- 在Chart類中包含很多if-else代碼塊,相當(dāng)冗長,可讀性很差;
- Chart類的職責(zé)過重,負(fù)責(zé)初始化和顯示各種圖表對象,違反了單一職責(zé)原則;
- 當(dāng)需要增加新的圖表類型時,必須修改Chart類的源代碼,違反了開閉原則;
- 客戶端只能通過new關(guān)鍵字來直接創(chuàng)建Chart對象,Chart類與客戶端類耦合度較高,對象的創(chuàng)建和使用無法分離;
- 客戶端在創(chuàng)建Chart對象之前可能還需要進(jìn)行大量初始化設(shè)置,例如設(shè)置柱狀圖的顏色和高度等,如果在Chart類的構(gòu)造函數(shù)中沒有提供一個默認(rèn)設(shè)置,那就只能由客戶端來完成初始設(shè)置,這些代碼在每次創(chuàng)建Chart對象時都會出現(xiàn),導(dǎo)致代碼的重復(fù);
二、簡單工廠模式概述
2.1 要點(diǎn)
簡單工廠模式并不屬于GoF 23種經(jīng)典設(shè)計(jì)模式,但通常將它作為學(xué)習(xí)其他工廠模式的基礎(chǔ)。
簡單工廠(Simple Factory)模式:定義一個工廠類,它可以根據(jù)參數(shù)的不同返回不同類的實(shí)例,被創(chuàng)建的實(shí)例通常都具有共同的父類。因?yàn)樵诤唵喂S模式中用于創(chuàng)建實(shí)例的方法是靜態(tài)(static)方法,因此簡單工廠模式又被稱為靜態(tài)工廠方法模式,它屬于創(chuàng)建型模式。
簡單工廠模式的要點(diǎn)在于:當(dāng)你需要什么,只需要傳入一個正確的參數(shù),就可以獲取你所需的對象,而無須知道其創(chuàng)建細(xì)節(jié)。
2.2 結(jié)構(gòu)圖
簡單工廠模式包含3個角色:
- Factory - 工廠角色:該模式的核心,負(fù)責(zé)實(shí)現(xiàn)創(chuàng)建所有產(chǎn)品實(shí)例的內(nèi)部邏輯,提供一個靜態(tài)的工廠方法GetProduct(),返回抽象產(chǎn)品類型Product的實(shí)例。
- Product - 抽象產(chǎn)品角色:所有產(chǎn)品類的父類,封裝了各種產(chǎn)品對象的共有方法,它的引入將提高系統(tǒng)的靈活性,使得在工廠類中只需要定義一個通用的工廠方法,因?yàn)樗袆?chuàng)建的具體產(chǎn)品對象都是其子類對象。
- ConcreteProduct - 具體產(chǎn)品角色:簡單工廠模式的創(chuàng)建目標(biāo),所有被創(chuàng)建的對象都充當(dāng)這個角色的某個具體類的實(shí)例。
在簡單工廠模式中,客戶端通過工廠類來創(chuàng)建一個產(chǎn)品類的實(shí)例,而無須直接使用new關(guān)鍵字來創(chuàng)建對象。(可以看出,它是工廠模式家族中最簡單的一員)
三、重構(gòu)圖表庫的實(shí)現(xiàn)
3.1 新的結(jié)構(gòu)圖
為了將Chart類的職責(zé)分離,同時將Chart對象的創(chuàng)建和使用分離,M公司開發(fā)人員決定使用簡單工廠模式對圖表庫進(jìn)行重構(gòu),重構(gòu)后的結(jié)構(gòu)圖如下所示:
3.2 新的代碼實(shí)現(xiàn)
(1)抽象產(chǎn)品角色:IChartable接口
public interface IChartable{void Display();}(2)具體產(chǎn)品角色:各種圖表類型
public class HistogramChart : IChartable{public HistogramChart(){Console.WriteLine("創(chuàng)建柱狀圖...");}public void Display(){Console.WriteLine("顯示柱狀圖...");}}public class LineChart : IChartable{public LineChart(){Console.WriteLine("創(chuàng)建折線圖...");}public void Display(){Console.WriteLine("顯示折線圖...");}}public class PieChart : IChartable{public PieChart(){Console.WriteLine("創(chuàng)建餅狀圖...");}public void Display(){Console.WriteLine("顯示餅狀圖...");}}(3)工廠角色:ChartFactory
public class ChartFactory{public static IChartable GetChart(string type){IChartable chart = null;if (type.Equals("histogram", StringComparison.OrdinalIgnoreCase)){chart = new HistogramChart();Console.WriteLine("初始化設(shè)置柱狀圖...");}else if (type.Equals("pie", StringComparison.OrdinalIgnoreCase)){chart = new PieChart();Console.WriteLine("初始化設(shè)置餅狀圖...");}else if (type.Equals("line", StringComparison.OrdinalIgnoreCase)){chart = new PieChart();Console.WriteLine("初始化設(shè)置折線圖...");}return chart;}}(4)客戶端調(diào)用:
public static void Main(){IChartable chart = ChartFactory.GetChart("histogram");if (chart != null){chart.Display();}chart = ChartFactory.GetChart("pie");if (chart != null){chart.Display();}}運(yùn)行結(jié)果如下:
在客戶端代碼中,使用工廠類的靜態(tài)方法來創(chuàng)建具體產(chǎn)品對象,如果需要更換產(chǎn)品,只需要修改靜態(tài)工廠方法中的參數(shù)即可。例如:將柱狀圖改為餅狀圖,只需要將代碼:
IChartable chart = ChartFactory.GetChart("histogram");改為:
IChartable chart = ChartFactory.GetChart("pie");3.3 改進(jìn)的方案
M公司開發(fā)人員發(fā)現(xiàn)在創(chuàng)建具體Chart對象時,每次更換一個Chart對象都需要修改客戶端中靜態(tài)工廠方法的參數(shù),客戶端代碼需要重新編譯,這對于客戶端而言,是違反了開閉原則的。于是,開發(fā)人員希望有一種方法能夠在不修改客戶端代碼地前提下更換具體產(chǎn)品對象。
因此,他們考慮使用配置文件(XML)來實(shí)現(xiàn):
<?xml version="1.0" encoding="utf-8" ?> <configuration><appSettings><add key="charttype" value="histogram"/></appSettings> </configuration>客戶端因此改為:
public static void Main(){string type = AppConfigHelper.GetChartType(); // 讀取配置文件中的charttypeif (string.IsNullOrEmpty(type)){return;}IChartable chart = ChartFactory.GetChart(type);if (chart != null){chart.Display();}}運(yùn)行結(jié)果如下:
四、簡單工廠模式總結(jié)
4.1 主要優(yōu)點(diǎn)
- 實(shí)現(xiàn)了對象創(chuàng)建和使用的分離:客戶端可以免除直接創(chuàng)建產(chǎn)品對象的職責(zé),而僅僅“消費(fèi)”產(chǎn)品。
- 客戶端無須知道所創(chuàng)建的具體產(chǎn)品類的類名,只需要知道具體產(chǎn)品類所對應(yīng)的的參數(shù)即可。
- 通過引入配置文件,可以在不修改任何客戶端代碼地情況下更換和增加新的具體產(chǎn)品類,在一定程度上提高了系統(tǒng)的靈活性。
4.2 主要缺點(diǎn)
- 由于工廠類集中了所有產(chǎn)品的創(chuàng)建邏輯,職責(zé)過重,一旦不能正常工作,整個系統(tǒng)都要受影響。
- 使用簡單工廠模式勢必會增加系統(tǒng)中類的個數(shù)(引入新的工廠類),增加了系統(tǒng)的復(fù)雜度和理解難度。
- 系統(tǒng)擴(kuò)展困難,一旦添加新產(chǎn)品就不得不修改工廠邏輯,在產(chǎn)品類型較多時,有可能會造成工廠邏輯過于復(fù)雜,不利于系統(tǒng)的擴(kuò)展和維護(hù)。
- 簡單工廠模式由于使用了靜態(tài)工廠方法,造成工廠角色無法形成基于繼承的等級結(jié)構(gòu)。
4.3 適用場景
- 工廠類負(fù)責(zé)創(chuàng)建的對象比較少,由于創(chuàng)建的對象較少,不會造成工廠方法中的業(yè)務(wù)邏輯太過復(fù)雜。
- 客戶端只需要知道傳入工廠類的參數(shù),對于如何創(chuàng)建對象并不關(guān)心。
參考資料
? ? ??
劉偉,《設(shè)計(jì)模式的藝術(shù)—軟件開發(fā)人員內(nèi)功修煉之道》
?
作者:周旭龍
出處:http://edisonchou.cnblogs.com
本文版權(quán)歸作者和博客園共有,歡迎轉(zhuǎn)載,但未經(jīng)作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接。
總結(jié)
以上是生活随笔為你收集整理的设计模式的征途—2.简单工厂(Simple Factory)模式的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: STL - Unorderedset -
- 下一篇: wpsofficeapp的excel表格