一起学习设计模式--02.简单工厂模式
工廠模式是最常用的一類創(chuàng)建型設計模式。我們所說的工廠模式是指工廠方法模式,它也是使用頻率最高的工廠模式。
簡單工廠模式是工廠方法模式的小弟,它不屬于GoF 23種設計模式,但是在軟件開發(fā)中應用也頗為頻繁,通常將它作為學習其它工廠模式的入門。
一、圖表庫的設計
A科技公司計劃使用C#語言開發(fā)一套圖表庫,該圖表庫可以為應用系統(tǒng)提供各種不同外觀的圖表,比如柱狀圖、餅狀圖、折線圖等。A科技公司的圖表庫設計人員希望為應用系統(tǒng)開發(fā)人員提供一套靈活易用的圖表庫,而且可以比較方便的對圖表庫進行擴展,以便能夠在將來增加一些新類型的圖表。A科技公司的研發(fā)人員提出了一個初始化設計方案,將所有圖表的實現(xiàn)代碼封裝在一個 Chart 類中,代碼如下:
????public?class?Chart{private?string?type;?//圖表類型public?Chart(object[][]?data,?string?type){this.type?=?type;if?(type.Equals("histogram",?StringComparison.OrdinalIgnoreCase)){//初始化柱狀圖}else?if?(type.Equals("pie",?StringComparison.OrdinalIgnoreCase)){//初始化餅狀圖}else?if?(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)){//顯示折線圖}}}客戶端代碼通過調用 Chart 類的構造函數(shù)來創(chuàng)建圖表對象,根據參數(shù) type 的不同可以得到不同類型的圖表,然后再調用 Display() 方法來顯示相應的圖表。
但是 Chart 類是一個巨大的類,在該類的設計中存在以下幾個問題:
類中包含大量的 “if - else” 代碼塊,整個類的代碼相當冗長,代碼越長,代碼的可讀性、維護難度、測試難度也越大。而且大量的判斷會影響性能,無論是什么類型的圖表,類的內部都需要做大量的判斷。
Chart 類的職責過重,違反了單一職責原則。它將圖表的創(chuàng)建和顯示都放在一個類中,不利于類的重用和維護。而且類的構造函數(shù)中有大量的判斷,并且對象初始化的操作也都寫在構造函數(shù)中,降低了創(chuàng)建效率。
如果需要增加新的圖表,就需要修改 Chart 類,違反了開閉原則。
客戶端只能通過 new 來實例化 Chart 對象,這樣的話 Chart 對象和客戶端的耦合度較高,對象的創(chuàng)建和使用無法分離。
客戶端在創(chuàng)建 Chart 對象之前可能還需要進行大量的初始化設計,比如柱狀圖的顏色、高度等。如果在 Chart 類的構造函數(shù)中沒有提供一個默認設置,那就只能由客戶端來完成初始設置,那么這些初始設置在每次創(chuàng)建 Chart 對象的時候都會出現(xiàn),導致了代碼的重復。
1.簡單工廠模式的流程
首先簡單工廠模式不屬于GoF 23種經典設計模式,但通常將它作為學習其它工廠模式的基礎,它的設計思想很簡單,基本流程如下:
將需要創(chuàng)建各種不同對象的相關代碼封裝到不同的類中,這些類稱為具體的產品類。
將他們公共的代碼進行抽象和提取后封裝在一個抽象產品類中,每一個具體的產品類都是這個抽象產品類的子類。
提供一個工廠類用于創(chuàng)建各種產品,在工廠類中提取一個創(chuàng)建產品的工廠方法,該方法可以根據傳入的參數(shù)不同創(chuàng)建不同的具體產品對象。
客戶端只需要調用工廠類的工廠方法并傳入相應的參數(shù)即可得到一個具體的產品對象。
2.簡單工廠模式的定義
簡單工廠模式定義如下:
簡單工廠模式(Simple Factory Pattern):定義一個工廠類,它可以根據參數(shù)的不同返回不同類的實例,被創(chuàng)建的實例通常都具有共同的父類。因為在簡單工廠模式中用于創(chuàng)建實例的方法是靜態(tài)方法,因此簡單工廠模式又被稱為靜態(tài)工廠方法(Static Factory Method)模式,它屬于類創(chuàng)建型模式。
3.簡單工廠模式的要點
簡單工廠模式的要點在于:當你需要什么,只需要傳入一個正確的參數(shù),就可以獲取你所需要的對象,而無需知道其創(chuàng)建細節(jié)。
4.簡單工廠模式的結構
在簡單工廠模式結構圖中包含3個角色:
Factory(工廠角色):即工廠類,它是簡單工廠模式的核心,負責實現(xiàn)創(chuàng)建所有產品實例的內部邏輯。工廠類可以被外界直接調用,創(chuàng)建所需的產品對象。在工廠類中提供了靜態(tài)的工廠方法FactoryMethod(),返回抽象產品類型 Product。
Product(抽象產品角色):它是工廠類創(chuàng)建所有對象的父類,封裝了各種產品對象的公共方法。抽象產品的引入將提高系統(tǒng)的靈活性,使得在工廠類中只需定義一個通用的工廠方法,因為所有創(chuàng)建的具體產品對象都是其子類對象。
ConceteProduct(具體產品角色):它是簡單工廠模式的創(chuàng)建目標,所有被創(chuàng)建的對象都充當這個角色的某個具體類的實例。每個具體產品角色都繼承了抽象產品角色,需要實現(xiàn)抽象產品中聲明的抽象方法。
在簡單工廠模式中,客戶端通過工廠類來創(chuàng)建一個產品類的實例,而無須使用 new 關鍵字來創(chuàng)建對象,它是工廠模式中最簡單的一員。
在使用簡單工廠模式時,首先需要對產品類進行重構,將所有產品類中公共的代碼轉移到抽象產品類中,并在抽象產品類中聲明一些抽象方法,以供不同的具體產品類來實現(xiàn)。
三、完整解決方案
為了將 Chart 類的職責分離,同時將 Chart 對象的創(chuàng)建和使用分離,A科技公司開發(fā)人員決定使用簡單工廠模式對圖表庫進行重構,重構后的圖表庫結構如下:
IChart 接口充當抽象產品類,其子類HistogramChart、LineChart、PieChart充當具體產品類,ChartFactory充當工廠類。完整代碼如下:
????///?<summary>///?抽象圖表接口:抽象產品類///?</summary>public?interface?IChart{void?Display();}///?<summary>///?柱狀圖類:具體產品類///?</summary>public?class?HistogramChart?:?IChart{public?HistogramChart(){Console.WriteLine("創(chuàng)建柱狀圖!");}public?void?Display(){Console.WriteLine("顯示柱狀圖!");}}///?<summary>///?折線圖類:具體產品類///?</summary>public?class?LineChart?:?IChart{public?LineChart(){Console.WriteLine("創(chuàng)建折線圖!");}public?void?Display(){Console.WriteLine("顯示折線圖!");}}///?<summary>///?餅狀圖類:具體產品類///?</summary>public?class?PieChart?:?IChart{public?PieChart(){Console.WriteLine("創(chuàng)建餅狀圖!");}public?void?Display(){Console.WriteLine("顯示餅狀圖!");}}///?<summary>///?圖表工廠類:工廠類///?</summary>public?class?ChartFactory{///?<summary>///?靜態(tài)工廠方法///?</summary>///?<param?name="type">圖表類型</param>///?<returns></returns>public?static?IChart?GetChart(string?type){IChart?chart?=?null;if?(type.Equals("histogram",?StringComparison.OrdinalIgnoreCase)){chart?=?new?HistogramChart();Console.WriteLine("初始化設置柱狀圖");}else?if?(type.Equals("line",?StringComparison.OrdinalIgnoreCase)){chart?=?new?LineChart();Console.WriteLine("初始化設置折線圖");}else?if?(type.Equals("pie",?StringComparison.OrdinalIgnoreCase)){chart?=?new?PieChart();Console.WriteLine("初始化設置餅狀圖");}return?chart;}}編寫客戶端測試代碼:
????class?Program{static?void?Main(string[]?args){IChart?chart?=?ChartFactory.GetChart("line");chart.Display();}}編譯運行輸出,結果如下:在客戶端的測試代碼中,我們可以看到使用工廠類的靜態(tài)工廠方法來創(chuàng)建具體產品對象。如果后期需要更換產品,只需要修改靜態(tài)工廠方法的入參即可。例如,我們需要將折線圖改成餅狀圖,只需要將代碼:IChart chart = ChartFactory.GetChart("line"); 中的 line 改為 pie 即可。
四、方案的改進
一切進行的很順利,但是A科技公司的開發(fā)人員發(fā)現(xiàn),在創(chuàng)建具體的 Chat 對象時,每次更換具體的 Chart 對象類型都需要修改靜態(tài)工廠方法中的入參,然后還需要重新編譯客戶端代碼,這對客戶端來說**違反了開閉原則。**那有沒有一種方法可以在不修改代碼的前提下就能更換具體的產品對象呢?當然有!在C#中我們可以將靜態(tài)工廠方法中的入參配置到config文件中,這樣每次要替換具體的產品類,我們只需要修改配置文件即可。比如:
<?xml?version="1.0"?encoding="utf-8"??> <configuration><appSettings><add?key="chartType"?value="line"/></appSettings> </configuration>客戶端代碼如下:
????class?Program{static?void?Main(string[]?args){//獲取配置文件中的chartType的值string?type?=?System.Configuration.ConfigurationManager.AppSettings["chartType"];IChart?chart?=?ChartFactory.GetChart(type);chart.Display();}}從上邊的代碼可以看出,客戶端代碼中不包含任何與具體圖表相關的信息。如果要更換具體圖表對象,只需要修改config配置文件中的配置即可,無需修改任何代碼,符合開閉原則。
補充:
在ASP.NET Core中配置文件通常使用的是json文件,比如appsettings.json,另外還有很多種配置文件類型,比如:ini、環(huán)境變量、用戶機密等
五、創(chuàng)建對象與使用對象
如果一個類即要負責創(chuàng)建引用的對象,又要使用使用引用對象的方法,這樣就會使創(chuàng)建對象和使用對象的職責耦合在一起,這樣會導致一個很嚴重的問題,那就是違反了開閉原則。那怎么解決這個問題呢?
最常用的一種方法就是將創(chuàng)建對象的職責移除,并交由其它類來負責創(chuàng)建。由誰創(chuàng)建呢?答案是:工廠類。通過引入工廠類,客戶類不涉及對象的創(chuàng)建,對象的創(chuàng)建者也不會涉及對象的使用。
所有的工廠模式都強調一點:兩個類A和B之間的關系應該僅僅是A創(chuàng)建B或者是A使用B,而不能兩種關系都有。將對象的創(chuàng)建和使用分離,也使得系統(tǒng)更加符合單一職責原則,有利于對功能的復用和系統(tǒng)的維護。
將創(chuàng)建對象和使用對象分離還有一個好處:防止用來實例化一個類的數(shù)據和代碼在多個類中到處都是。
也不是說將每一個類都配置一個工廠類,而是要具體問題具體分析,對于那些產品類很簡單,而且也不存在太多變數(shù),構造過程也很簡單的類,就無需為其提供工廠類,直接在使用的時候實例化即可。
六、簡單工廠模式總結
簡單工廠模式提供了專門的工廠類用于創(chuàng)建對象,將對象的創(chuàng)建和對象的使用分離開來。
1.主要優(yōu)點
簡單工廠模式實現(xiàn)了對象創(chuàng)建和使用的分離。對于客戶端來說免除了直接創(chuàng)建產品對象的職責,而僅僅負責“消費”產品即可。
客戶端無需知道所創(chuàng)建的具體產品類的類名,只需要知道具體產品類所對應的參數(shù)即可。
通過引入配置文件,可以在不修改任何客戶端代碼的情況下更換和增加新的具體產品類,在一定程度上提高了系統(tǒng)的靈活性。
2.主要缺點
工廠類集中了所有具體產品類的創(chuàng)建邏輯,職責過重。一旦工廠類不能正常工作,整個系統(tǒng)都會受到影響。
簡單工廠模式會增加系統(tǒng)中類的個數(shù)(引入了新的工廠類),增加了系統(tǒng)的復雜度和理解難度。
系統(tǒng)擴展困難。如果要添加新產品就需要修改工廠邏輯,如果產品類較多時,有可能會造成工廠邏輯過于復雜,不利于系統(tǒng)的擴展和維護。
簡單工廠模式由于使用了靜態(tài)工廠方法,造成工廠角色無法形成基于繼承的等級結構。
3.適用場景
工廠類負責創(chuàng)建的對象比較少。由于創(chuàng)建的對象較少,不會造成工廠方法中的業(yè)務邏輯太過復雜。
客戶端只知道傳入工廠類的參數(shù),對于如何創(chuàng)建對象并不關心。
示例代碼:
https://github.com/crazyliuxp/DesignPattern.Simples.CSharp
參考資料:
總結
以上是生活随笔為你收集整理的一起学习设计模式--02.简单工厂模式的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: Csv数据库CsvDb
 - 下一篇: 聊一聊Load Average