《设计模式》杂记之里氏替换原则
生活随笔
收集整理的這篇文章主要介紹了
《设计模式》杂记之里氏替换原则
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
在這篇博文中,我想把自己學(xué)習(xí)過的里氏替換原則一些好知識點分享給大家。首先我想把繼承的一下優(yōu)缺點給大家分享一下,然后再引出里氏替換原則吧!<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /> 我們都知道在面向?qū)ο蟮恼Z言中,繼承是必不可少的,那么它的優(yōu)點是哪些呢?引用書上一段話吧! (1)?????? 代碼共享,減少創(chuàng)建類的工作量,每個子類都擁有父類的方法和屬性; (2)?????? 提高了代碼的重用性; (3)?????? 子類可以形似父類,但有異于父類; (4)?????? 提高了代碼的可擴展性; (5)?????? 提高產(chǎn)品或項目的開放性。 既然有了優(yōu)點就應(yīng)該有缺點: (1)?????? 繼承是侵入性的。只有繼承,就必須擁有父類的所有屬性和方法; (2)?????? 降低代碼的靈活性。子類必須擁有父類的屬性和方法,讓子類受到了許多的約束; (3)?????? 增強了耦合性。當(dāng)父類的常量,變量和方法被修改時,必須要考慮子類的修改,更糟糕的結(jié)果就是大片的代碼需要重構(gòu)。 C#與C++不同,不支持多重繼承(即一個類從多個直接基類派生)。那么這里就引出了里氏替換原則。 里氏替換原則通俗的講就是:只要父類能出現(xiàn)的地方子類就可以出現(xiàn),而且替換為子類也不會產(chǎn)生任何錯誤或者異常,我們根本不需要知道是父類還是子類。 但是這里我們需要注意的是:有子類出現(xiàn)的地方,父類未必就能適應(yīng)。 前面我們引出了里氏替換原則的概念,通過這些概念我們可以了解到里氏替換原則為良好的繼承定義了一個規(guī)范。這里就包含了4層含義: 1,子類必須完全實現(xiàn)父類的方法 《設(shè)計模式之禪》這本書上的例子,我個人感覺比較有意思,所以自己就直接把書上的例子分享給大家學(xué)習(xí)吧!呵呵~~ ?書上引用的是CS打槍游戲例子,這里把槍的類圖列出: ???????? <?xml:namespace prefix = v ns = "urn:schemas-microsoft-com:vml" /> 通過這個類圖我們可以知道,槍得主要職責(zé)是射擊,如何設(shè)計在各個具體的子類中去定義。在Soldier中定義了一個方法killEnemy,使用槍來殺敵,具體使用什么槍來殺敵人,只有我們在調(diào)用時才知道啊! 下面是AbstractGun類的源程序代碼: abstract class AbstractGun ??? { ??????? //射擊 ??????? public abstract void shoot(); ??? } ×××,×××,機槍的實現(xiàn)類源代碼: class Handgun:AbstractGun ??? { ??????? public override void shoot() ??????? { ??????????? Console.WriteLine("×××射擊"); ??????? } ??? } class Rifle:AbstractGun ??? { ??????? public override void shoot() ??????? { ??????????? Console.WriteLine("×××射擊"); ??????? } ??? } class MachineGun:AbstractGun ??? { ??????? public override void shoot() ??????? { ??????????? Console.WriteLine("機槍掃射"); ??????? } ??? } 下面我們來定義一個士兵來使用這些槍吧!代碼如下: class Program ??? { ??????? static void Main(string[] args) ??????? { ??????????? //產(chǎn)生個士兵 ??????????? Soldier soldier = new Soldier(); ??????????? //給士兵一支槍 ??????????? soldier.setGun(new Handgun());???????? ??????????? soldier.killEnemy(); ??????????? System.Threading.Thread.Sleep(5000); ??????? } ??? } 在這個程序中,在編寫程序時Solider士兵類根本不用知道是哪個型號的槍(子類)被傳入。 注意:在類中調(diào)用其他類時務(wù)必要使用父類或接口,如果不能使用父類或接口,則說明類的設(shè)計已經(jīng)違背了LSP原則。 可能有人會說,我們?nèi)绻褂谩痢痢聊?#xff1f;大家可以想象×××是不能用來射擊殺死人的,所以說我們就不能寫在shoot方法中。下面是ToyGun的源代碼: class ToyGun:AbstractGun ??? { ??????? //×××是不能射擊的只能虛構(gòu)一個了。 ??????? public override void shoot() ??????? { ??????????? //×××不能殺人,所以這個方法不能實現(xiàn)。 ??????? } ??? } 因為我們引入了新的子類,在main方法中源代碼如下: class Program ??? { ??????? static void Main(string[] args) ??????? { ??????????? //產(chǎn)生一個士兵 ??????????? Solder soldier = new Solder(); ??????????? soldier.setGun(new ToyGun()); ??????????? soldier.killEnemy(); ??????????? System.Threading.Thread.Sleep(5000); ??????? } ??? } 在這種情況下,我們發(fā)現(xiàn)業(yè)務(wù)調(diào)用類已經(jīng)出現(xiàn)問題了,正常的業(yè)務(wù)邏輯不能運行。作者給我們提供了兩種解決辦法: 1)????????????? 在Soldier類中增加判斷,如果是×××,就不用來殺人。這個方法可以解決問題,但是在程序中,我們每增加一個類,所有與這個父類有關(guān)系的類都必須修改,這樣就不可行了。所以這個方案被否定了。 2)????????????? ToyGun脫離繼承,建立一個獨立的父類,可以與AbstractGun建立關(guān)聯(lián)委托關(guān)系。類圖如下: ???????? 大家都知道C#的三大特性:繼承,封裝,多態(tài)。繼承就是告訴我們擁有父類的方法和屬性,然后我們就可以重寫父類的方法。那么按照繼承原則,我們定義的×××繼承AbstractGun是沒有問題的。但是我們在具體應(yīng)用場景中就要考慮這個問題:子類是否能夠完整地實現(xiàn)父類的業(yè)務(wù)。 ???????? 注意:如果子類不能完整地實現(xiàn)父類的方法,或者父類的某些方法在子類中已經(jīng)發(fā)生“畸變”,則建議斷開父子繼承關(guān)系,采用依賴,聚合等關(guān)系代替繼承。 2,子類可以有自己的個性 由于里氏替換原則可以正著用,但是不能反過來用。在子類出現(xiàn)的地方,父類未必就可以勝任。這次我們以×××舉例子吧!×××也分為×××和×××,如AK47,AUG×××等等,Rifle子類圖如下: AUG×××源代碼如下: class AUG:Rifle ??? { ??????? public void zoomout() ??????? { ??????????? Console.WriteLine("通過望遠(yuǎn)鏡觀看敵人?"); ??????? }
?
??????? public override void shoot() ??????? { ??????????? Console.WriteLine("AUG射擊"); ; ??????? } ??? } 狙擊手的源代碼如下: class Sniper ??? { ??????? private AUG aug;?
??????? public void setGun(AUG _aug) ??????? { ??????????? this.aug = _aug; ??????? }?
??????? public void killEnemy() ??????? { ??????????? //首先用望遠(yuǎn)鏡觀察 ??????????? aug.zoomout(); ??????????? //開始射擊 ??????????? aug.shoot(); ??????? } ??? } 那么如何讓狙擊手使用AUG殺死人呢? static void Main(string[] args) ??????? { ??????????? Sniper sniper = new Sniper(); ??????????? sniper.setGun(new AUG()); ??????????? sniper.killEnemy(); ??????????? System.Threading.Thread.Sleep(5000); ??????? } 在這里,系統(tǒng)直接調(diào)用了子類。如果我們這個時候使用父類傳遞進來呢?代碼如下: static void Main(string[] args) ??????? { ??????????? Sniper sniper = new Sniper(); ??????????? sniper.setGun((AUG)(new Rifle())); ??????????? sniper.killEnemy(); ??????????? System.Threading.Thread.Sleep(5000); ??????? } ?在運行時拋出InvalidCastException異常,從里氏替換原則來看,有子類出現(xiàn)的地方父類未必就可以出現(xiàn)。轉(zhuǎn)載于:https://blog.51cto.com/wzk89/548618
總結(jié)
以上是生活随笔為你收集整理的《设计模式》杂记之里氏替换原则的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [lighttpd] lighttpd的
- 下一篇: asp.net ajax1.0基础回顾(