與軟體複雜性的永無止境的鬥爭
什麼是複雜性?
最近讀完了《軟體設計哲學》,第二章探討了軟體複雜性的議題。
《軟體設計哲學》一書實際定義了複雜性:
「複雜性是指與軟體系統的結構相關的任何使其難以理解和修改的事物。」
換句話說,複雜性可以有多種形式,並且不一定與效能有任何關係,您的程式碼可以具有高效能但仍然很複雜
我想在本文中分享本書中的一些關鍵定義和見解。但首先,讓我們想像一個您可能已經經歷過的常見情況...
一個簡短的恐怖故事
讓我們深入探討許多人可能已經經歷過或將要經歷的恐怖故事。
它始於一個簡單的 CRUD 任務管理應用程式。程式碼乾淨、模組化且易於維護。開發團隊很高興,系統對於最初的客戶來說運作得很好。
當銷售團隊將系統出售給一家大公司時,問題就開始了,聲稱它具有日曆整合、電子郵件通知和令人驚嘆的報告產生器。銷售完成後,這些功能必須快速實施。
日曆整合:團隊必須與 Google 日曆和 Outlook 整合。不同的開發人員實施了解決方案,導致方法不一致。
電子郵件通知:接下來新增了電子郵件通知。一位開發人員使用特定的庫,而另一位開發人員創建了自訂解決方案。混合的方法使程式碼變得混亂。
報告產生器: 對於報告產生器,開發人員使用了各種技術:PDF、Excel 匯出和互動式儀表板。缺乏統一的方法使維護成為一場噩夢。
複雜性不斷增加:每個功能都是獨立快速開發的,導致功能之間存在依賴關係。開發人員開始創建「快速修復」以使一切正常運行,從而增加了系統的複雜性和耦合性。
軟體開發不是在真空中進行的;而是在現實中進行的。各種內部和外部因素都會對其產生影響。我們都曾經或將會經歷過這樣的情況。
結束的開始
然後問題就開始了:
- 系統某一部分的變更意外地影響了其他部分。
- 小改動需要修改許多其他文件,使得估計變得困難。
- 月復一月,程式碼變得越來越難以理解,通常透過反覆試驗來修復。
- 生產力下降,每個人都害怕維修任務。
- 不可避免地要求「我們需要重構。」
- 某些任務只能由特定的開發人員處理(經典)
- 隨著時間的推移,曾經編寫精美且文檔齊全的軟體變成了火車殘骸。
命名症狀
很明顯,我們現在擁有一個複雜的系統。
現在讓我們「剖析」這種複雜性,以便更容易識別和緩解它。
嗯,「緩解」的意思是:
「減輕嚴重性、嚴重性或痛苦;緩解。」
我相信複雜性通常是程式碼所固有的。有些事情本質上是複雜的。作為開發人員,您的角色不僅僅是創建電腦可以有效執行的程式碼,還要創建未來的開發人員(包括未來的您)可以使用的程式碼。
「控制複雜性是電腦程式設計的本質。」
— 布萊恩·科尼漢
上述書籍的作者指出,複雜性通常以三種方式表現出來,我們將在這裡進行探討。
改變放大
當看似簡單的更改需要在許多不同的地方進行修改時,就會發生更改放大。
例如,如果產品負責人要求「優先順序」或「完成日期」字段,並且您的實體緊密耦合,那麼您需要進行多少更改?
認知負荷
認知負荷是指開發者完成任務所需的知識量和時間。
想像一下這樣的場景:一位新開發人員加入了團隊,他被指派去修復報告產生器中的錯誤。為了完成此任務,開發人員需要:
- 了解不同的日曆整合(Google 和 Outlook)。
- 掌握電子郵件通知的不同方法。
- 瀏覽報告產生器的碎片程式碼,處理 PDF、Excel 和儀表板。
- 整合這些不同的技術和風格來尋找並修復錯誤。
這是典型的「無法估計」場景,任務可能需要 1 分或 8 分——最好擲 D20 並做出相應的反應。
未知的未知
未知的未知是指你不知道自己不知道的事。
這是複雜性最糟糕的表現,因為你可能會改變不應該改變的東西,導致一切崩潰。
範例:開發人員修改了電子郵件發送程式碼以新增通知,但沒有意識到這會影響依賴該函數的報告產生器。這給客戶帶來了重大問題,體現了緊急複雜性的最壞形式。
複雜性的原因
看完恐怖故事和三個主要症狀,讓我們看看是什麼導致了複雜性。
1. 依賴關係
依賴關係在軟體中是必不可少的,並且無法完全消除。它們允許系統的不同部分相互作用並一起運行。然而,如果管理不當,依賴關係會顯著增加複雜性。
定義:
當程式碼無法單獨理解或修改時,就存在依賴關係,需要考慮或修改相關程式碼。
依賴項類型:
- 直接:模組A直接依賴模組B。
- 傳遞性:模組 A 依賴模組 B,模組 B 又依賴模組 C。
- 循環:模組A、B、C以循環方式相互依賴。
2. 默默無聞
當重要資訊不明顯時,就會出現模糊性。這可能會使程式碼庫難以理解,從而導致認知負荷增加和未知未知的風險。
定義:
當重要資訊不明顯時,就會出現模糊。
晦澀難懂的例子:
- 命名不當:名稱不明確的變數和函數。
- 隱藏的副作用:執行意外操作的方法。
- 全域狀態:過度使用全域變數。
- 深度繼承: 行為分佈在類別層次結構中的多個層級。
請記住:複雜性是漸進的
- 複雜性很少是由單一「錯誤」或錯誤的決定引起的。
- 隨著時間的推移,錯誤的決策和依賴性會「緩慢」地增加複雜性。
因為它是增量的,所以很容易想到,「就這一次,沒關係。」但累積起來後,僅修復一兩個依賴項並不會產生太大影響。
「軟體工程中的一切都是權衡。」
— 我不記得作者了
結論
我可以寫很多你可能已經在網路上看到的關於如何避免複雜性的規則、策略和框架:SOLID、設計模式、YAGNI、KISS 等。
但是,您可以將它們全部統一為一個指導原則(如“務實的程式設計師”中提到的。):「我正在實現的內容容易更改嗎?」如果答案是否定的,那麼您可能正在增加複雜性。
確保程式碼易於更改可以簡化維護,減少開發人員的認知負擔,並使系統更具適應性且不易出錯。
謝謝!
以上是與軟體複雜性的永無止境的鬥爭的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

Python更適合初學者,學習曲線平緩,語法簡潔;JavaScript適合前端開發,學習曲線較陡,語法靈活。 1.Python語法直觀,適用於數據科學和後端開發。 2.JavaScript靈活,廣泛用於前端和服務器端編程。

JavaScript在Web開發中的主要用途包括客戶端交互、表單驗證和異步通信。 1)通過DOM操作實現動態內容更新和用戶交互;2)在用戶提交數據前進行客戶端驗證,提高用戶體驗;3)通過AJAX技術實現與服務器的無刷新通信。

JavaScript在現實世界中的應用包括前端和後端開發。 1)通過構建TODO列表應用展示前端應用,涉及DOM操作和事件處理。 2)通過Node.js和Express構建RESTfulAPI展示後端應用。

理解JavaScript引擎內部工作原理對開發者重要,因為它能幫助編寫更高效的代碼並理解性能瓶頸和優化策略。 1)引擎的工作流程包括解析、編譯和執行三個階段;2)執行過程中,引擎會進行動態優化,如內聯緩存和隱藏類;3)最佳實踐包括避免全局變量、優化循環、使用const和let,以及避免過度使用閉包。

Python和JavaScript在開發環境上的選擇都很重要。 1)Python的開發環境包括PyCharm、JupyterNotebook和Anaconda,適合數據科學和快速原型開發。 2)JavaScript的開發環境包括Node.js、VSCode和Webpack,適用於前端和後端開發。根據項目需求選擇合適的工具可以提高開發效率和項目成功率。

C和C 在JavaScript引擎中扮演了至关重要的角色,主要用于实现解释器和JIT编译器。1)C 用于解析JavaScript源码并生成抽象语法树。2)C 负责生成和执行字节码。3)C 实现JIT编译器,在运行时优化和编译热点代码,显著提高JavaScript的执行效率。

Python更適合數據科學和自動化,JavaScript更適合前端和全棧開發。 1.Python在數據科學和機器學習中表現出色,使用NumPy、Pandas等庫進行數據處理和建模。 2.Python在自動化和腳本編寫方面簡潔高效。 3.JavaScript在前端開發中不可或缺,用於構建動態網頁和單頁面應用。 4.JavaScript通過Node.js在後端開發中發揮作用,支持全棧開發。

JavaScript在網站、移動應用、桌面應用和服務器端編程中均有廣泛應用。 1)在網站開發中,JavaScript與HTML、CSS一起操作DOM,實現動態效果,並支持如jQuery、React等框架。 2)通過ReactNative和Ionic,JavaScript用於開發跨平台移動應用。 3)Electron框架使JavaScript能構建桌面應用。 4)Node.js讓JavaScript在服務器端運行,支持高並發請求。
