你的代碼過度設計了嗎?
引言
設計模式被認為是軟件設計的“規(guī)范”,但是在互聯(lián)網(wǎng)快速發(fā)展的過程中,也暴露了一些問題。相比過程式代碼的簡單與易于修改,設計模式常常導致代碼復雜,增加理解與修改的成本,我們稱之為 “過度設計”。
過度設計是認知提升過程中的必經(jīng)階段,用什么語言都一樣。一般情況下,我們跟著公司內(nèi)部的開發(fā)規(guī)范走,就能在寫代碼的時候避免大部分坑點,減少代碼故障和設計缺陷,但這也無法完全解決過度設計的問題,那么過度設計是怎么產(chǎn)生的,又需要怎么減少呢?
本文將探索以下問題:
- 為什么要做代碼設計
- 設計模式是如何衍生出來的
- 過度設計的的常見形式
- 如何避免過度設計
1.為什么要做代碼設計
我認為做代碼設計的根本原因只有一個:提高代碼質(zhì)量;
而代碼質(zhì)量的提升怎么體現(xiàn)出來呢?兩個方面:1、讓代碼讀起來更容易;2、讓代碼寫起來更簡便。
1.1 讓代碼讀起來更容易
首先,對于一個程序員來說,我們既要開發(fā)自己的項目,也會維護別人交接過來的項目。我們接手的代碼風格各式各樣,理解成本也各不相同。
不否認一些同學技術能力非常出眾,理解能力超群,什么樣的代碼都能讀懂,并能很快的在其基礎上進行優(yōu)化、開發(fā)、重構。但對于絕大多數(shù)心態(tài)平和的同學而言,接手一個 設計凌亂,無標準開發(fā)規(guī)范 的項目絕對能讓其心態(tài)崩潰甚至開發(fā)時滿嘴臟話。我們開發(fā)時遵守的“編碼規(guī)范”、“設計規(guī)范”,無一不是在為了讓項目更好理解,更好維護。
舉幾個例子:編碼的分層、分模塊,對象和接口設計時的單一職責原則、開閉原則、策略模式等,都能讓我們的項目更有層級,更好理解其結構,讓代碼讀起來更容易。
1.2 讓代碼寫起來更簡單
說到讓代碼寫起來更簡單,就不得不說到代碼的復用。如果代碼不復用,每增加一個需求就只能往上堆代碼,寫的代碼越來越多,代碼風格與功能就不會呈收斂態(tài)。
那么怎么能使代碼更簡單呢?
盡量相同的功能不寫第二遍,抽對象相同屬性走繼承、抽相同方法做工具類、抽象類;按模塊抽、根據(jù)路由條件抽、橫著抽、豎著抽,總之代碼復用性越高,寫的代碼就越少。但我們又不能不按條理抽,因為這樣會影響代碼的可讀性也會使依賴變得復雜,這時候設計模式就登場了,先輩們經(jīng)過多年的代碼設計經(jīng)驗總結了一些常用代碼的寫法,用于解決 不同場景下如何能將代碼在有條理的條件下寫的更“偷懶” 的問題。(這里只是舉例,并不是說這些模式就是全部的,也不代表這些就是最好的)。
創(chuàng)建型模式:工廠方法模式、抽象工廠模式、單例模式、建造者模式、原型模式。結構型模式:適配器模式、代理模式、橋接模式、裝飾器模式、外觀模式、享元模式、組合模式。行為型模式:策略模式、模板方法模式、觀察者模式、迭代子模式、責任鏈模式、命令模式、備忘錄模式、狀態(tài)模式、訪問者模式、中介者模式、解釋器模式。
學習這些東西能讓我們的代碼復用性更高,擴展起來更容易,也會讓我們代碼看起來高端、大氣上檔次。
簡單介紹幾種設計模式
- 工廠模式能讓我們更簡單地創(chuàng)建基礎相同而局部不同的相似對象;
- 適配器模式能讓我們在實現(xiàn)接口時更簡單地只實現(xiàn)特異性方法;
- 策略模式則能夠讓我們調(diào)用接口時根據(jù)不同的條件路由到不同的實現(xiàn)策略上去。
換個角度看到設計模式
讓我們換個角度來看上面這些設計模式,如果我們將接口的實現(xiàn)方法當做對象類的屬性,
適配器模式是否就成了一個適用于 class 類的工廠模式;工廠模式通過不同的屬性參數(shù)創(chuàng)建不同的對象;
適配器模式通過不同的場景創(chuàng)建不同的接口實現(xiàn)類,甚至更向上抽象一層,策略模式通過不同的業(yè)務條件路由到不同的接口實現(xiàn)時也有些同樣的味道。
我想說的是不同的設計好多時候他們的基本思考方式是相通的,它們只是一些問題的常用解決方法模板,并不是各不相同的死定義。
2.設計模式是如何衍生出來的
上面說了那么多因為所以,大家可能看文字看的頭痛,這里我先舉例來說明下常規(guī)代碼接口設計的發(fā)展形式:
產(chǎn)品說我要做個天體系統(tǒng),先來個月亮
需求一:要有功能:創(chuàng)建個月亮(新產(chǎn)品,產(chǎn)品功能可能給不明確,方向也不明確,先簡單實現(xiàn)其功能)
一月后,再來個地球
需求二:要有功能創(chuàng)建一個月亮,一個地球(有兩個星球了,后續(xù)可能會有多個,抽象星球父對象,將相同的東西放父類中)
再一月后,要上星球管理系統(tǒng)了
需求 3:星球創(chuàng)建的時候要加過程,創(chuàng)建地球的時候要順便在星球上造山川、河流,還都要通知到星球管理局(針對不同星球有差異化需求了,考慮到現(xiàn)在星球還只有兩種,段時間內(nèi)也不會增加特別多,結合開發(fā)時間,將星球類當做適配器來用,同時使用模板方法模式將特異性方法抽取成公共方法)
再一年后,要豐富星系了
需求 4:一次創(chuàng)建一個星球,要支持太陽系的 8 種行星,并且之后會擴展為無數(shù)種星球,除了地球外初始化的數(shù)據(jù)和月亮一樣(由于星球數(shù)大量擴張,再用 java 類來定義星球需要大量創(chuàng)建 java 類,而且之后要支持無數(shù)種星球,現(xiàn)有模式要被打破了,但現(xiàn)階段稍微改改還能平滑過度,結合星球擁有的公共屬性方法,將其合為一個類,先對地球做特殊處理,等要無限支持的時候,做成數(shù)據(jù)庫配置或者配置中心配置實現(xiàn))
再一月后:完善產(chǎn)品,支持無限種星球
需求 5:需要支持無限種星球了,可以創(chuàng)建的星球需要由運營去配置,而且創(chuàng)建后需要按照順序分別執(zhí)行不同的動作,現(xiàn)定義 3 種動作,將來還可能支持更多種動作,動作 1:初始化初始化物體,具體是什么可以由運營配置;動作 2:通知星球管理局;動作 3:遭到撞擊,毀滅;(到處都要支持配置,開放度太高,由于每種星球的差異化需求,之前的星球模型的模板方法明顯已經(jīng)不支持現(xiàn)有的場景了,需要用一種可以通過配置來執(zhí)行方法的模型,這里 DemoService 類在一張圖里截不完了,和場景 4 的完全一樣,就先省去吧)
上述過程是一個功能從簡單到復雜,然后使用的設計模式也逐漸改變,最終適用于業(yè)務發(fā)展的一種過程,如果在需求 1 的時候直接就上需求 5 的責任鏈模式,那么這個就屬于設計過度了。
關于設計過度不同同學往往有不同的容忍度,有時候在某種情況下也可能會成為有前瞻性的設計,關于這塊有時候還是不好評判的。
3.過度設計的的常見形式
什么叫過度設計?
只要團隊里內(nèi)沒有足夠能力把控未來三年的架構發(fā)展,便只需要把代碼重構到恰好滿足當下的最佳狀態(tài),那么我們在開發(fā)的時候總是要考慮一下未來的需求到底會往哪個方向走。你蒙中了,就叫正交分解。你沒蒙中,就叫過度設計。
過度設計的場景很多,這里列舉下常見的幾種情況:
有時相同的功能使用不同的設計模式都可以實現(xiàn),設計模式的引入常常導致代碼復雜,增加理解與修改的成本,我們作為開發(fā)者必須要把控理解成本、代碼實現(xiàn)復雜度、減少的代碼量,代碼擴展性之間的平衡,一旦思考不到位、理解不到位,打破了其中的平衡,過度設計就產(chǎn)生了。
而我們在日常學習和開發(fā)中,甚至在發(fā)或者技術方案評審的時候也不可能開對技術、業(yè)務全部理解到位,即使對自己負責的項目理解到位了也沒有條件對跨項目協(xié)作中的直接或間接依賴項目的來龍去脈完全清楚,這時候當你想搞個“高級”東西的時候,就很難把控會不會搞出過度設計,這也就是我說的過度設計是認知提升過程中的必經(jīng)階段。
4.如何避免過度設計
上面說了過度設計的來龍去脈和很難避免性,那么如何能最大限度的避免過度設計呢?
根據(jù)我過往經(jīng)驗大概從這幾個方面著手:
- 按照公司的編碼規(guī)范寫代碼、遵守公司內(nèi)的約定是很重要的事情,如果公司內(nèi)部沒有自己的編碼規(guī)范,還可以參考業(yè)內(nèi)認可的編碼規(guī)范(這些都是公司前輩們總結的最適用于自己公司的經(jīng)驗)
- 熟悉設計模式,對于不熟悉的設計模式寧愿不用也不瞎用,保持代碼簡單易懂(不做一知半解,不炫技)
- 充分理解需求并有一定前瞻能力,根據(jù)業(yè)務場景復雜度選擇自己熟悉的設計模式(一切拋開場景的設計都是攪屎棍)
- 編碼時有點潔癖,習慣抽取和改動不合理的代碼,對代碼質(zhì)量有追求(要認真哦,出了 bug 是要背的,沒把握的話可以暫時不改,但要打上標記后續(xù)一點點改)
- 小步快跑,簡單點可能也挺好:不要一開始就想著做出完美的方案,很多時候優(yōu)秀的方案不是設計出來的,而是逐漸演變出來的,一點點優(yōu)化已有的設計方案比一開始就設計出一個完美的方案容易得多。
5.總結
上面或許有些東西說的比較主觀,有些細節(jié)點說的不到位,但我覺得當你真正發(fā)現(xiàn)并領悟所有的設計架構都是圍繞業(yè)務服務和后期維護考慮時,過度設計的問題大體上就解決了。
以上是我對于過度設計的理解,隨著我工作年限的增加說不定過兩年我會再有其他感悟,到時候再分享給大家。