## 第6章 可以工作的類
> // checklist For Design in Construction
> 核對表:軟件構造中的設計
### 設計實踐
- 你已經做過多次迭代,并且從眾多嘗試結果中選擇最佳的一種,而不是簡單選擇第一次嘗試的結果嗎?
- 你嘗試用多種方案來分解系統,以確定最佳的方案嗎?
- 你同時用自上而下和自下而上的方法來解決設計問題嗎?
- 為了解決某些特定的問題,你對系統中的風險部分或者不熟悉的部分創建過原型、寫出數量最少的可拋棄的代碼嗎?
- 你的設計方案被其他人檢查了嗎(無論正式與否)?
- 你一直在展開設計,知道實施細節躍然紙上嗎?
- 你用某種適當的技術—比如說Wiki、電子郵件、掛圖、數碼相機、UML、CRC卡或者在大馬鞋注釋–來保留設計成果嗎?
### 設計目標
- 你的設計是否充分地處理了由系統架構層定義出并且推遲確定的事項嗎?
- 你的設計被劃分為層次嗎?
- 你對把這一程序分解成為子程序、包和類的方式感到滿意嗎?
- 你把對這個類分解成為子程序的方法感到滿意嗎?
- 類與類之間的交互關系是否已設計成最小化了?
- 類和子程序是否被設計為能夠在其他的系統中重用?
- 程序是不是易于維護?
- 設計是否精簡?設計出來的每一部分都是絕對必要的嗎?
- 設計中是否采用了標準的技術?是否避免使用怪異且難以理解的元素?
- 整體而言,你的設計是否有助于最小化偶然性和本質性的復雜度嗎?
> 6.1 類的基礎:抽象數據類型(ADTs)
### 使用ADT的益處
- 可以隱藏實現細節
- 改動不會影響到整個程序
- 讓接口能提供更多的信息
- 更容易提高性能
- 讓程序的正確性更加顯而易見
- 程序更具有自我說明性
- 無須在程序內到處傳遞數據
- 你可以像在現實世界中那樣操作尸體,而不用再底層實現上操作它
> 良好的類接口
### 好的抽象
- 類的接口應該展現一致的抽象層次?
- 一定要理解所實現的抽象是什么?
- 提供成對的服務?
- 把不相關的信息轉移到其他類中?
- 盡可能讓接口可編程,而不是表達語義?
- 謹防在修改時破壞接口的抽象?
- 不要添加于接口抽象不一致的公用成員?
- 同時考慮抽象性和內聚性?
> Good Encapsulation
### 良好的封裝
- 盡可能地限制類和成員的可訪問性?
- 不要公開暴露成員數據?
- 避免吧私用的實現細節放入類的接口中?
- 不要對類的使用者作出任何假設?
- 避免使用友元類?
- 不要因為一個子程序里僅使用公用子程序,就把它歸入公開接口?
- 要格外警惕從語義上破壞封裝性?
- 留意過于緊密的耦合關系?
> Containment (“has a” Relationships)
### - 包含(“有一個…”的關系)
- 通過包含來實現“有一個/has a ” 的關系?
- 在萬不得已時通過private 繼承來實現“有一個”的關系?
- 警惕有超過7個數據成員的類?
> Inheritance (“is a ” Relationship)
### 繼承(“是一個…”關系)
- 用public 繼承來實現”是一個“的關系?
- 要么使用繼承并進行詳細說明,要么就不要使用它?
- 遵循Liskov替換原則?
- 確保只繼承需要繼承的部分?
- 不要”覆蓋“一個不可覆蓋的成員函數?
- 把共用的接口、數據及操作放到繼承樹種盡可能高的位置?
- 只有一個實例的類是值得懷疑的?
- 只有一個派生類的基類也是值得懷疑的?
- 派生后覆蓋了某個子程序,但在其中沒有做任何操作,這種情況也是值得懷疑的?
- 避免讓繼承體系過深?
- 盡量使用多態,避免大量的類型檢查?
- 讓所有數據都是private ,而非protected
> Why Are There So Many Rules for Inheritance
### 為什么有這么多關于繼承的規則?
- 如果多個類共享數據而非行為,應該創建這些類可以包含的共用對象?
- 如果多個類共享行為而非數據,應該讓它們從共同的基類繼承而來,并在基類里定義共用的子程序?
- 如果多個類即共享數據也共享行為,應該讓它們從一個共同的基類繼承而來,并在基類里定義共用的數據和子程序?
- 當你想由基類控制接口時,使用繼承;當你想自己控制接口時,使用包含?
> Reasons to Create a Class
### – 創建類的原因
- 為現實世界中的對象?
- 為抽象的對象建模?
- 降低復雜度?
- 隔離復雜度?
- 隱藏實現細節?
- 限制變動的影響范圍?
- 隱藏全部數據?
- 讓參數傳遞更順暢?
- 建立中心控制點?
- 讓代碼更加容易復用?
- 為程序族做計劃?
- 把相關操作包裝到一起?
- 實現某種特定的重構?
> checklist For Class Quality
### 核對表:類的質量
### 抽象數據類型
1. 你是否把程序中的類都砍做事抽象數據類型了?是否從這個角度評估它們的接口?
### 抽象
- 類是否有一個中心目的?
- 類的命名是否恰當?其名字是否表達了其中心目的?
- 類的接口是否展現一致的抽象?
- 類的接口是否足夠抽象,使你能不必顧慮它是如何實現其服務的?你能把類看做黑盒子嗎?
- 類提供的服務是否足夠完整,能讓其他類無須動用其內部數據?
- 是否已從類中除去無關信息?
- 是否考慮過把類進一步分解為組件類?是否已盡可能將其分解?
- 在修改類是是否維持了其接口的完整性?
### 封裝
- 是否把類的成員的可訪問性降到最小?
- 是否避免暴露類中的數據成員?
- 在編程語言所許可的范圍內,類是否盡可能的對其他的類隱藏自己的實現細節?
- 類是否避免對其使用者,包括其派生類會如何使用它做了假設?
- 類是否不依賴于其他類?它是松散耦合的嗎?
### 繼承
- 繼承是否只用來建立”是一個“的關系?也就是說,派生類是否遵循了LSP原則?
- 類的文檔中是否記述了其繼承策略?
- 派生類是否避免了”覆蓋“不可覆蓋的方法?
- 是否把公用的接口、數據和行為都放到盡可能高的繼承層次中了?
- 繼承層次是否很淺?
- 基類中所有的數據成員是否都被定義為private而非protected的了?
### 跟實現相關的其他問題
- 類中是否只有大約7個或者更少的數據成員?
- 是否把類直接或間接調用其他類的子程序的數量減到最少了?
- 類是否只在絕對必要時才與其他的類相互協作?
- 是否在構造函數中初始化了所有的數據成員?
- 除非擁有經過測量的、創建淺層副本的理由,類是否都被設計為當做深層副本使用?
### 與語言相關的問題
- 你是否研究過所用編程語言里和類相關的各種特定問題?
### 中文要點:
- 類的接口應提供一致的抽象。很多問題都是由于違背該原則而引起的。
- 類的接口應隱藏一些信息――如某個系統接口、某項設計決策、或一些實現細節。
- 包含往往比繼承更為可取――除非你要對“是一個/is a”的有關系建模。
- 繼承是一種有用的工具,但它卻會增加復雜度,這有違于軟件的首要技術使命――管理復雜度。
- 類是管理復雜度的首選工具。要在設計類時給予足夠的關注,才能實現這一目標。
### English Key Points:
- Class interfaces should provide a consistent abstraction. Many problems arise from violating this single principle.
- A class interface should hide something—a system interface, a design decision, or an implementation detail.
- Containment is usually preferable to inheritance unless you’re modeling an “is a” relationship.
- Inheritance is a useful tool, but it adds complexity, which is counter to the Primary Technical Imperative of minimizing complexity.
- Classes are your primary tool for managing complexity. Give their design as much attention as needed to accomplish that objective.