跳轉到內容

軟體工程/架構/反模式簡介

來自華夏公益教科書

反模式和程式碼異味

[編輯 | 編輯原始碼]

如果設計模式是好人,那麼反模式就是壞人。有時好人也會變成壞人。這種情況在好萊塢電影中會發生,但也會在軟體工程中發生。

“金錘”是解決這個問題的一個常見概念:你在一個環境中學會了使用一個工具(金錘),現在因為你對學會使用這個複雜的工具感到自豪,所以突然間你到處都看到了金釘子。

一個很好的例子是單例模式:它非常容易,它是大多數初級軟體工程師理解的第一個模式,因此,假設它是好人,他們會在任何可能的場合使用它。然而,單例模式的問題是它違反了資訊隱藏。現在,資訊隱藏是現代軟體工程中的一條金科玉律,只有在有充分理由的情況下才能違反它。僅僅是瞭解了單例模式並不是一個充分的理由!

在軟體工程中,反模式是一種在實踐中可能被普遍使用但效率低下和/或適得其反的模式。[1][2]這個詞由安德魯·科尼格在 1995 年創造,[3]靈感來自於四人幫的著作設計模式,這本書在軟體領域發展了設計模式的概念。這個詞在三年後由反模式一書廣泛普及,[4]這本書將這個詞的使用範圍擴充套件到軟體設計領域之外,進入到一般社會互動中。根據後者的作者,至少需要存在兩個關鍵要素才能正式區分一個實際的反模式與一個簡單的壞習慣、壞做法或壞主意

  • 一些重複的行動、過程或結構模式,最初看起來是有益的,但最終產生的負面後果多於積極結果,以及
  • 存在一個經過重構的解決方案,該解決方案有明確的文件記錄,在實際實踐中得到驗證並且可重複。

透過正式描述重複的錯誤,人們可以識別導致這些錯誤重複的力量,並學習如何從這些破壞性模式中重構自己。

反模式的例子

[編輯 | 編輯原始碼]

為了更好地理解反模式,讓我們看幾個例子。透過研究這些例子,你可能會識別出你在某個時間點可能犯過的一些違反軟體工程原則的行為。其中一些反模式有非常有趣的名字。

單例過度使用

[編輯 | 編輯原始碼]

我們已經討論過這個問題:你立即理解的第一個模式,並且你大量使用它。但要注意它違反了資訊隱藏。因此,簡單的規則是:如果存在疑問,就不要使用它。我的經驗是,專案越大,出現的單例模式就越多。

如何檢測單例模式?檢視類圖。所有引用自身(或其基類)的類都是潛在的單例模式。如果你想擺脫它們,Kerievsky 向你展示了治療這種疾病的藥物。 [5]

功能分解

[編輯 | 編輯原始碼]

儘管曾經非常流行,但在現代面向物件的語言中,已經沒有功能分解的空間了。它是 C 或 Pascal 等過程語言的殘留。通常它表示被整合到新專案或遷移的舊軟體。

這種反模式以三種方式表現出來:類名聽起來像函式名(例如 CalculateInterest)。或者類只有一個動作,也就是說它們只做一件事。或者所有類屬性都是私有的(這是可以的),但它們只在類中使用。

人們喜歡這種反模式,因為它有一個有趣的名字。它指的是那些短暫出現然後消失得無影無蹤的類。要麼沒有人知道它們到底做什麼,要麼它們的 功能非常有限。通常它們是 不需要的,或者可以被其他類吸收。

通常透過以’*controller’或’*manager’結尾的類名來識別這種反模式。

通常是“敏捷”方法的結果,在這些方法中,沉思比設計更受重視。

義大利麵條

[編輯 | 編輯原始碼]

義大利麵條程式碼就像麵條一樣:非常長。雖然麵條很好吃,但程式碼越長就越不好。

Blob 是一個有很多屬性和方法的類。通常它們甚至不相關。你可以使用你最喜歡的程式碼分析工具來檢測這種異味,方法是列出具有大量屬性和方法或大量程式碼行的類。通常將這個類拆分為幾個更小的類會有所幫助。

複製貼上

[編輯 | 編輯原始碼]

顧名思義,有人從某個地方複製了一些程式碼到另一個地方。這是複製功能最簡單的方法,但由於許多原因應該避免。最簡單的解決方案是將程式碼變成一個方法,或者使用繼承。

要檢測幾乎完全相同的程式碼,你可以使用 PMD 的程式碼複製/貼上檢測器之類的工具。 [6][7]

熔岩流

[編輯 | 編輯原始碼]

什麼是熔岩流?“熔岩流是火山噴發時流出的熔岩,它是在非爆炸性溢流式噴發中形成的。當它停止流動時,熔岩會凝固形成火成岩。”[8]在軟體工程中,這意味著程式碼很古老,沒有人觸碰過它,而且沒有人敢觸碰它(永遠不要觸碰正常工作的類……)。

你可以使用原始碼控制系統找到這些類。只需列出那些長時間未被檢出和修改的類。

程式碼異味

[編輯 | 編輯原始碼]

程式碼異味類似於反模式,但沒有那麼正式。如果程式碼有異味,那麼這種異味可能是好的(像某些乳酪一樣),也可能是壞的,可能表明存在更深層次的問題。肯特·貝克在 20 世紀 90 年代後期提出了這個概念,馬丁·福勒在他的著作重構:改善既有程式碼的設計中使它流行起來。 [9]你可以使用 FindBugs、Checkstyle 或 PMD 等工具來查詢異味。通常使用重構來消除異味。馬丁·福勒和喬舒亞·凱里夫斯基等人在他們的著作中提供了相應的重構方法。

重複程式碼

[編輯 | 編輯原始碼]

這種氣味與複製貼上反模式非常相似。你可以使用 PMD 工具複製/貼上檢測器[6]來找出有問題的區域。

長方法

[edit | edit source]

與義大利麵條式程式碼反模式相關。具有超過 50 行程式碼的方法絕對值得懷疑。

過度暴露

[edit | edit source]

在當前資訊隱藏的維多利亞時代,過度暴露自然是一件壞事。如果一個類有太多方法,或者更糟糕的是,有任何公共屬性,那麼我們就稱之為過度暴露。你可以透過檢查類的公共方法來發現這種氣味。如果一個類擁有超過 50% 的公共方法,那麼它可能不符合資訊隱藏策略。

懶惰類

[edit | edit source]

讓我想起了幽靈反模式:這是一個功能過於簡單,以至於沒有存在理由的類。嘗試將其吸收進另一個類中。

大型類

[edit | edit source]

大型類與懶惰類正好相反。你可以透過類似的方法找到它,查詢具有太多方法或太多語句的類。通常情況下,一個類不應該擁有超過 30 個方法或超過 400 個語句。此外,具有太多屬性的類也可能是大型類。Kerievsky 展示了多種可能的方法來減少這種氣味。[5]

實際上是指沒有按照程式碼規範進行編碼。請參考 Meyer、MISRA 等規範。

已知反模式

[edit | edit source]

存在許多已知的反模式。以下列出了一些反模式及其簡要描述,供您參考。

組織反模式

[edit | edit source]
  • 分析癱瘓:將過多的精力投入到專案的分析階段。
  • 搖錢樹:一項利潤豐厚的傳統產品,往往會導致對新產品的懈怠。
  • 委員會設計:由許多貢獻者參與設計,但沒有統一的願景的結果。
  • 承諾升級:當決策被證明錯誤時,未能撤回決策。
  • 管理者至上:管理風格獨斷專行,不容許異議。
  • 矩陣管理:組織結構缺乏重點,導致忠誠度分散,缺乏方向。
  • 道德風險:將決策者與決策的後果隔離開來。
  • 蘑菇管理:對員工進行隱瞞和誤導(矇在鼓裡,餵食糞便)。
  • 煙囪式結構或孤島式結構:支援資料主要向上和向下流動,但阻礙跨組織交流的結構。
  • 供應商鎖定:使系統過度依賴外部提供的元件[10]

專案管理反模式

[edit | edit source]
  • 死亡行軍:每個人都知道專案將會是一場災難,除了 CEO。然而,真相仍然隱藏,專案被人工維持下去,直到“大爆炸”的零點到來。另一個定義:員工被迫在專案中加班加點,面對不合理的截止日期。
  • 群體思維:在群體思維中,群體成員避免提出超出共識思維舒適區的觀點。
  • 露易絲·萊恩計劃:管理層/銷售人員過度承諾,導致需要 IT 部門救援,這往往會導致開發匆忙、錯誤、疲勞、更重要的長期專案被延遲或停止,以及決策者變得更加大膽,重複這種冒險行為。
  • 煙霧和鏡子:展示尚未實現的功能將如何呈現。
  • 軟體膨脹:允許系統在後續版本中不斷需求更多資源。
  • 瀑布模型:一種較舊的軟體開發方法,無法充分應對意外變化。

分析反模式

[edit | edit source]
  • 旁觀者冷漠:當需求或設計決策錯誤時,發現問題的人卻無所作為,因為這會影響更多的人。

軟體設計反模式

[edit | edit source]

  • 抽象反轉:沒有公開使用者所需已實現的功能,因此他們使用更高級別的函式重新實現它。
  • 觀點模糊:展示一個模型(通常是面向物件分析和設計 (OOAD)),但沒有指定其觀點。
  • 一團亂麻:一個沒有可識別結構的系統。
  • 資料庫作為 IPC:使用資料庫作為常規程序間通訊的訊息佇列,而更輕量級的機制更適合。
  • 鍍金:在額外努力不再增加價值的情況下,繼續進行一項任務或專案。
  • 內部平臺效應:一個系統可定製性極強,以至於成為軟體開發平臺的糟糕複製品。
  • 輸入權宜之計:未能指定和實現對可能無效輸入的處理。
  • 介面膨脹:使介面功能過於強大,以至於難以實現。
  • 神奇按鈕:在介面程式碼中直接編碼實現邏輯,而不使用抽象。
  • 競爭性風險:未能看到不同事件順序造成的後果。
  • 煙囪式系統:幾乎無法維護的,由彼此關係不大的元件組成的集合。
面向物件設計反模式
[edit | edit source]
  • 貧血領域模型:使用沒有業務邏輯的領域模型。領域模型中的物件無法保證其在任何時刻的正確性,因為它們的驗證和修改邏輯放置在外部(很可能在多個地方)。
  • BaseBean:從實用程式類繼承功能,而不是委託給它。
  • 呼叫父類:要求子類呼叫父類的重寫方法。
  • 圓形-橢圓問題:基於值子型別對變數型別進行子型別化。
  • 迴圈依賴:在物件或軟體模組之間引入不必要的直接或間接相互依賴關係。
  • 常量介面:使用介面定義常量。
  • 上帝物件:將太多功能集中在設計的單個部分(類)中。
  • 物件汙水池:重用狀態不符合重用(可能是隱式)契約的物件。
  • 物件狂歡:未能正確封裝物件,允許無限制地訪問其內部。
  • 幽靈:物件唯一的目的是將資訊傳遞給另一個物件。
  • 順序耦合:一個類要求其方法以特定順序呼叫。
  • 悠悠球問題:由於過度碎片化,難以理解的結構(例如繼承)。
  • 急於等待:在一個物件的建構函式中觸發一個或多個非同步事件。

程式設計反模式

[edit | edit source]
  • 意外複雜性:在解決方案中引入不必要的複雜性。
  • 遠端操作:系統中相隔甚遠的兩個部分之間出現意外互動。
  • 盲目信任:缺乏對以下方面的檢查:(a)錯誤修復的正確性;(b)子程式的結果。
  • 沉重的錨:保留一個系統中不再有任何用途的部分。
  • 繁忙迴圈:在等待某個事件發生時佔用 CPU,通常是透過重複檢查而不是訊息傳遞來實現。
  • 快取故障:在錯誤被糾正後忘記重置錯誤標誌。
  • 迷信程式設計:在不理解原因的情況下使用模式和方法。
  • 按異常編碼:在識別到每個特殊情況時,新增新的程式碼來處理它。
  • 錯誤隱藏:在錯誤訊息顯示給使用者之前捕獲它,並且不顯示任何內容或顯示無意義的訊息。
  • 硬編碼:將有關係統環境的假設嵌入到其實現中。
  • 熔岩流:保留不希望的(冗餘或質量低的)程式碼,因為刪除它成本太高或具有不可預測的後果[11][12]
  • 迴圈-開關序列:使用迴圈語句中的開關來編碼一系列順序步驟。
  • 神奇數字:在演算法中包含無法解釋的數字。
  • 神奇字串:在程式碼中包含文字字串,用於比較、事件型別等。
  • 軟程式碼:將業務邏輯儲存在配置檔案中,而不是原始碼中[13]
  • 義大利麵條式程式碼:結構難以理解的程式,尤其是由於程式碼結構的誤用造成的。

方法論反模式

[編輯 | 編輯原始碼]
  • 複製貼上程式設計:複製(並修改)現有程式碼,而不是建立通用的解決方案
  • 金錘:假設最喜歡的解決方案普遍適用(參見:銀彈)
  • 不可能因素:假設已知錯誤發生的可能性很小
  • 非我所創(NIH)綜合徵:傾向於重新發明輪子(未能採用現有的、足夠的解決方案)
  • 過早最佳化:過早地為了感知的效率而編碼,犧牲了良好的設計、可維護性,有時甚至犧牲了實際的效率
  • 排列程式設計(或“意外程式設計”):試圖透過連續修改程式碼來找到解決方案,看看是否有效
  • 重新發明輪子:未能採用現有的、足夠的解決方案
  • 重新發明方形輪子:未能採用現有解決方案,而是採用自定義解決方案,其效能遠遜於現有解決方案
  • 銀彈:假設最喜歡的技術解決方案可以解決更大的過程或問題
  • 測試驅動開發:軟體專案中,新的需求在錯誤報告中指定

配置管理反模式

[編輯 | 編輯原始碼]
  • 依賴地獄:所需產品的版本問題
  • DLL地獄:動態連結庫 (DLL) 管理不善,尤其是在 Microsoft Windows 上
  • 擴充套件衝突:Mac OS X 之前的 Mac OS 版本的不同擴充套件嘗試修補作業系統相同的部件時出現的問題
  • JAR地獄:過度使用多個 JAR 檔案,通常由於對 Java 類載入模型的誤解而導致版本和位置問題

參考文獻

[編輯 | 編輯原始碼]
  1. Budgen, D. (2003). 軟體設計. Harlow, Eng.: Addison-Wesley. p. 225. ISBN 0-201-72219-4. "正如 Long (2001) 中所述,設計反模式是 '顯而易見但錯誤的,對反覆出現問題的解決方案'。"。
  2. Scott W. Ambler (1998). 過程模式:使用面向物件技術構建大型系統. Cambridge, UK: Cambridge University Press. p. 4. ISBN 0-521-64568-9. "...對反覆出現的問題,證明是無效的通用解決方案。這些方法稱為反模式。"。
  3. Koenig, Andrew (1995 年 3 月/4 月)。"模式和反模式"。面向物件程式設計雜誌8, (1): 46–48. {{cite journal}}: |access-date= requires |url= (help); Check date values in: |date= (help)CS1 maint: extra punctuation (link); 後來重新印刷在:Rising, Linda (1998). 模式手冊:技術、策略和應用. Cambridge, U.K.: Cambridge University Press. p. 387. ISBN 0-521-64818-1. "反模式就像模式一樣,只不過它不提供解決方案,而是提供看起來像是解決方案的東西,但實際上並非如此。"。
  4. Brown, William J. (1998). 反模式:重構軟體、架構和專案危機. John Wiley & Sons, ltd. ISBN 0471197130. {{cite book}}: Unknown parameter |coauthors= ignored (|author= suggested) (help)
  5. a b Kerievsky, Joshua (2004). 重構到模式. Addison-Wesley 專業版. ISBN 0321213351.
  6. a b http://pmd.sourceforge.net/cpd.html PMD
  7. http://www.onjava.com/pub/a/onjava/2003/03/12/pmd_cpd.html 使用 PMD 的 CPD 檢測重複程式碼
  8. http://en.wikipedia.org/wiki/Lava 熔岩
  9. Fowler, Martin (1999). 重構。改進現有程式碼的設計. Addison-Wesley. ISBN 0-201-48567-2.
  10. 供應商鎖定 在 antipatterns.com
  11. 熔岩流 在 antipatterns.com
  12. "未記錄的 '熔岩流' 反模式使流程複雜化". Icmgworld.com. 2002-01-14. 檢索於 2010-05-03.
  13. Papadimoulis, Alex (2007-04-10). "軟編碼". Worsethanfailure.com. 檢索於 2010-05-03.

進一步閱讀

[編輯 | 編輯原始碼]
書籍
Laplante, Phillip A. (2005). 反模式:識別、重構和管理. Auerbach 出版社. ISBN 0-8493-2994-9. {{cite book}}: 未知引數 |coauthors= 忽略(建議使用 |author=) (幫助)
Brown, William J. (2000). 專案管理中的反模式. John Wiley & Sons, ltd. ISBN 0-471-36366-9. {{cite book}}: 未知引數 |coauthors= 忽略(建議使用 |author=) (幫助)
Brown, William J. (1998). 反模式:重構軟體、架構和危機中的專案. John Wiley & Sons, ltd. ISBN 0471197130. {{cite book}}: 未知引數 |coauthors= 忽略(建議使用 |author=) (幫助)
Kerievsky, Joshua (2004). 重構到模式. Addison-Wesley Professional. ISBN 0321213351.
Feathers, Michael (2004). 有效地使用遺留程式碼. Prentice Hall. ISBN 0131177052.
網站
"糟糕程式碼探測指南". Diomidis Spinellis. 檢索於 2011-03-09.
[編輯 | 編輯原始碼]
華夏公益教科書