Contents
SOLID Principle
Single Responsibility Principle
- 每個 class 單一職責,例如有一個 Journal class 負責日記的資料結構,如果要加入儲存功能,要另外設計 JournalManager class 而不是直接加在裡面,可以做到儲存的抽象化、模組化
- C++ 問題
explicit
class member function
Open-Closed Principle
- 對 class 開放擴充,關閉修改
Liskov Substitution Principle
- 要符合 subtype 的規則,否則不能使用繼承,使用繼承會造成不可預測的結果
- 若 A 不是 B 的 subtype,A 就不應該繼承自 B,例如 B 是長方形,提供
set_width()
、set_height()
,此時 A 是正方形,因為繼承後,正方形的set_width()
、set_height()
分別執行後,會造成結果與長方形的預期不同
- 若 A 不是 B 的 subtype,A 就不應該繼承自 B,例如 B 是長方形,提供
- Subtype Relation
- Covariance of arguments
- Contravariance of method parameter types in the subtype.
- Covariance of result
- Covariance of method return types in the subtype.
- Exception rule
- New exceptions cannot be thrown by the methods in the subtype, except if they are subtypes of exceptions thrown by the methods of the supertype.
- Pre-condition rule
- Preconditions cannot be strengthened in the subtype.
- Post-condition rule
- Postconditions cannot be weakened in the subtype.
- Invariant rule
- Invariants must be preserved in the subtype.
- Constraint rule
- Subtype constraints ensure supertype constraints.
- Covariance of arguments
參考資料
Interface Segregation Principle
- 介面只要有需要的,而不是包山包海,若留下來的代表是要實作,去掉可能不會實作的介面
Dependency Inversion Principle
- High-level module 的實作不該依賴 low-level module 的實作,兩者只該依賴抽象介面
- 介面不該依賴實作,實作才該依賴介面
UML Class Diagram
UML Sequence Diagram
Design Patterns Catalog
Design Patterns 如何解決問題
Design for Reuse
- Inheritance: subclass 透過繼承 parent class,來達到 Reusability
- 優點:
- 在 compile-time 就定義好 implementation,執行速度較快
- 簡單就能重複使用 parent class 的內容
- 缺點:
- 無法在 run-time 決定 implementation,彈性較低
- 繼承 parent class 所有內容,破壞封裝的原則,當 parent class 修改實作,subclass 就會跟著被迫改變,導致彈性低、降低重複使用性
- 優點:
- Object Composition: subclass 僅繼承介面,在 run-time 決定實作的 object
- 優點:
- 只繼承介面,不會破壞封裝原則,彈性較高
- 不會造成複雜的繼承關係,較容易管理
- 較容易設計出,每個物件單一職責
- 缺點:
- 要特別注意介面的設計,因為不能常改動
- 容易產生多個 object,要特別注意 object 之間的關係,object 之間要如何互動
- 優點:
- Delegation: 目的在於將 composition 接近 inheritance 的方便性。通常的做法是在 class 中擁有要 delegate 的 instance
- 優點:
- 如果要 delegate 的對象有相同的 type (繼承相同的介面),可以在 run-time 替換
- 缺點
- highly parameterized 的軟體較難理解,較適合簡單的情境
- 要有比較多的經驗來判斷是否適合使用 delegation,優先考慮標準的 pattern,例如:
State
、Strategy
、Visitor
- 優點:
- Parameterized Type: 在 compile-time 將 type 當作參數建立 object
- GoF 介紹的 design pattern 不會使用此技術
Design for Change
- 減少修改後的後果,確保使用特定的方式修改
- 以下列出各種設計缺失與對應的 pattern
- 使用特定的 class 建立 object
- 避免原因: 當 object 變多,統一建立 object 的方法,降低建立 object 的複雜度
- 相關 patterns:
Abstract Factory
、Factory Method
、Prototype
- 依賴於特定的 operation
- 避免原因: operation 互相依賴,避免 hard-coded request,降低加入其他 operation 的複雜度
- 相關 patterns:
Chain of Responsibility
、Command
- 依賴於硬體或軟體平台
- 避免原因: 若依賴於特定平台,會難維護與升級,要降低系統依賴
- 相關 patterns:
Abstract Factory
、Bridge
- 依賴於 object 內部行為與實作
- 避免原因: 當 object 內部修改,若依賴內部實作,client 需要重新修改
- 相關 patterns:
Abstract Factory
、Bridge
、Memento
、Proxy
- 依賴於演算法內部實作
- 避免原因: 在開發中演算法常常會擴充、最佳化、重複使用,若依賴內部實作,當演算法內部修改,client 需要重新修改
- 相關 patterns:
Bridge
、Iterator
、Strategy
、Template Method
、Visitor
- 緊密耦合
- 避免原因: 耦合程度高,造成 monolithic system,難以修改、維護、學習,使用 abstract coupling、layering 降低耦合
- 相關 patterns:
Abstract Factory
、Bridge
、Chain of Responsibility
、Command
、Facade
、Mediator
、Observer
- 使用繼承實作來擴充功能
- 避免原因: 需要理解 parent class 的內部實作,可能會不必要的覆蓋 parent class 的實作。比較好的做法是,只繼承介面
- 相關 patterns:
Bridge
、Chain of Responsibility
、Composite
、Decorator
、Observer
、Strategy
- 無法方便的修改 class
- 避免原因: 可能會造成修要修改一堆 subclass
- 相關 patterns:
Adapter
、Decorator
、Visitor
- 使用特定的 class 建立 object
如何設計 Application
- 設計重點: 考慮 object 在內部重複使用、維護性、擴充性
- Design Pattern 解決方式: 降低耦合、封裝和獨立單一功能
如何設計 Toolkit
- Toolkit: 提供給 Application 的 general purpose 函示庫,例如 C++ STL
- 設計重點: 提供 general purpose 介面、提供給 Application 容易使用且彈性
如何設計 Framework
- Framework: 提供給 Application 架構的 specific domain 函示庫,例如 Qt
- 設計重點: 提供架構設計的重複使用、文件說明架構與使用的 pattern
Run-time、Compile-time 架構與物件的關係
- Aggregation: object 負責其 aggregate object 的一部分功能,每個相關的 object 都有獨立的 lifetime。例如,在 C++ 中的 member variables 聚合而成 aggregation object
- Acquaintance (aka Association、Using relationship): 較弱的關係,object 之間不互相負責,較容易變動。例如,在 C++ 中的 member variables (通常是 pointer 或 reference),且可能在 run-time 指向其他 instance
- 有些 design patterns 特別設計在 compile-time 和 run-time 的架構,例如,
Composite
、Decorator
特別用在建構複雜的 run-time 架構;Observer
參與 run-time 時的架構;Chain of Responsibility
在繼承物件之間溝通 - run-time 架構,若不知道相關的模式會較難理解
如何選擇 Design Pattern
- 大方向了解 Creational、Structural、Behavioral 分別的用處
- 參考 GoF Chapter 1.4 對各 Pattern 的概述
- 思考模組之間如何互動,參考 Pattern 之間的關聯
- 思考如何避免將來的重新設計
- 思考將來可能會有甚麼改變
- 常見的用法
Creational Pattern
- Creational Pattern 對 class 的 Realization 的過程進行抽象,能將 Module 的創建和使用分離
Simple Factory
- 模式動機: 想使用特定的子類別,但不想用該子類別的名字,而用父類別的名字加上特定參數
- 模式定義: 又稱 Static Factory Method。定義一個 class,根據參數返回特定 class 的 instance,被創建的 class 通常有相同的父類別
- 適用情況: 產生的 class 種類較少
Factory Method Pattern
- 模式動機: 不設計一個統一的工廠來負責所有產品的創建,能將工廠和產品抽象化
- 模式定義: 工廠父類別負責定義創建產品的介面,產生統一的產品介面,需要再個別對工廠和產品實作
- 適用情況: 不需要知道產品類別名,也可以不需要知道對應的工廠類別名 (需要時再動態指定),產品等級結構單一
- 實際應用: JDBC
|
|
Abstract Factory Pattern
- 模式動機: 工廠往往會產生多種不同等級結構和產品族,例如鴻海工廠,生產不同品牌的手機 (不同等級結構),生產不同的電器 (不同產品族),Factory Method 無法有效率的處理
- 模式定義: 又稱作 Kit 模式。工廠提供生產某產品族 (手機) 的介面,根據不同的等級結構 (Google 品牌) 各別實作對應的工廠,產生出特定的產品
- 適用情況:
- 系統有多個產品族,每次只使用其中每一產品族
- 同產品族的產品會一起使用
- 實際應用: 更換桌面主題,相關的按鈕、背景等一起改變
實作練習
Sigleton
1.2.2 Example 1 - FileRepository Class
- C++ 問題
static
functionunique_ptr
使用 get 的行為? 使用 Sigleton 時,要回傳 pointer、smart pointer、reference?
1.2.3 Example 2 - OpenGL GLFW Keyboard Manager
- C++ 問題
::
與 lambda 的用法?- lambda 中 capture, parameter 的差異
1.2.4 Example 3 - Generic Singleton
- C++ 問題
delete
default
final
member functionprivate
default
constructorfriend
member in classprotected
member in class- 繼承 template class
Named constructor
- is a static factory method
Factory Method
- 避免 hardcoding class name,常見的做法是,factory 根據參數回傳 derived class instance
Polymorphic Factory
- registry based factory
- C++
- 繼承 Factory base class,使用 template class 來包裝成 ConcreteFactory,當使用此 template 就會自動註冊新的 ConcreteFactory instance 到 Factory 的 static map
- 使用 Anonymous namespace 來宣告不能直接存取的 static object
- 使用 macro 來註冊 ConcreteFactory instance
Builder (Joshua Blosh)
- 跟 GoF 的 builder pattern 不同
- UserData 和 UserBuilder 分離,Builder 提供建立資料的介面,回傳 Data
- C++
friend
class in public section
1 2 3 4 5
class UserData { public: // Allow builder class access UserData's private data friend class UserBuilder; };
- 可以直接使用
UserBuilder()
而沒有建立 instance?
Structural Design Pattern
- 方便在既有的 Component 擴充功能,而不修改 Component Class
Decorator
- Component 的共同屬性獨立出來,放在 Decorator 中,Decorator 可修改不同種 Component 特定的屬性
1.6.2 Example
- C++
- Decorator 會擁有 Component 的
shared_ptr
make_shared
與繼承的關係?[]{}()
是甚麼?
- Decorator 會擁有 Component 的
Behavioral Design Pattern
- 模式動機:
- 模式定義:
- 適用情況:
- 實際應用:
實作練習
Комментарии