來源:北大青鳥總部 2023年02月16日 14:40
在程序猿世界里,能夠寫出優雅、可擴展、低耦合、甚至像詩一樣的代碼,是每個程序猿畢生的追求~
秉承這個人生的終極理想,猿小二入職一家新公司,開始慢慢熟悉公司的代碼,越來越覺得不對勁,這家公司的代碼寫的可真爛,各種耦合牽一發動全身,完全不是面試時老板說的公司的代碼都像詩一樣優雅,猿小二感覺自己上當了!
沒辦法,猿小二只能硬著頭皮繼續了,不過猿小二轉念一想,代碼這么多問題,不就是給我表現的機會嗎?猿小二心中竊喜,思考著如何用設計模式來重構現在的代碼,增加程序間的可擴展性并降低程序間的耦合度,猿小二想到最近正在研究的工廠模式,于是猿小二開始了他的改造之旅......
經過了一段時間的努力,猿小二終于完成了一部分代碼的改造;正當他在對自己的成就自信滿滿的時候,一些同事突然過來說改造之后的代碼雖然感覺很優雅(不明覺厲),但是他們卻看不懂,猿小二說,我來給大家講一下我的改造之旅吧,其實也不是那么順利的,于是猿小二又開始了他的分享之旅~~~
最開始的時候,猿小二對一些代碼的改造使用了簡單工廠模式,簡單工廠就是有一個工廠類,根據傳入的參數不同創建不同的對象實例;比如說,有個生產汽車的工廠(CarFactory),可以生產各種不同品牌的汽車(AudiCar、BWMCar、AMGCar)
定義一個汽車的接口
/** * 汽車接口 */ public interface Car { // 汽車會跑 public void run(); } |
定義各種品牌的汽車類,實現汽車接口
/** * 奧迪汽車類 */ public class AudiCar implements Car { public void run() { System.out.println("奧迪汽車..."); } } |
/** * 寶馬汽車類 */ public class BWMCar implements Car { public void run() { System.out.println("寶馬汽車..."); } } |
對于這些不同品牌的汽車我們就可以提供一個工廠類,來根據不同的品牌參數去生產不同的汽車
/** * 汽車工廠 */public class CarFactory { // 生產不同品牌的汽車 public Car produce(String bank) { if ("AudiCar".equals(bank)) { return new AudiCar(); } else if ("BWMCar".equals(bank)) { return new BWMCar(); } return null; } } |
有了工廠對象了,接下來就可以生產汽車了
/** * 測試生產汽車 */ public class CarTest { public static void main(String[] args) { // 創建汽車工廠 CarFactory carFactory = new CarFactory(); // 生產奧迪汽車 Car audi = carFactory.produce("Audi"); audi.run(); // 生產寶馬汽車 Car bwm = carFactory.produce("BWM"); bwm.run(); } } |
這樣以來的話,我們就可以不用關心具體汽車對象的生產過程,只需要根據傳遞的參數(汽車品牌)就可以創建需要的汽車品牌了。
不過還有一個問題,就是如果我們要生產其他品牌的汽車,比如:豐田汽車,就的去改工廠類CarFactory里代碼,如果是汽車品牌多了話,豈不是要經常修改,這種頻繁修改的代碼的行為,這顯然不符合開放-封閉原則,對于追求極致的猿小二來說,顯然是接受不了的,于是他有開始新一輪的改造。
這個時候,猿小二想到了工廠方法模式,不同于簡單工廠模式的是,工廠方法模式沒有使用統一的工廠去生產不同的汽車品牌,而是分別給不同的汽車品牌建設不同工廠,比如:要生產Audi汽車,就創建Audi的汽車工廠;這樣每一個汽車品牌都有自己獨立的汽車生產工廠。
首先,先抽取一個最頂層的汽車工廠接口
/** * 汽車工廠 */ public interface CarFactory { // 生產汽車 public Car produce(); } |
然后,讓不同的汽車品牌工廠都實現這個接口,比如:奧迪汽車工廠、寶馬汽車工廠、豐田汽車工廠。
/** * 奧迪汽車工廠 */ public class AudiCarFactory implements CarFactory { // 負責生產奧迪汽車 public Car produce() { return new AudiCar(); } } |
豐田汽車工廠
/** * 豐田汽車廠 */ public class TOYOTACarFactory implements CarFactory { // 負責生產豐田汽車 public Car produce() { return new TOYOTACar(); } } |
測試生產汽車
/** * 汽車工廠方法測試 */ public class CarFactoryMethodTest { public static void main(String[] args) { // 奧迪汽車廠生產奧迪汽車 CarFactory audiCarFactory = new AudiCarFactory(); Car audi = audiCarFactory.produce(); audi.run(); // 寶馬汽車廠生產寶馬汽車 CarFactory toyotaCarFactory = new TOYOTACarFactory(); Car toyota = toyotaCarFactory.produce(); toyota.run(); } } |
通過這種工廠方法模式,我們再也不用擔心增加汽車品牌而去修改汽車工廠的代碼了,只需要創建相應品牌的汽車工廠類就可以了,而且這種方式還符合了設計模式的開放-封閉原則,簡直是完美;雖然有可能隨著汽車品牌的增加,工廠類也不斷增加,一定程度上會增加系統的復雜度,但是相對于它帶來的好處還是可以接受的。
本來事情已經告一段落,猿小二可以稍微輕松下了,沒想到老板說又有新的需求了,這些汽車廠要涉足汽車發動機領域,想要自己生產汽車發動機,這可難壞了猿小二,本來按照原來的思路,可以在創建發動機的工廠類,但是考慮到制造成本(發動機和組裝在同一個工廠生產),準備對原來的汽車工廠進行改造。
為了滿足新的需求,需要在對原來的工廠進行改造,增加生產發動機的方法,這種方式就涉及到另外一種抽象工廠模式,在抽象工廠模式中,每一個具體工廠都提供了多個工廠方法用于產生多種不同類型的對象,比如,汽車工廠既生產汽車,也生產汽車發動機,于是猿小二開始新一輪的改造。
首先,我們先定義抽象工廠類,它既可以生產汽車,也可以生產汽車發動機
/** * 抽象工廠 */ public interface CarAbstractFactory { // 生產汽車 public Car produce(); // 生產發動機 public Engine createEngine(); } |
奧迪汽車工廠類實現這個抽象接口,生產奧迪汽車和奧迪的發動機
/** * 奧迪汽車工廠 */ public class AudiCarFactory implements CarAbstractFactory { // 負責生產奧迪汽車 public Car produce() { return new AudiCar(); } // 生產奧迪發動機 public Engine create() { return new AudiEngie(); } } |
這樣我們就可以看到奧迪汽車廠就可以生產自己品牌的汽車和發動機,其他品牌也可以生產各自的汽車和發動機
/** * 抽象工廠測試 */ public class AbstractFactoryTest { public static void main(String[] args) { // 奧迪汽車廠 CarAbstractFactory audiCarFactory = new AudiCarFactory(); // 生產奧迪汽車 Car produce = audiCarFactory.produce(); produce.run(); // 生產奧迪汽車發動機 Engine audiengine = audiCarFactory.createEngine(); audiengine.createEngine(); } } |
從這里我們可以看出其實抽象工廠模式是一個工廠可以生產多個產品類,也就是一系列相互關聯的產品,比如:汽車和發動機。
其實,無論是簡單工廠模式,工廠方法模式,還是抽象工廠模式,他們都屬于工廠模式,雖然在形式有些區別,但是最終目的都是為了解耦。在使用時,我們不必去在意這個模式到底工廠方法模式還是抽象工廠模式,因為通常情況下是結合使用;經常你會發現,明明使用的工廠方法模式,當新需求來臨,稍加修改,加入了一個新方法后,由于類中的產品構成了不同等級結構中的產品族,它就變成抽象工廠模式了;而對于抽象工廠模式,當減少一個方法使的提供的產品不再構成產品族之后,它就演變成了工廠方法模式;所以,在使用工廠模式時,只需要關心是不是達到我們的業務需求,并且最大限度降低系統間的耦合度。