"Simplicity is prerequisite for reliability." - Edsger Dijkstra
「簡單是可靠的前提條件。」 —— 艾茲格·迪傑斯特拉
最近在重溫設計模式(in Java)的相關知識,然後在工廠模式的實現上面進行了一些較深入的探究,有了一些以前不曾注意到的發現,遂將其整理成文,以作後用。
工廠模式是典型的建立型模式,相比於建立簡單物件的小作坊(new一個物件),它可以將複雜的構造邏輯進行封裝,並對外暴露相對簡單的介面方法,從而簡化物件的建立流程。並在一定程度上提供平替模組而不用改變程式碼結構的靈活特性。
在許多書籍和文獻裡喜歡將工廠模式劃分為簡單工廠模式和抽象工廠模式,但是我個人認為它們或許並沒有如此明確的界限——至少在OOP裡面。繼承的存在使得我們的「工廠」和」產品「都可以是多型的,我認為理解這一點尤為重要。工廠模式只是一種對複雜構造流程的封裝,至於抽象程度高還是低並不影響它的本質。
通過程式碼來展示工廠模式的文章已經很多了,但我認為它很無聊且脫離實際,有點通過答案來倒推過程的味道。我將用一個誇張的故事來說明工廠模式的侷限,以及需要注意的地方,提醒自己在今後的工作中不要犯這樣的錯誤——當我得意忘形,以為自己很聰明的時候。
通常來說,只有明確了我們的「產品」是什麼,才能知道我們需要怎麼樣的「工廠」。我們假定最初的產品是一個MP3播放器,它的構造很複雜,因此我們引入了一個工廠類用來建立它的範例。為了應對未來可能的修改,我們對解碼器和解碼器工廠做了介面的抽象,看起來像這樣:
到目前為止,它都符合我們的設計預期,運作良好。我們甚至有足夠的信心應對未來可能的(想像中的)需求變更:「隨著業務的發展,只支援MP3格式的播放器看起來已經太寒酸了,於是乎引入了其它的播放格式。得益於我們之前的設計,不用改動原來的程式碼,只需要增加一組實現就能實現新增的需求,so easy.」
然而,現實總是打臉的,經過產品的深入調研,決定將播放器業務擴充套件到視訊領域,對沒錯,就先支援個H.264編碼的視訊流吧,好像同行競品都在用呢。於是,開發人員不得不重新考量原來的設計。於是經過若干個996,開發組調整了原來的設計,並完成了下面的系統:
開發人員頂著黑眼圈,面帶滿意微笑地在最後期限的前一天晚上提交了程式碼——這下無論它增加音訊格式和視訊格式,我們都可以靈活應對了,嘿嘿嘿!負責架構的小鍋拍著胸脯向各位開發成員如是說道。並在PPT上附上了下一階段的(想像中的)技術方向:
新入職的初級開發小蔡似乎覺得有什麼地方不太對勁,欲言又止。但下一秒又安慰自己:肯定是我自己經驗不足,理解還不到位,這個設計應該沒有問題!
隨著一陣聊天工具此起彼伏的響聲,產品又緊急召集眾人開會了。」來活了各位,我們的播放器面對同行毫無優勢,經過我們的研究,決定在原有的播放基礎上,增加轉換輸出的功能!速度要快,質量要穩!對了,最好能多支援幾種格式,最好市面上找得到的格式都支援一下。「各位開發臉色鐵青,尤其是架構師小鍋的臉色黑得最為純粹。
由於研發週期短,專案組來不及細細思考和重新設計,只能在原有專案基礎上增加相關的模組來應對新的需求。半小時討論,半小時敲定。大家一致決定,不如就沿用原來工廠模式的設計,反正我們已經有前面的經驗,應該不會有什麼大問題,於是新的設計誕生了:
為了維持專案原有的結構設計,開發人員在編碼階段似乎顯得有點不堪重負,經過兩週緊鑼密鼓的開發,專案不出所料地延期了。
年輕的被寄予厚望的架構師小鍋在覆盤會議上失去了往昔眼中的高光。
以至於多年以後,在公司新來的架構師小程向大家講述工廠模式的巧妙應用的那一刻。
不再年輕的小鍋又回想起三月裡那個溫暖的下午。注:就是去背鍋的那個下午。
小鍋起身,先是肯定了小程紮實的基本功和流暢的表達,隨後又把當年的往事向大家娓娓道來,邊板書邊講,盡最大可能向大家講述了當年事情的來龍去脈,並作出總結:
眾人陷入沉默,隨後年輕的小程率先打破僵局,向小鍋問道:」那麼,最後,問題是怎麼解決的呢?「
小鍋微微一笑,先是沒有說話,隨後轉過身去瘋狂畫起圖來,畢了瀟灑轉身將手背到身後,說:」從哪裡跌倒就從哪裡站起來。「
為了更好的說明工廠模式能解決什麼問題,不能解決什麼問題,以上故事純屬虛構,飽含誇張的成分,如有雷同,請勿對號入座。
回到最初的起點,只有明確了我們的「產品」是什麼,才能知道我們需要怎麼樣的「工廠」。 不必著急引入模式,先讓產品飛一會兒。在不穩定的維度上引入工廠模式是沒有意義的,因為異變需求無法被初始抽象完全界定,錯誤的抽象還不如不抽象。