1.“new”有什麼不對勁? 在我們沒有接觸到工廠模式(簡單工廠、工廠方法模式、抽象工廠模式)之前,我們實例化對象唯一的方法就是通過“new”關鍵字來完成。但是,大量的使用“new”關鍵字來實例化對象會違背一些設計原則,因為代碼與具體的類型綁在一起,從而導致過多的依賴於細節而非抽象,這樣代碼就很難適 ...
1.“new”有什麼不對勁?
在我們沒有接觸到工廠模式(簡單工廠、工廠方法模式、抽象工廠模式)之前,我們實例化對象唯一的方法就是通過“new”關鍵字來完成。但是,大量的使用“new”關鍵字來實例化對象會違背一些設計原則,因為代碼與具體的類型綁在一起,從而導致過多的依賴於細節而非抽象,這樣代碼就很難適應需求變化。
在面向對象編程中我們大量的使用到了繼承、多態的特性,但是在使用這些特性時往往會延申出一些新的問題。例如在一個有繼承模塊的代碼當中,如果子類發生新增或刪除,這就不得不去使用類的“調用層”做出相應的修改,因為你new的都是具體的類型,當下層發生變動就不得不去上層進行修改。對於這樣的場景來說,實際上它也違背了設計模式原則之一的“開閉原則”,即對擴展開放,對修改關閉,這會不利於程式的穩定和擴展。
2.舉例反映問題
接下來將通過代碼案例來反映出,在一個使用繼承模塊的代碼當中,大量使用“new”關鍵字,在設計層面會帶來什麼樣的缺陷。我們以一個汽車銷售系統作為背景,其中汽車類型UML圖如下:
其中有一個賣車的方法使用到了“new”來實例化車型對象,相應的代碼如下:
1 //賣車方法
2 public void SellCar(string carType)
3 {
4 Car car;
5 if(carType=="寶馬 X3")
6 {
7 car=new Bmwx3();
8 }
9 else if(carType=="吉普牧馬人")
10 {
11 car=new Jeeper();
12 }
13 else if(carType=="賓士G63")
14 {
15 car=new BenzG63();
16 }
17
18 TestDrive(car);//試駕
19 Collection(car);//收款
20 License(car);//上牌
21 }
在實際的生活當中我們應該都知道,4s店賣的車型不可能是一層不變的,而是經常性的會發生產品的更新換代,所以會不斷對車型做出新增或刪減。如果汽車銷售系統中使用了以上的代碼,這就意味這每次車型模塊的調整都要同步到“賣車方法”中,並且隨著公司規模的增大所賣的車型會增多,在新增車型後代碼中出現大量的else if,這會顯得非常臃腫,在刪除時還可能誤刪。
即便你說你的“汽車銷售系統只賣一種車”(也就是只new一種對象類型),你可能還是會在使用構造函數創建對象時要加入參數的情形,這些變化都會促使你因為下層模塊的調整而影響到上層模塊。
3.改善方法
1.編程當中任何需求或代碼都是無法避免的,而我們能做到就是運用設計模式的思想,能夠將程式更好的去適應變化。其中的一個思想就是:“找出變化的部分,把它們從不變的部分分離出來”。以上面的“汽車銷售系統”中的賣車方法為例,分析變化如下:
2.然後,我們將變化的部分單獨的封裝到一個類的靜態方法中,該類的靜態方法將專門用於創建具體的汽車對象,代碼如下:
1 public class CarFactory
2 {
3 public static Car CreateCarInstance(string type)
4 {
5 Car car=null;
6 if(carType=="寶馬 X3")
7 {
8 car=new Bmwx3();
9 }
10 else if(carType=="吉普牧馬人")
11 {
12 car=new Jeeper();
13 }
14 else if(carType=="賓士G63")
15 {
16 car=new BenzG63();
17 }
18 return car;
19 }
20 }
3.在抽離並封裝變化後,我們將對“賣車方法”中的實例化車對象部分進行改造。
1 public void SellCar(string carType)
2 {
3 Car car=CarFactory.CreateCarInstance(carType);
4
5 TestDrive(car);//試駕
6 Collection(car);//收款
7 License(car);//上牌
8 }
根據以上的三個步驟,其實一個簡單工廠創建對象的方式就已經形成了,其中專門創建對象的Factory類我們稱之為工廠,它實例化的對象我們稱之為產品。此時我們創建對象的方式不在是"new",而是通過一個靜態方法。
對於上層模塊(賣車方法),在也不用擔心下層模塊(汽車車型)的變化而牽連到自身,它只有根據指定的類型創建對應的車型對象即可,並且上層模塊(賣車方法)也不用在承擔對象創建的細節,減少大量代碼和複雜度。
另外簡單工廠也解決了對象創建復用的問題,例如4s店的汽車保險系統、汽車維修系統等等都會用到實例化車型對象。這樣的話,如果車型發生修改我們只用調整簡單工廠類即可而其他(調用層)都不必受到改動的牽連。
4.定義
簡單工廠又稱之為靜態工廠方法,它屬於創建型模式中的一種簡單運用。
嚴格來講它其實並不屬於一種設計模式,反而比較像我們在遵循設計模式原則時的一種編程習慣。你要你能夠在編程中遵循並靈活運用:迪米特法則(最少知道原則)、依賴倒置(依賴抽象,不依賴細節)、里氏替換(子類替代父類)這些原則,其實就會無形當中使用簡單工廠來實現對象的實例化,而並非只使用“new”。
5.短板
使用簡單工廠實例化對象雖然解決了一些問題,但是有同時存在一些新的問題。因為我們的簡單工廠本身就是一個具體的類型,這違反了面向介面(抽象)編程的思想。
還是以汽車銷售作為背景來說,假如你依賴的這個A工廠因為疫情出現停產,又或者因為你要擴張你的車型使用新的工廠,那麼此時你依賴的A工廠(具體的類)就很難做出擴展,而只能因為你換了工廠導致上層模塊都要跟著調整。所以,後續誕生出的“工廠方法模式”和“抽象工廠模式”,就是在不斷解決因為變化而帶來各種的問題。
知識改變命運