物件導向設計原則:solid + di
TRANSCRIPT
寫出更物件導向的程式小朱
C# 深耕系列 (2) – Part 1
物件導向不是只有封裝、繼承與多型 SOLID DI 其他你可能聽過的…
Agenda
物件導向不是只有封裝、繼承與多型大家都知道,但兩個都懂的人卻不一定寫得出相同結構品質的程式碼。
高內聚 (High Cohesion) 低耦合 (Low Couple) 不重覆程式 (Don’t Repeat Yourself) – DRY 二進位層次的擴充性
物件導向程式應有的特性…
封裝 (Encapsulation)◦ 最小知識原則 (Principles of Least Knowledge) 。
繼承 (Inheritance)◦ 複製被繼承 ( 父物件 ) 的行為,達到可重覆使用。
多型 (Polymorphism)◦ API 獨立,但實作 API 的物件可隨時抽換。
讓我們複習一下…
將物件的細節包裝起來。 外面不需要了解它怎麼做,就能用它開放的方法來得到預期的結果。 排序演算法或搜尋演算法。 集合物件 (Collections) 。
封裝
由程式碼層次取得被繼承 ( 父 ) 物件的絕大多數能力或特徵。
繼承 ( 子 ) 物件可視需求重寫以變更原本的行為。 Stream 物件。
繼承
對外界而言介面相同,但是外界呼叫介面時,其反應由實作該介面的物件決定。 抽象化的重要概念。 雜湊演算法、加解密演算法。 多型是 Design Patterns 的基石。
多型
看起來很好啊,有什麼問題嗎?
SOLID物件導向的使用不難,但怎麼用對,其實比你想像中難。
S O IL DSRP單一職責原則
OCP開放封閉原則
LSP里氏替換原則
ISP介面區隔原則
DIP相依反轉原則
一個類別應有一個,也有一個修改它的理由。 職責劃分。 也可應用於屬性與方法的設計。
單一職責原則
+ CalcPay+ ReportHours+ W riteEmployee
EmployeePayroll
+ CalcPay
Em ployeePayroll
ReportWriter
EmployeeRepository
軟體的實體應該對擴充開放,但對修改封閉。 擴充是添加新程式,而非修改舊程式。 抽象化程度的考驗。 API 的穩定度。
開放封閉原則
Client Server
Client AbstractServer
ConcreteServer
子類別與必須能夠平順無問題的以父類別替代。 外界不應該也不需要知道兩者的差異。 抽象類別與介面的使用。 不可以違反開放封閉原則。
◦ 程式內不能以判斷型別的方式決定父或子類別。
里氏替換原則
Square
Rectangle
+S etH e igh t()+S etW id th()
-he ight : rea l-w id th : rea l
用戶端不應該直接相依於它不會用到的介面。介面區隔原則
Client 3Client 2Client 1
Server
+ C1Function()+ C2Function()+ C3Function()
Client 3Client 2Client 1
Client 3InterfaceClient 2InterfaceClient 1Interface«interface» «interface»«interface»
Server
+ C1Function()+ C2Function()+ C3Function
+ C1Function() + C2Function() + C3Function()
高階模組不應相依於低階的模組,兩者應同時相依於抽象。 抽象不應相依於細節,細節應該相依於抽象。 多型的基石, Design Patterns 的入門磚。
相依反轉原則
Copy
Reader W riter
KeyboardReader PrinterW riter
ReadKeyboard
Copy
W ritePRinter
V.
S O IL DSRP單一職責原則
OCP開放封閉原則
LSP里氏替換原則
ISP介面區隔原則
DIP相依反轉原則
Dependency Injection你知道用程式碼擴充,但未來的世界是二進位擴充。( 其實現在已經是了… )
服務指的是提供特定功能的軟體元件 (software component) 。
特定功能的規格由介面定義。 物件可使用不同的服務聚合 (composite) ,來擴充自己的功能。 現代軟體架構上多採用這種作法。
服務 (Service)
在執行期 (runtime) 以二進位執行碼的方式,於物件需要時將服務 (service) 注射 (injection) 到物件內,讓物件能存取其功能。幫助你落實 DIP 原則的重要工具。
什麼是 DI?
DI 的核心元件,負責管理物件的生成與生命週期。1. 服務向 DI 容器註冊可用的服務 ( 介面 ) 及其對應的實作。2. 物件向 DI 容器 “要求” 服務。
3. DI 容器生成服務或回傳現有服務給物件。4. DI 容器會處理生命週期的工作。
DI 容器
建構式注入 (Constructor Injection) 屬性注入 (Property Injection) 介面注入 (Interface Injection)
如何注入服務 ?
建構式注入
屬性注入
使用介面內宣告服務的方式來注入。通常是用於建立 contract ,要求物件一定要使用特定服務。 實際運作多半還是採用建構式注入或屬性注入法。
介面注入
類似於 DI 的作法,但它不會管理物件的生命週期,而是將物件的要求丟給已存在的服務個體。適用於網路上的服務 (ex:
WCF)
Service Locator (Design Patterns)
Autofac StructureMap Castle Windsor Unity Ninject Spring.NET
DI Frameworks
如果你想 DIY 的話… Building an IoC container in 15 lines of code
SOLID 是物件導向必須內化的重要心法。◦ 有了它才寫得出夠 qualify 的程式碼。◦減少被 WTF 的次數
DI 是發展現代化軟體架構與框架的核心概念。◦ ASP.NET Core, Xamarin 都用它了。◦ 別再逃避它了
總結
https://en.wikipedia.org/wiki/SOLID_(object-oriented_design) https://en.wikipedia.org/wiki/Dependency_injection http://martinfowler.com/articles/injection.html Book: 軟體構築美學 (Brownfield Application Development in .NET) Book: 敏捷軟體開發:原則、樣式及實務 (Agile Software Development: Principles,
Patterns, and Practices) Book: 重構 - 改善既有程式的設計 (Refactoring: Improving the design of existing
code) eBook: .NET 相依性注入 (https://leanpub.com/dinet)
Reference
Thank you