設計模式簡介
每個模式描述了一個在我們周圍重複發生的問題,以及該問題的解決方案的核心。
---Chistopher Alexander
設計模式描述了軟體設計過程中某一類常見問題的一般性的解決方案。
面向對象設計模式藐視了面向對象設計過程中,特定場景下,類與互相通信的對象之間常見的組織關系。
GoF 23種設計模式
曆史性著作《設計模式:可複用面向對象軟體的》書中描述了
23種經典的瞄向對象設計模式,創立了模式在軟體設計的地位。
該書描述的23種經典設計模式又被人們成為GoF 23種設計 模式。
由于《設計模式:可複用面向對象軟體的基礎》一書确定了設計
模式的地位,人們通常所說的設計模式隐含地表示“面向對象設計模式”。
但這并不意味着“設計模式”就等于“面向對象設計模式”,也不意味着GoF23種設計模式就表示了所有的“面向對象設計模式”。除了“面向對象設計模式 ”外,還有其他設計模式。除了GoF 23種設計模式, 還有很多的面向對象設計模式。
GoF 23種設計模式是學習面向對象設計模式的起點,而非終點。
設計模式與面向對象
面向對象設計模式解決的是“類與互相通信的對象之間的組織關系,包括它們的角色、職責、寫作方式幾個方面。
面向對象設計模式是“好的面向對象設計”,所謂“好的面向對象設計”是那些可以滿足“因對變化,提高複用”的設計。
面向對象設計模式描述的是軟體設計,是以它是獨立于程式設計語言的,。但是面向對象設計模式最終實作仍然要使用面向對象語言來表達,本課程給予C#語言,但實際上它适合于支援 .NET架構的所有.NET語言,如:Visual Basic.NET、C++/CLI等。
面向對象設計模式不像算法技巧,可以照搬照用,它是建立在“面向對象”純熟、深入的了解的基礎上的經驗性認識。掌握面向對象設計模式的前提是首先找我“面向對象”!
從程式設計語言直覺了解面向對象
各種面向對象程式設計語言互相有别,但都能看到它們對面向對象三大機制的支援,即:“封裝、繼承、多态”
-封裝,隐藏内部實作
-繼承,複用現有代碼
-,多态,改寫對象行為
使用面向對象程式設計語言(C#),可以推動程式員以面向對象的思維來思考軟體設計結構,進而強化面向對象的程式設計範式。
C#是一門支援面向對象程式設計的優秀語言,包括:各種級别的封裝支援;單實作繼承+多接口實作;抽象方法與虛方法重寫。
但OOPL并非面向對象的全部
通過面向對象程式設計語言(OOPL)認識到的面向對象,并不是面向對象的全部,甚至隻是淺陋的面向對象。
OOPL的三大機制“封裝、繼承、多态”可以表達面向對象的所有概念。但這三種機制本身并沒有刻畫出面向對象的核心精神。換言之,既可以用這三大機制做出“好的面向對象設計”,也可以用這三大機制做出“差的面向對象設計”。不是使用了面向對象的語言(例如C#),就是實作面向對象設計與開發!是以不能依賴程式設計語言的面向對象機制,來掌握面向對象。
OOPL沒有回答面向對象的根本問題--我們 為什麼要使用面向對象?我們應該怎樣使用三大機制來實作“好的面向對象”?我們應該遵循什麼樣的面向對象原則?
任何一個嚴肅的面向對象程式員(例如C#程式員),都需要系統地學習面向對象的知識,單純從程式設計語言上獲得面向對象知識,不能夠勝任面向對象設計與開發。
從一個示例談起
示例場景:
我們需要設計一個人事管理系統,其中的一個功能是對各種不同類型的員工,計算其當月的工資--不同類型的員工,擁有不同薪金計算制度。
結構化做法
1.獲得人事系統中所有可能的員工類型
2.根據不同的員工類型所對應的不同薪金制度,計算其工資
enum EmployeeType{
Engineer;
Sales;
Manager;
……
}
//計算工資程式
if(type==EmployeeType.Engineer){
……
}
else if(type==Employee.Sales){
……
}
面向對象對象設計
1.根據不同員工類型設計不同的類,并使這些類繼承自一個Employee抽象類,其中有一個抽象方法GetSalary。
2. 在各個不同的員工類中,根據自己的薪金制度,重寫(override)GetSalary方法。
abstract class Employee{
……
public abstract int GetSalary();
}
class Sales:Empoyee{
……
public override int GetSalary()
{
……
}
}
class Engineer:Employee{
……
public override int GetSalary(){
……
}
}
//顯示工資程式
Employee e=emFactory。GetEmployee(id);
MessageBox.Show(e.GetSalary());
現在需要改變了……
示例場景:
随着客戶公司業務規模的拓展,又出現了更多類型的員工,比如鐘點工、計件工……等等,這對人事管理系統提出了挑戰--原有的程式必須改變。
結構化的做法
幾乎所有設計員工類型的地方(當然寶庫”計算工資的程式“)都需要做改變……這些代碼都需要重新編譯,重新部署……
面向對象的做法
隻需要在新的檔案裡添加的員工類,讓其繼承自Employee抽象類,并重寫GetSalary()方法,然後在EmployeeFactory.GetEmployee方法中條件,根據條件産生新的員工類型就可以了。其他地方(顯示工資程式、Enginer類、Sales類等)則不需要任何改變。
重新認識面向對象
對于前面的例子,從宏觀層面來看,面向對象的建構方式更能适應軟體的變化,能将變化的影響減為最小
從微觀層面來看,面向對象的方式更強調各個類的”責任“,新曾員工類型不會影響原來員工類型的實作代碼--這更符合真實的世界,也更能控制變化所影響的範圍,畢竟Engineer類不應為新增的”鐘點工“來買單……
對象是什麼?
-從概念層面講,對象是某種和擁有責任的抽象。
-從規格層面講,對象是一系列可以被其他對象使用的公共接口。
-從語言實作層面來看,對象封裝了代碼和資料。
有了這些認識後,怎樣才能設計"好的面向對象”?
-遵循一定的面向對象設計的原則
-熟悉一些典型的面向對象設計模式
從設計原則到設計模式
-針對接口程式設計,而不是針對實作程式設計
客戶無需知道使用對象的特定類型,隻需要知道對象擁有客戶所期望的接口。
-優先使用對象組合,而不是類繼承
類繼承通常為“白盒複用”,對象組合通常為“黑盒複用”。繼承在某種程度上破壞了封裝性,子類父類耦合度高;而對象咋喝則隻需要被組合的對象具有良好定義的接口,耦合度低。
-封裝變化點
使用封裝來建立對象之間的分界層,讓設計者可以在分界層的一側進行修改,而不會對另一側産生不良影響,進而實作層次間的松耦合。
-使用重構得到模式--設計模式應用不宜先入為主,上來就是用設計模式是對設計模式最大的誤用。沒有一步到位的設計模式,靈活軟體開發實踐提倡的“Refactoring to Patterns”是目前普遍公認的最好的使用設計模式的方法。
幾條更具體的設計原則
-單一職責原則(SRP)
一個類應該僅有一個引起它變化的原因。
-開放封閉原則
一個子產品應該是擴充的,但是不可以修改(對擴充開放,對更改封閉)
-Liskow替換原則(LSP)
子類必須能夠替換它們的基類
-依賴倒置原則(DIP)
高層子產品不應該依賴于底層子產品,二者都應該依賴于抽象。
抽象不應該依賴于實作細節,實作細節應該依賴于抽象
-接口隔離原則(ISP)
不應該強迫客戶程式依賴于它們不用的方法。