讀書筆記:A Philosophy of Software Design (一)

今天一位同事在斯坦福的博士生導師John Ousterhout (注,Tcl語言的設計者)來公司作了他的新書《A Philosophy of Software Design》的演講,介紹了他對於軟件設計的思考。這裏我把本書的讀書筆記和心得分享給你們,歡迎你們來和我交流探討。html

你們也能夠去看做者在google演講時的視頻和他演講的slides程序員

複雜性的本質

軟件設計應該簡單,避免複雜,關於複雜性的定義,做者認爲主要有兩個量度數據庫

  1. 系統是否是難以理解
  2. 系統是否是難以修改

關於複雜性的症狀:編程

  1. 當新增特性時,須要修改大量的代碼
  2. 當須要完成一個功能時,開發人員須要瞭解許多知識
  3. 當新增/修改功能時,不能明顯的知道要修改那些代碼

引發複雜性的緣由:依賴和晦澀。服務器

最後,複雜性不是忽然出現的,它是隨着時間和系統的演進逐漸增長的。架構

個人解讀:這本書講的是軟件設計的哲學,哲學要解決的是最根本的問題。做者認爲軟件設計要解決的最根本的問題就是避免複雜性,依賴和晦澀是形成軟件負責的主要緣由。依賴不少時候是沒法避免的,可是應該儘量的減小依賴,去除沒必要要的依賴。軟件設計應該容易理解,晦澀是引發複雜性增長的另外一個緣由。這個核心觀點是這本書的主旨,借用老愛的話「Simple,but not simpler!」less

我曾經就任某存儲巨頭,其中有一塊代碼由於是收購的產品,代碼已經很是陳舊了,由於沒有人能看懂,因此也就沒有人敢修改。你看,這個產品不是也賣的挺好的。 ide

僅僅可工做的代碼還遠遠不夠

在第二章,做者提出了「戰術性編程」「戰略性編程」的對立。微服務

「戰術性編程」最求以最快的速度完成可工做的功能。這看上去無可厚非。可是這種行爲每每會增長系統的複雜性。引起大量的技術債。能夠說這種作法以犧牲長遠利益來得到眼前的利益。google

「戰略性編程」不只僅要求可工做的代碼,以好的設計爲重,將來的功能投資,認爲現階段在設計上的投入會在未來得到回報。

好的設計是有代價的,問題是你願意投入多少?

個人解讀:頗有趣的是,我司以前的產品的負責人在公司推行大規模的敏捷(LeSS),當時有一個顧問給咱們上課,他也說設計要儘量簡單,可是不要爲了將來作設計。以最小的代價實現可用的功能。以John的觀點,這樣作無疑會增長系統變複雜的可能性。我比較認同John這裏的觀點,好的設計是有價值的,投入在軟件設計上的,對功能毫無影響的東西,是有價值的。可是如何取捨和權衡,投入多少是須要開發團隊達成共識。 軟件有它的生命週期,爲了將來的投入也不是越多越好。

模塊要有深度

深度實際上是對模塊封裝的度量,模塊應該提供儘量簡單的接口和儘量強大的功能。這樣的模塊稱之爲深度模塊。

個人解讀:這一部分沒有什麼新東西,傳統的面向對象和現在的微服務架構都是對這一哲學的應用。好的封裝能夠減小依賴,簡單的接口能夠避免晦澀。也就是減小了複雜性。

信息的隱藏和泄漏

關於信息的隱藏和泄漏,這一部分對於熟悉面向對象的猿們來講不是新東西。基於SOLID,這就是Open,軟件應該是對於擴展開放的,可是對於修改封閉的。信息隱藏使得修改變的封閉。

具備通用功能的模塊更具深度

更通用功能的接口意味着更高層級的抽象,隱藏更多的實現細節,按照John的觀點,也就更具深度。那麼如何在通用接口和特殊接口之間作權衡呢?

  1. 可以實現所需功能的最簡單接口是什麼?
  2. 該接口會被用於那些不一樣場景?
  3. 該接口對於個人當前是否容易使用?

個人解讀:通用的接口和以前的「戰略性編程」是一致的,更通用的接口在面對將來可能發生的需求變化的時候,更容易使用。這裏的藝術在於可以找到需求到軟件接口之間的最佳映射。抽象到哪個層級,是主要問題。

不一樣的層,不一樣的抽象

軟件系統一般有不一樣的層次組成,每一層都經過和它之上和之下的層的接口來交互。每一層都具備本身不一樣的抽象。例如典型的數據庫,服務器和客戶端模型中,數據庫層的抽象是數據表和關係,服務器層是應用對象和應用邏輯而客戶端的抽象是用戶接口視圖和交互。若是你發現不一樣的層具備相同的抽象,那也許你的分層有問題。

把複雜性向下移

在軟件分層的鄙視鏈中,最高層是用戶,接着的一層的UI工程師,而後是後臺工程師,數據庫工程師,等等。用戶是上帝不能得罪,若是必定要在某個層次處理複雜性,那麼這個層次越低越好,反正苦逼程序員也不會抱怨,對得,就是這個道理。

合併仍是分離

「天下大事,分久必合,合久必分」。軟件設計中常常要問的問題就是這兩個功能模塊是合併好,仍是分開好?不管是合併仍是分離,目標都是下降複雜性,那麼把系統分離成更多的小的單元模塊,每個模塊都更簡單,系統的複雜性會下降麼?答案是不必定:

  • 複雜性可能來源於系統模塊的數量
  • 更多的模塊也許意味着須要額外的代碼來管理和協調
  • 更多的模塊可能帶來許多依賴
  • 更多的模塊可能帶來重複的代碼,而重複的代碼是惡魔

在如下的狀況下,須要考慮合併:

  • 模塊之間共享信息
  • 合併後的接口更簡單
  • 合併後減小了重複的代碼

確保錯誤終結

異常和錯誤處理是形成軟件複雜的罪魁禍首之一。程序員每每錯誤的認爲處理和上報越多的錯誤,就越好。這也就致使了過分防護性的編程。而不少時候,程序員捕獲了異常並不知道該如何處理,乾脆往上層扔,這就違背了封裝原則。

“funny error message”的图片搜索结果

用戶一臉懵逼,「你叫我幹啥?」

下降複雜度的一個原則就是儘量減小須要處理的異常可能性。而最佳實踐就是確保錯誤終結,例如刪除一個並不存在的文件,與其上報文件不存在的異常,不如什麼都不作。確保文件不存在就行了,上層邏輯不但不會被影響,還會由於不須要處理額外的異常而變得簡單。

今天就先分享到這裏,後面有空,我會繼續分享本書的後半部分,祝你們開學愉快!

相關文章
相關標籤/搜索