日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

面向对象基本原则

發布時間:2024/4/14 编程问答 84 豆豆
生活随笔 收集整理的這篇文章主要介紹了 面向对象基本原则 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

?

面向對象設計的原則是面向對象思想的提煉,它比面向對象思想的核心要素更具可操作性,但與設計模式相比,卻又更加的抽象,是設計精神要義的抽象概括。形象地將,面向對象思想像法理的精神,設計原則則相對于基本憲法,而設計模式就好比各式各樣的具體法律條文了。

面向對象設計原則有6個:開放封閉原則,單一職責原則,依賴倒置原則,Liskov替換原則,迪米特法則和接口隔離原則或合成/聚合復用原則(不同資料略有不同,這里對7都做了整理)。

?

1單一職責原則(Single?Responsibility?Principle?SRP)

??There?should?never?be?more?than?one?reason?for?a?class?to?change.?什么意思呢?

??所謂單一職責原則就是一個類只負責一個職責,只有一個引起變化的原因。

??如果一個類承擔的職責過多,就等于把這些職責耦合在一起,一個職責的變化會削弱或抑制這個類完成其他職責的能力,這個耦合會導致脆弱的設計。

軟件設計真正要做的許多內容,就是發現職責并把這些職責相互分離;如果能夠想到多于一個動機去改變一個類,那么這個類就具有多于一個職責,就應該考慮類的分離。

?

以調制解調器為例如下圖:

?

從上述類圖里面我們發現有四個方法Dial(撥通電話)Hangup(掛電話)Receive(收到信息)Send(發送信息),經過分析不難判斷出,實際上Dial(撥通電話)Hangup(掛電話)是屬于連接的范疇,而Receive(收到信息)Send(發送信息)是屬于數據傳送的范疇。這里類包括兩個職責,顯然違反了SRP。

這樣做有潛在的隱患,如果要改變連接的方式,勢必要修改Modem,而修改Modem類的結果導致凡事依賴Modem類可能都需要修改,這樣就需要重新編譯和部署,不管數據傳輸這部分是否需要修改。

因此要重構Modem類,從中抽象出兩個接口,一個專門負責連接,另一個專門負責數據傳送。依賴Modem類的元素要做相應的細化,根據職責的不同分別依賴不同的接口。如下圖:

?

這樣以來,無論單獨修改連接部分還是單獨修改數據傳送部分,都彼此互不影響。

?

總結單一職責優點:

降低類的復雜性,

提高可維護性

提高可讀性。

降低需求變化帶來的風險。需求變化是不可避免的,如果單一職責做的好,一個接口修改只對相應的實現類有影響,對其它的接口無影響,這對系統的擴展性和維護性都有很大的幫助。

2里氏替換原則(Liskov?Substitution?Principle?LSP

?

里氏替換原則是面向對象設計的基本原則之一。任何基類可以出現的地方,子類一定可以出現。LSP是繼承復用的基石,只有當子類可以替換基類,軟件單位的功能不受影響時,基類才能真正的被復用,而子類也可以在基類的基礎上增加新的行為。

Liskov提出了關于繼承的原則:Inheritance?should?ensure?that?any?property?proved?about?supertype?objects?also?holds?for?subtype?objects.----繼承必須確保超類中所擁有的性質在子類中仍然成立。2002年,軟件工程大師Robert?C.?Martin出版了一本《Agile?Software?DevelopmentPrinciples?Patterns?and?Practices》,在文中他把里氏代換原則最終簡化為一句話:“Subtypes?must?be?substitutable?for?their?base?types”也就是說子類必須能夠替換成他們的基類。

里氏替換原則講的是基類和子類的關系,只有這種關系存在的時候里氏替換原則才能成立。里氏替換原則是實現開放封閉原則的具體規范。這是因為:實現開放封閉原則的關鍵是抽象,而繼承關系又是抽象的一種具體實現。

?

我們大家都打過CS的游戲,用槍射擊殺人,如下類圖:

?

槍的主要職責是射擊,如何射擊在各個具體的子類中定義。注意在類中調用其他類時務必調用父類或接口,如果不能掉話父類或接口,說明類的射擊已經違反了LSP原則。

如果我們有一個玩具手?槍,該如何定義呢?我們先在類圖2-1上增加一個類ToyGun,然后繼承于AbstractGun類,修改后的類圖如下:

?

玩具槍是不能用來射擊的,殺不死人的,這個不應該寫shoot方法,在這種情況下業務的調用類就會出現問題。為了解決這個問題,ToyGun可以脫離繼承,建立一個獨立的父類,為了做到代碼可以服用,可以與AbstractGun建立關聯委托關系,如下圖:

?

因此,如果子類不能完整地實現父類的方法,那么建議斷開父子繼承關系,采用依賴,聚合,組合等關系代替繼承。

子類可以有自己的屬性或方法。

覆蓋或實現父類的方法時輸入的參數可以放大。

覆蓋或實現父類的方法時輸出結果可以被縮小。這是什么意思呢,父類的方法返回值是一個類型T,子類相同的方法(覆寫)的返回值為類型S,那么根據里氏替換原則就要求S必須小于等于T,也就是說要么ST是同一個類型,要么ST的子類型。

采用里氏替換原則的目的就是增加程序的健壯性,需求變更時也可以保持良好的兼容性和穩定性,即使增加子類,原有的子類可以繼續運行。在實際項目中,每個子類對應不同的業務含義,使用父類作為參數,傳遞不同的子類完成不同業務邏輯。

3依賴倒置原則(Dependence?Inversion?Principle?DIP?

?所謂依賴倒置原則就是要依賴于抽象,不要依賴于具體。簡單的說就是對抽象進行編程,不要對實現進行編程,這樣就降低了客戶與實現模塊間的耦合。

面向過程的開發,上層調用下層,上層依賴于下層,當下層劇烈變化時,上層也要跟著變化,這就會導致模塊的復用性降低而且大大提高了開發的成本。

面向對象的開發很好的解決了這個問題,一般的情況下抽象的變化概率很小,讓用戶程序依賴于抽象,實現的細節也依賴于抽象。即使實現細節不斷變化,只要抽象不變,客戶程序就不需要變化。這大大降低了客戶程序域實現細節的耦合度。

比如一個合資汽車公司現在要求開發一個自動駕駛系統,只要汽車上安裝上這個系統,就可以實現無人駕駛,該系統可以在福特車系列和本田車系列上使用。面向過程的結構圖:

?

實現代碼如下:

public?class?HondaCar

????{

????????public?void?Run()?{?Console.WriteLine("本田車啟動了!");?}

????????public?void?Turn()?{?Console.WriteLine("本田車拐彎了!");?}

????????public?void?Stop()?{?Console.WriteLine("本田車停止了!");?}

????}

????public?class?FordCar?

????{

????????public?void?Run()?{?Console.WriteLine("福特車啟動了!");?}

????????public?void?Turn()?{?Console.WriteLine("福特車拐彎了!");?}

????????public?void?Stop()?{?Console.WriteLine("福特車停止了!");?}

????}

????public?class?AutoSystem

????{

????????public?enum?CarType{?Ford,Fonda}

????????private?HondaCar?hondcar=new?HondaCar();

????????private?FordCar?fordcar=new?FordCar();

????????private?CarType?type;

????????public?AutoSystem(CarType?carType)

????????{

????????????this.type?=?carType;

????????}

????????public?void?RunCar()

????????{

????????????if?(this.type?==?CarType.Fonda)

????????????{

????????????????hondcar.Run();

????????????}

????????????else?if?(this.type?==?CarType.Ford)

????????????{

????????????????fordcar.Run();

????????????}

????????}

????????public?void?StopCar()

????????{

????????????if?(this.type?==?CarType.Fonda)

????????????{

????????????????hondcar.Stop();

????????????}

????????????else?if?(this.type?==?CarType.Ford)

????????????{

????????????????fordcar.Stop();

????????????}

????????}

????????public?void?TurnCar()

????????{

????????????if?(this.type?==?CarType.Fonda)

????????????{

????????????????hondcar.Turn();

????????????}

????????????else?if?(this.type?==?CarType.Ford)

????????????{

????????????????fordcar.Turn();

????????????}

????????}

????}

顯然這個實現代碼也可滿足現在的需求。

但是如何現在公司業務規模擴大了,該自動駕駛系統還要把吉普車也兼容了。這些就需要修改AutoSystem類如下:

public?class?AutoSystem

????{

????????public?enum?CarType{?Ford,Fonda,Jeep}

????????private?HondaCar?hondcar=new?HondaCar();

????????private?FordCar?fordcar=new?FordCar();

????????private?Jeep?jeep?=?new?Jeep();

????????private?CarType?type;

????????public?AutoSystem(CarType?carType)

????????{

????????????this.type?=?carType;

????????}

????????public?void?RunCar()

????????{

????????????if?(this.type?==?CarType.Fonda)

????????????{

????????????????hondcar.Run();

????????????}

????????????else?if?(this.type?==?CarType.Ford)

????????????{

????????????????fordcar.Run();

????????????}

????????????else?if?(this.type?==?CarType.Jeep)

????????????{

????????????????jeep.Run();

????????????}

????????}

????????public?void?StopCar()

????????{

????????????if?(this.type?==?CarType.Fonda)

????????????{

????????????????hondcar.Stop();

????????????}

????????????else?if?(this.type?==?CarType.Ford)

????????????{

????????????????fordcar.Stop();

????????????}

????????????else?if?(this.type?==?CarType.Jeep)

????????????{

????????????????jeep.Stop();

????????????}

????????}

????????public?void?TurnCar()

????????{

????????????if?(this.type?==?CarType.Fonda)

????????????{

????????????????hondcar.Turn();

????????????}

????????????else?if?(this.type?==?CarType.Ford)

????????????{

????????????????fordcar.Turn();

????????????}

????????????else?if?(this.type?==?CarType.Jeep)

????????????{

????????????????jeep.Turn();

????????????}

????????}

????}

通過代碼分析得知,上述代碼也確實滿足了需求,但是軟件是不斷變化的,軟件的需求也是變化的,如果將來業務又擴大了,該自動駕駛系統還有能實現通用、三菱、大眾汽車,這樣我們不得不又要修改AutoSystem類了。這樣會導致系統越來越臃腫,越來越大,而且依賴越來越多低層模塊,只有低層模塊變動,AutoSystem類就不得不跟著變動,導致系統設計變得非常脆弱和僵硬。

?

導致上面所述問題一個原因是,含有高層策略的模塊,如AutoSystem模塊,依賴于它所控制的低層的具體細節的模塊(如FordCar和HondaCar)。如果能使AutoSystem模塊獨立于它所控制的具體細節,而是依賴抽象,那么我們就可以服用它了。這就是面向對象中的“依賴倒置”機制。如下類圖:

?

實現代碼如下:

public?interface?ICar

????{

????????void?Run();

????????void?Stop();

????????void?Turn();

????}

????public?class?HondaCar:ICar

????{

????????public?void?Run()?{?Console.WriteLine("本田車啟動了!");?}

????????public?void?Turn()?{?Console.WriteLine("本田車拐彎了!");?}

????????public?void?Stop()?{?Console.WriteLine("本田車停止了!");?}

????}

????public?class?FordCar?:ICar

????{

????????public?void?Run()?{?Console.WriteLine("福特車啟動了!");?}

????????public?void?Turn()?{?Console.WriteLine("福特車拐彎了!");?}

????????public?void?Stop()?{?Console.WriteLine("福特車停止了!");?}

????}

????public?class?Jeep:ICar

????{

????????public?void?Run()?{?Console.WriteLine("福特車啟動了!");?}

????????public?void?Turn()?{?Console.WriteLine("福特車拐彎了!");?}

????????public?void?Stop()?{?Console.WriteLine("福特車停止了!");?}

????}

????public?class?AutoSystem

????{

????????private?ICar?car;

????????public?AutoSystem(ICar?car)

????????{

????????????this.car?=?car;

????????}

????????public?void?RunCar()

????????{

????????????this.car.Run();

????????}

????????public?void?StopCar()

????????{

????????????this.car.Stop();

????????}

????????public?void?TurnCar()

????????{

????????????this.car.Turn();

????????}

????}

現在Autosystem系統依賴于ICar這個抽象,而與具體的實現細節HondaCar:和FordCar無關,所以實現細節的變化不會影響AutoSystem.對于實現細節只要實現ICar即可。即實現細節依賴于ICar抽象。

綜上所述:一個應用中的重要策略決定及業務?正是在這些高層的模塊中。也正是這些模塊包含這應用的特性。但是,當這些模塊依賴于低層模塊時,低層模塊的修改比較將直接影響到他們,迫使它們也改變。這種情況是荒謬的。

??應該是處于高層的模塊去迫使那些低層的模塊發生改變。處于高層的模塊應優先于低層的模塊。無論如何高層模塊也不應該依賴于低層模塊。而且我們想能夠復用的是高層的模塊,只有高層模塊獨立于低層模塊時,復用才有可能。

??總之,高層次的模塊不應該依賴于低層次的模塊,它們都應該依賴于抽象。抽象不應該依賴于具體,具體應該依賴于抽象。

?

?

4迪米特法則

迪米特法則(Law?of?Demeter)又叫最少知識原則(Least?Knowledge?Principle?LKP),就是說一個對象應當對其他對象有盡可能少的了解,不和陌生人說話。

????對面向對象來說,一個軟件實體應當盡可能的少的與其他實體發生相互作用。每一個軟件單位對其他的單位都只有最少的知識,而其局限于那些與本單位密切相關的軟件單位。

迪米特法則的目的在于降低類之間的耦合。由于每個類盡量減少對其他類的依賴,因此,很容易使得系統的功能模塊相互獨立,相互之間不存在依賴關系。應用迪米特法則有可能造成的一個后果就是,系統中存在的大量的中介類,這些類只所以存在完全是為了傳遞類之間的相互調用關系---這在一定程度上增加系統的復雜度。

設計模式中的門面模式(Facade)和中介模式(Mediator)都是迪米特法則的應用的例子。

狹義的迪米特法則的缺點:

在系統里面造出大量的小方法,這些方法僅僅是傳遞間接的調用,與系統的商業邏輯無關。

遵循類之間的迪米特法則會使一個系統的局部設計簡化,因為每一個局部都不會和遠距離的對象有之間的關聯。但是,這也會造成系統的不同模塊之間的通信效率降低,也會使系統的不同模塊之間不容易協調。

廣義的迪米特法則在類的設計上的體現:

優先考慮將一個類設置成不變類.

盡量降低一個類的訪問權限。

盡量降低成員的訪問權限。

?

下面的代碼在方法體內部依賴了其他類,這嚴重違反迪米特法則

1

2

3

4

5

6

7

8

9

10

11

12

13

public?class?Teacher?{?

??

????public?void?commond(GroupLeader?groupLeader)?{?

????????List<Girl>?listGirls?=?new?ArrayList<Girl>();?

??

????????for?(int?i?=?0;?i?<?20;?i++)?{?

????????????listGirls.add(new?Girl());?

????????}?

??

????????groupLeader.countGirls(listGirls);?

????}?

??

}

?

方法是類的一個行為,類竟然不知道自己的行為與其他類產生了依賴關系,這是不允許的。正確的做法是:

?

1

2

3

4

5

6

7

public?class?Teacher?{?

??

????public?void?commond(GroupLeader?groupLeader)?{?

????????groupLeader.countGirls();?

????}?

??

}

?

1

2

3

4

5

6

7

8

9

10

11

12

13

public?class?GroupLeader?{?

??

????private?List<Girl>?listGirls;?

??

????public?GroupLeader(List<Girl>?_listGirls)?{?

????????this.listGirls?=?_listGirls;?

????}?

??

????public?void?countGirls()?{?

????????System.out.println("女生數量是:"?+?listGirls.size());?

????}?

??

}

?

?

5開放封閉原則(Open-Closed?Principle?OCP)

Software?entities(classes,modules,functions?etc)?should?open?for?extension?,but?close?for?modification.?

???什么意思呢?

???所謂開放封閉原則就是軟件實體應該對擴展開發,而對修改封閉。開放封閉原則是所有面向對象原則的核心。軟件設計本身所追求的目標就是封裝變化,降低耦合,而開放封閉原則正是對這一目標的最直接體現。

???開放封閉原則主要體現在兩個方面:

???對擴展開放,意味著有新的需求或變化時,可以對現有代碼進行擴展,以適應新的情況。

???對修改封閉,意味著類一旦設計完成,就可以獨立其工作,而不要對類盡任何修改。

?

為什么要用到開放封閉原則呢?

軟件需求總是變化的,世界上沒有一個軟件的是不變的,因此對軟件設計人員來說,必須在不需要對原有系統進行修改的情況下,實現靈活的系統擴展。

?

如何做到對擴展開放,對修改封閉呢?

實現開放封閉的核心思想就是對抽象編程,而不對具體編程,因為抽象相對穩定。讓類依賴于固定的抽象,所以對修改就是封閉的;而通過面向對象的繼承和多態機制,可以實現對抽象體的繼承,通過覆寫其方法來改變固有行為,實現新的擴展方法,所以對于擴展就是開放的。

對于違反這一原則的類,必須通過重構來進行改善。常用于實現的設計模式主要有Template?Method模式和Strategy?模式。而封裝變化,是實現這一原則的重要手段,將經常變化的狀態封裝為一個類。

以銀行業務員為例

沒有實現OCP的設計:

?

public?class?BankProcess

????{

????????//存款

????????public?void?Deposite()

????????{

????????}

????????//取款

????????public?void?Withdraw()

????????{

????????}

????????//轉賬

????????public?void?Transfer()

????????{

????????}

????}

?

????public?class?BankStaff

????{

????????private?BankProcess?bankpro?=?new?BankProcess();

????????public?void?BankHandle(Client?client)

????????{

????????????switch?(client.Type)

????????????{

????????????????????//存款

????????????????case?"deposite":

????????????????????bankpro.Deposite();

????????????????????break;

????????????????????//取款

????????????????case?"withdraw":

????????????????????bankpro.Withdraw();

????????????????????break;

????????????????????//轉賬

????????????????case?"transfer":

????????????????????bankpro.Transfer();

????????????????????break;

????????????}

????????}

????}

這種設計顯然是存在問題的,目前設計中就只有存款,取款和轉賬三個功能,將來如果業務增加了,比如增加申購基金功能,理財功能等,就必須要修改BankProcess業務類。我們分析上述設計就不能發現把不能業務封裝在一個類里面,違反單一職責原則,而有新的需求發生,必須修改現有代碼則違反了開放封閉原則。

從開放封閉的角度來分析,在銀行系統中最可能擴展的就是業務功能的增加或變更。對業務流程應該作為擴展的部分來實現。當有新的功能時,不需要再對現有業務進行重新梳理,然后再對系統做大的修改。

如何才能實現耦合度和靈活性兼得呢?

那就是抽象,將業務功能抽象為接口,當業務員依賴于固定的抽象時,對修改就是封閉的,而通過繼承和多態繼承,從抽象體中擴展出新的實現,就是對擴展的開放。

以下是符合OCP的設計:

?

首先聲明一個業務處理接口

public??interface?IBankProcess

????{

?????????void?Process();

????}

public?class?DepositProcess?:?IBankProcess

????{

????????public?void?Process()

????????{

????????????//辦理存款業務

????????????Console.WriteLine("Process?Deposit");

????????}

}

public?class?WithDrawProcess?:?IBankProcess

????{

????????public?void?Process()

????????{

????????????//辦理取款業務

????????????Console.WriteLine("Process?WithDraw");

????????}

}

?

public?class?TransferProcess?:?IBankProcess

????{

????????public?void?Process()

????????{

????????????//辦理轉賬業務

????????????Console.WriteLine("Process?Transfer");

????????}

????}

?

public?class?BankStaff

????{

????????private?IBankProcess?bankpro?=?null;

????????public?void?BankHandle(Client?client)

????????{

????????????switch?(client.Type)

????????????{

????????????????????//存款

????????????????case?"Deposit":

????????????????????bankpro?=?new?DepositUser();

????????????????????break;

????????????????????//轉賬

????????????????case?"Transfer":

????????????????????bankpro?=?new?TransferUser();

????????????????????break;

????????????????????//取款

????????????????case?"WithDraw":

????????????????????bankpro?=?new?WithDrawUser();

????????????????????break;

????????????}

?

?

?

?

????????????bankpro.Process();

????????}

????}

?

這樣當業務變更時,只需要修改對應的業務實現類就可以,其他不相干的業務就不必修改。當業務增加,只需要增加業務的實現就可以了。

?

設計建議:

開放封閉原則,是最為重要的設計原則,Liskov替換原則和合成/聚合復用原則為開放封閉原則提供保證。

可以通過Template?Method模式和Strategy模式進行重構,實現對修改封閉,對擴展開放的設計思路。

封裝變化,是實現開放封閉原則的重要手段,對于經常發生變化的狀態,一般將其封裝為一個抽象,例如銀行業務中IBankProcess接口。

拒絕濫用抽象,只將經常變化的部分進行抽象。

?

6接口隔離原則(ISP)

接口隔離原則?認為:"使用多個專門的接口比使用單一的總接口要好"。因為接口如果能夠保持粒度夠小,就能保證它足夠穩定,正如單一職責原則所標榜的那樣。多個專門的接口就好比采用活字制版,可以隨時拼版拆版,既利于修改,又利于文字的重用。而單一的總接口就是雕版印刷,顯得笨重,實現殊為不易;一旦發現錯字別字,就很難修改,往往需要整塊雕版重新雕刻。

例一:

參考下圖的設計,在這個設計里,取款、存款、轉帳都使用一個通用界面接口,也就是說,每一個類都被強迫依賴了另兩個類的接口方法,那么每個類有可能因為另外兩個類的方法(跟自己無關)而被影響。拿取款來說,它根本不關心“存款操作”和“轉帳操作”,可是它卻要受到這兩個方法的變化的影響。

?


那么我們該如何解決這個問題呢?參考下圖的設計,為每個類都單獨設計專門的操作接口,使得它們只依賴于它們關系的方法,這樣就不會互相影了!

?

?

?

例二:

使用多個專門的接口還能夠體現對象的層次,因為我們可以通過接口的繼承,實現對總接口的定義。例如,.NET框架中IList接口的定義。

1.?public?interface?IEnumerable??

2.?{??

3.?????IEnumerator?GetEnumerator();??

4.?}??

5.?public?interface?ICollection?:?IEnumerable??

6.?{??

7.?????void?CopyTo(Array?array,?int?index);??

8.??

9.?????//?其余成員略??

10.?}??

11.?public?interface?IList?:?ICollection,?IEnumerable??

12.?{??

13.?????int?Add(object?value);??

14.?????void?Clear();??

15.?????bool?Contains(object?value);??

16.?????int?IndexOf(object?value);??

17.?????void?Insert(int?index,?object?value);??

18.?????void?Remove(object?value);??

19.?????void?RemoveAt(int?index);??

20.??

21.?????//?其余成員略??

22.?}?

如果不采用這樣的接口繼承方式,而是定義一個總的接口包含上述成員,就無法實現IEnumerable接口、ICollection接口與IList接口成員之間的隔離。假如這個總接口名為IGeneralList,它抹平了IEnumerable接口、ICollection接口與IList接口之間的差別,包含了它們的所有方法?,F在,如果我們需要定義一個Hashtable類。根據數據結構的特性,它將無法實現IGeneralList接口。因為Hashtable包含的Add()方法,需要提供鍵與值,而之前針對ArrayList的Add()方法,則只需要值即可。這意味著兩者的接口存在差異。我們需要專門為Hashtable定義一個接口,例如IDictionary,但它卻與IGeneralList接口不存在任何關系。正是因為一個總接口的引入,使得我們在可枚舉與集合層面上丟失了共同的抽象意義。雖然Hashtable與ArrayList都是可枚舉的,也都具備集合特征,它們卻不可互換。

如果遵循接口隔離原則,將各自的集合操作功能分解為不同的接口,那么站在ICollection以及IEnumerable的抽象層面上,可以認為ArrayList和Hashtable是相同的對象。在這一抽象層面上,二者是可替換的,如圖2-9所示。這樣的設計保證了一定程度的重用性與可擴展性。從某種程度來講,接口隔離原則可以看做是接口層的單一職責原則。

?

?

圖2-9??遵循接口隔離原則

倘若一個類實現了所有的專門接口,從實現上看,它與實現一個總接口的方式并無區別;但站在調用者的角度,不同的接口代表了不同的關注點、不同的職責,甚至是不同的角色。因此,面對需求不同的調用者,這樣的類就可以提供一個對應的細粒度接口去匹配。此外,一個龐大的接口不利于我們對其進行測試,因為在為該接口實現Mock或Fake對象?時,需要實現太多的方法。

概括地講,面向對象設計原則仍然是面向對象思想的體現。例如,單一職責原則與接口隔離原則體現了封裝的思想,開放封閉原則體現了對象的封裝與多態,而Liskov替換原則是對對象繼承的規范,至于依賴倒置原則,則是多態與抽象思想的體現。在充分理解面向對象思想的基礎上,掌握基本的設計原則,并能夠在項目設計中靈活運用這些原則,就能夠改善我們的設計,尤其能夠保證可重用性、可維護性與可擴展性等系統的質量屬性。這些核心要素與設計原則,就是我們設計的對象法則,它們是理解和掌握設計模式的必備知識。

7組合/聚集復用原則

組合/聚合復用原則(Composite/Aggregate?Reuse?Principle?CARP.組合和聚合都是對象建模中關聯(Association)關系的一種.聚合表示整體與部分的關系,表示“含有”,整體由部分組合而成,部分可以脫離整體作為一個獨立的個體存在。組合則是一種更強的聚合,部分組成整體,而且不可分割,部分不能脫離整體而單獨存在。在合成關系中,部分和整體的生命周期一樣,組合的新的對象完全支配其組成部分,包括他們的創建和銷毀。一個合成關系中成分對象是不能與另外一個合成關系共享。

組合/聚合和繼承是實現復用的兩個基本途徑。合成復用原則是指盡量使用合成/聚合,而不是使用繼承。

只有當以下的條件全部被滿足時,才應當使用繼承關系。

1?子類是超類的一個特殊種類,而不是超類的一個角色,也就是區分“Has-A”和“Is-A.只有“Is-A”關系才符合繼承關系,“Has-A”關系應當使用聚合來描述。

2?永遠不會出現需要將子類換成另外一個類的

子類的情況。如果不能肯定將來是否會變成另外一個子類的話,就不要使用繼承。

3?子類具有擴展超類的責任,而不是具有置換掉或注銷掉超類的責任。如果一個子類需要大量的置換掉超類的行為,那么這個類就不應該是這個超類的子類。

錯誤的使用繼承而不是合成/聚合的一個常見原因是錯誤地把“Has-A”當成了“Is-A.”Is-A”代表一個類是另外一個類的一種;而“Has-A”代表一個類是另外一個類的一個角色,而不是另外一個類的特殊種類。

我們需要辦理一張銀行卡,如果銀行卡默認都擁有了存款、取款和透支的功能,那么我們辦理的卡都將具有這個功能,此時使用了繼承關系:

?

為了靈活地擁有各種功能,此時可以分別設立儲蓄卡和信用卡兩種,并有銀行卡來對它們進行聚合使用。此時采用了合成復用原則

?

?

轉載于:https://www.cnblogs.com/sh91/archive/2012/03/25/2416624.html

總結

以上是生活随笔為你收集整理的面向对象基本原则的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。