反面模式
在軟件工程中,反面模式(anti-pattern或antipattern)指的是在實踐中經常出現但又低效或是有待優化的設計模式[1][2],是用來解決問題的帶有共同性的不良方法。它們已經經過研究並分類,以防止日後重蹈覆轍,並能在研發尚未投產的系統時辨認出來。
Andrew Koenig在1995年造了anti-pattern這個詞[3],靈感來自於GoF的《設計模式》一書。而這本書則在軟件領域引入了「設計模式」(design pattern)的概念[4]。三年後antipattern因《AntiPatterns》這本書而獲得普及,而它的使用也從軟件設計領域擴展到了日常的社會互動中。按《AntiPatterns》作者的說法,可以用至少兩個關鍵因素來把反面模式和不良習慣、錯誤的實踐或糟糕的想法區分開來:
- 行動、過程和結構中的一些重複出現的乍一看是有益的,但最終得不償失的模式
- 在實踐中證明且可重複的清晰記錄的重構方案
很多反面模式只相當於是錯誤、咆哮、不可解的問題、或是可能可以避免的糟糕的實踐,它們的名字通常都是一些用反話構成的詞語。有些時候陷阱(pitfalls)或黑色模式(dark patterns)這些不正式的說法會被用來指代各類反覆出現的糟糕的解決方法。因此,一些有爭議的候選的反面模式不會被正式承認。
這個概念很容易推廣到工程學以及工程以外需要人們付出努力去爭取的領域。儘管在工程學以外很少用到這個術語,但其概念是通用的。
舉例
社會和組織結構
組織結構
- 分析癱瘓(Analysis paralysis):花費太多精力在項目的分析階段
- 腳踏車棚(Bicycle shed):對於一些不重要的事務花上不成比例的精力
- 超前沿技術(Bleeding edge,刀鋒):採用一些未經測試和/或尚不穩定的前沿技術來運營,從而導致成本超支、表現/性能不佳,和/或交付延期。
- 搖錢樹(cash cow):假如舊產品的盈利能力強,通常會導致企業沒有動力開發新產品
- 委員會設計(Design by committee):很多人同時進行設計,卻沒有統一的看法
- 承諾升級(Escalation of commitment):明知錯了還不能收回之前的決定
- 目標管理(Management by objectives):通過數字管理,過於關注非本質而或不易取得的數字指標
- 道德風險(Moral hazard):不讓做決定的人知道他的決定會帶來什麼結果
- 蘑菇管理(Mushroom management):不通知或是錯誤地通知雇員信息。雇員像蘑菇一樣在黑暗中吸取養分,自生自滅
- 海鷗管理(Seagull management):只有當出現問題的時候管理人員才會跟雇員進行接觸和互動的管理模式。典型的場景就是,海鷗式的管理人員「飛」過來,嘁嘁喳喳,是人都批評一通,爾後「飛」走了!
- 煙囪式管理(Stovepipe or Silos,豎井式/發射井式/穀倉式管理):組織結構是由若干彼此孤立的團隊組成,並且整個組織結構的範圍內,上下溝通交流能夠有效進行,而水平/橫向的則不然。結構上支持數據主要在上下方面的流動,卻禁止跨部門的通信。
- 廠商陷阱(Vendor lock-in,供應商套牢,供應商陷阱,廠商泥潭):使一個系統過於依賴於外部所提供的組件/部件。
項目管理
- 死亡征途(Death march,死亡之旅):除了CEO,每個人都知道這個項目會成為一場災難,但是真相卻被隱瞞下來,以免項目被立即取消。(儘管CEO通常知道並且仍然繼續試圖最大化利潤。)然而,真相被隱藏起來,直到大限來臨("Big Bang")。另一種定義:雇員由於不合理的deadline,被迫在深夜和周末加班。
- 團隊思維(Groupthink):在團隊思維中,團隊成員避免提出在一致觀點之外的思維。
- 九九定律(Ninety-ninety rule):當項目「幾近完成」時,低估完成項目所需時間的傾向。
- 過度設計(Overengineering):花費資源完成比實際需要的還要複雜的工程
- 障眼法(Smoke and mirrors):展示還沒實現的功能,就像它們已經實現了一樣
分析方式
- 旁觀冷漠(Bystander apathy):一個需求或者設計是錯的,注意到這一點的人卻不指出,因為這影響的是其他人。
軟件工程
軟件設計
- 抽象倒置(Abstraction inversion):不把用戶需要的功能直接提供出來,導致他們要用更上層的函數來重複實現
- 用意不明(Ambiguous viewpoint):給出一個模型(通常是面向對象分析與設計,面向對象分析與設計)卻沒有指出用意何在
- 大泥球(Big ball of mud):沒有清晰結構的系統
- 數據庫式進程間通信(Database-as-IPC):使用數據庫進行進程間通信,而不使用更輕量級的合適的機制。或者說,對於常規的進程間通信,不是去採用輕量得多的合適機制,而是將數據庫用作消息隊列。
- 鍍金(Gold plating):在項目達到最高價值後還繼續工作。
- 內部平台效應(Inner-platform effect):系統可自定義的太多,以至於成為一個軟件開發平台的蹩腳的複製品。
- 輸入問題(Input kludge):無法確定和實現對異常輸入的處理
- 接口膨脹(Interface bloat):把一個接口做得過於強大以至於極其難以實現
- 魔力按鍵(Magic pushbutton):直接在接口的代碼里實作功能,而不維持其抽象性。這可能導致軟體的耦合性增加,彈性與可重覆使用性下降。
- 競爭風險(Race hazard):輸出結果受到事件執行順序和時機的影響,在多線程環境和分布式系統中可能發生
- 煙囪系統(Stovepipe system):過度聚集數據和功能,忽視了與其他系統和模塊的共享
面向對象設計
- 貧血的域模型(Anemic Domain Model):僅因為每個對象都要有屬性和方法,而在使用域模型的時候沒有加入非OOP的業務邏輯
- 調用父類(Call super):需要子類調用父類被重定義的方法
- 圓還是橢圓問題(Circle-ellipse problem):基於變量的子類化關係進行子類化
- 循環依賴(Circular dependency):在對象或軟件模塊中,直接或間接引入循環依賴。
- 常量接口(Constant interface):使用接口定義常量
- 上帝對象(God object):在設計的單一部分(某個類)集中了過多的功能
- 對象糞池(Object cesspool):復用那些不滿足復用條件的對象。對象池是一種管理對象的方法,在重複使用對象前,需要針對對象進行初始化,以避免上次使用後的狀態等數據影響下次的使用
- 不羈的對象(Object orgy):沒有成功封裝對象,外部可以不受限制地訪問它的內部
- 幽靈(Poltergeists):指這樣一些對象,它們唯一的作用就是把信息傳給其它對象
- 順序耦合(Sequential coupling):指這樣一些對象,它們的方法必須要按某種特定順序調用
- 悠悠問題(Yo-yo problem):一個結構(例如繼承)因為過度碎片化而變得難於理解
編程
- 偶然複雜度(Accidental complexity):向一個方案中引入不必要的複雜度
- 遠隔作用(Action at distance):意料之外的在系統分離的部分之間交互
- 船錨(Boat anchor):在系統中保留無用的部分
- 忙等待(Busy waiting):在等待的時候不斷占用CPU,通常是因為採用了重複檢查而不是適當的消息機制
- 緩存失敗(Caching failure):錯誤被修正後忘記把錯誤標誌復位
- 拜物編程(Cargo cult programming):由於對模式的盲目崇拜,在不理解的情況下就使用模式和方法,企圖得到好的結果
- 靠異常編程(Coding by exception):當有特例被發現時才添加新代碼去解決
- 隱藏錯誤(Error hiding):在顯示給用戶之前捕捉到錯誤信息,要麼什麼都不顯示,要麼顯示無意義的信息
- 硬編碼(Hard code):將對系統環境的假設寫入實現中
- 熔岩流(Lava flow):保留不想要的(冗餘的或是低質量的)代碼,僅因為除去這些代碼的代價太高或是會帶來不可預期的結果
- 循環-switch序列(Loop-switch sequence)在循環結構中使用switch語句來編寫連續步驟
- 魔術數字(Magic numbers):在算法里直接使用數字,而不解釋含義
- 魔幻字符串(Magic strings):直接在代碼里使用常量字符串,例如用來比較,或是作為事件代碼
- 自我複製(Repeating yourself):通過不斷複製已有代碼的模式或代碼段進行編碼;而非採用once and only once(抽取原則)
- 軟代碼(Soft code):在配置文件里保存業務邏輯而不是在代碼中
- 麵條代碼(Spaghetti code):指那些結構上完全不可理解的系統,尤其是因為誤用代碼結構
- 霰彈槍手術(Shotgun surgery):開發人員一次性在一個多個實現的代碼基中增加功能
方法論
- 拷貝粘貼編程(Copy and paste programming):拷貝(然後修改)現有的代碼而不是構造通用的解決方案
- 黃金大錘(Golden hammer):認為自己最喜歡的解決方案是到處通用的(參見:銀彈)
- 不可能因素(Improbability factor):認為已知的錯誤不可能發生
- 非我所創(Not invented here):拒絕使用組織外的主意或方案,但這也可能是出於版權等原因
- 這裡發明的(invented here):拒絕組織內部實現的創新或解決方案,通常因為對成員沒有信心
- 不成熟的優化(Premature optimization):在編碼的早期追求代碼的效率,犧牲了好的設計、可維護性、有時甚至是現實世界的效率
- 轉換編程法(Programming by permutation):也稱巧合編程,試圖通過連續修改代碼再看是否工作的方式來解決問題
- 重新發明方的輪子(Reinventing the square wheel):已經有一個很好的方案了,又再搞一個爛方案來替代它
- 銀彈(Silver bullet):認為自己最喜歡的技術方案能解決一個更大的問題
- 測試人員驅動開發(Tester driven development):需求來自bug報告的軟件工程
配置管理
- 依賴地獄(Dependency hell):所依賴產品的版本所導致的問題
- DLL地獄(DLL hell):不同版本DLL所帶來的問題,包括DLL可見性和多版本問題,在微軟的Windows上尤為突出
- 擴展衝突(Extension conflict):蘋果系統在Mac OS X版本之前的不同擴展的問題
- JAR地獄(JAR hell):JAR文件不同版本或路徑帶來的問題,通常是由於不懂類加載模型導致的
注釋
- ^ Budgen, D. Software design. Harlow, Eng.: Addison-Wesley. 2003: pp. 225 [2008-05-05]. ISBN 0-201-72219-4. (原始內容存檔於2015-04-14). "As described in Long (2001), design anti-patterns are 'obvious, but wrong, solutions to recurring problems'."
- ^ Ambler, Scott W. Process patterns: building large-scale systems using object technology. Cambridge, UK: Cambridge University Press. 1998: pp. 4 [2008-05-05]. ISBN 0-521-64568-9. (原始內容存檔於2015-04-14). "...common approaches to solving recurring problems that prove to be ineffective. These approaches are called antipatterns."
- ^ Koenig, Andrew. Patterns and Antipatterns. Journal of Object-Oriented Programming. March/April 1995, 8, (1): 46–48.
- ^ Rising, Linda. The patterns handbook: techniques, strategies, and applications. Cambridge, U.K.: Cambridge University Press. 1998: pp.387 [2008-05-05]. ISBN 0-521-64818-1. (原始內容存檔於2015-04-15).
參考文獻
- Perl設計模式–一部開放的線上書籍。
- Brown, William J.; Raphael C. Malveau, Hays W. McCormick III,和Thomas J. Mowbray. 反模式:软件重构、架构及项目危机. John Wiley & Sons. 1998. ISBN 978-0-471-19713-3.
- Laplante, Phillip A.; and Colin J. Neill. Antipatterns: Identification, Refactoring and Management. Auerbach Publications. 2005. ISBN 978-0-8493-2994-4.