您好,登錄后才能下訂單哦!
C#forUnity編程語言快速入門教程(連載10)_C#OOP編程之里氏替換原則
最近在Unity教學過程中,有學員對于C#的“里氏替換原則”(LSP)產生疑問表示不太理解。問題集中在不知道這個原則是做什么,有什么優勢,具體在游戲開發(程序開發)過程中有什么借鑒與指導作用。
就以上問題我總結梳理一下,與大家共同探討。
起源: 在我們的程序開發過程中,起初應該都是以完成項目功能為重點。但是隨著軟件規模的不斷擴大,人們發現只完成功能是遠遠不夠的。于是聰明的軟件工程師,從大量的編程實踐中開始總結一些共性的軟件開發理論,以供后人規范編程,更有效率、更穩定的開發各種項目(包括游戲項目)。
目前公認的一些編程原則:
1: 單一職責原則 英文名稱是Single Responsibility Principle,簡稱SRP
2: 開閉原則 英文全稱是Open Close Principle,簡稱OCP
3: 里氏替換原則 英文全稱是Liskov Substitution Principle,簡稱LSP
4: 依賴倒置原則 英文全稱是Dependence Inversion Principle,簡稱DIP
在以上編程開發原則中,“開閉原則”與“里氏替換原則”比較類似,都是研究如何在相對穩定的編碼環境中,盡可能的允許程序功能的變化。也就是最低的編碼改動量,實現最大程度的軟件項目變化性,來適應用戶對軟件系統不斷變化的功能要求。
解釋如上概念:
“開放-封閉原則”(即: 開閉原則)(OCP): 對于程序的擴展是開放的(Open for extension),對于程序的修改是封閉的(Closed for modification)
“里氏替換原則”:子類型必須能夠替換掉它們的父類型!(反之則不成立)
“ 里氏替換”(也叫“里氏代換”原則)原則英文全稱是Liskov Substitution Principle,簡稱LSP。 由2008年圖靈獎得主、美國第一位計算機科學女博士Barbara Liskov教授和卡內基·梅隆大學Jeannette Wing教授于1994年提出。 里氏替換原則就是要求寫程序前,先建立抽象,通過抽象建立規范,具體的實現在運行時替換掉抽象,保證系統的高擴展性、靈活性。
“開閉原則”要求我們寫的程序修改盡量要少(即:封閉),但是對于功能的擴展確實開放的、允許的。 這其實是提了一個很高的編程目標,很好的一個指導原則,但是如何具體到實現上,是沒有方法的,是不接“底氣”的。 所以“里氏替換”就給了一個具體的實現方法的原則,就是讓子類來無縫的對接父類,“子類型必須能夠替掉父類”。
“里氏替換原則”具體的要求可以表述如下:
1: 子類的所有方法必須在父類中聲明,或子類必須實現父類中聲明的所有方法。根據里氏替換原則,為了保證系統的擴展性,在程序中通常使用父類來進行定義,如果一個方法只存在子類中,在父類中不提供相應的聲明,則無法在以父類定義的對象中使用該方法。
2: 我們在運用里氏替換原則時,盡量把父類設計為抽象類或者接口,讓子類繼承父類或實現父接口,并實現在父類中聲明的方法,運行時,子類實例替換父類實例,我們可以很方便地擴展系統的功能,同時無須修改原有子類的代碼,增加新的功能可以通過增加一個新的子類來實現。里氏替換原則其實就是“開閉原則”的具體實現手段之一!
我們從序號為2的表述中可以看出,其實“里氏替換”原則就是一種更加具體的手段來實現“開閉原則”。 序號為2的理論其實就是我們常說的“動態多態性”的規則。
針對以上理論,為了更好的理解,筆者給出“里氏替換原則”的實現參考代碼,供C#初學者進行參考研究,有具體問題,可以留言,謝謝!
//參考源碼1: 以下是使用“虛方法”,來實現“里氏替換”。
class Person
{
//虛方法
public virtual void SpeakLanguage()
{
Console.WriteLine("Person, 說話方法");
}
}
class MrLiu:Person
{
//方法重寫
public override void SpeakLanguage()
{
Console.WriteLine("MrLiu, 劉老師說中國話!");
}
}
class Tom:Person
{
//方法重寫
public override void SpeakLanguage()
{
Console.WriteLine("Tom, Tom Speak Engligh!");
}
}
class Test
{
public static void Main()
{
/* “里氏替換原則”(即:動態多態性)測試 */
//功能的定義部分
//Person per = new MrLiu(); //打印“MrLiu, 劉老師說中國話!”
Person per = new Tom(); //打印“Tom, Tom Speak Engligh!”
//功能的實現部分
per.SpeakLanguage();
}
}
//參考源碼2: 以下是使用“接口”,來實現“里氏替換”。
interface ISpeakable
{
//抽象方法
void SpeakLanguage();
}
class MrLiu:ISpeakable
{
//方法實現
void ISpeakable.SpeakLanguage()
{
Console.WriteLine("MrLiu, 劉老師說中國話!");
}
}
class Tom:ISpeakable
{
//方法實現
void ISpeakable.SpeakLanguage()
{
Console.WriteLine("Tom, Tom Speak Engligh!");
}
}
class Test
{
public static void Main()
{
/* “里氏替換原則”(即:動態多態性)測試 */
//功能的定義部分
//ISpeakable Ispeak = new Tom();//打印“Tom, Tom Speak Engligh!”
ISpeakable Ispeak = new MrLiu();//打印“MrLiu, 劉老師說中國話!”
//功能的實現部分
Ispeak.SpeakLanguage();
}
}
以上兩段代碼,分別用了父類“虛方法”與“接口”的方式分別實現“里氏替換”,當然還可以用“抽象類”來實現,究竟三者哪個方式,更應該優先使用呢? 現給出一個原則:
“里氏替換”原則可以用以下方式來實現:
1> 虛方法的動態多態性。
2> 抽象方法的動態多態性。
3> 接口方法的動態多態性。
規則: 實現“里氏替換”原則(動態多態性): 能用接口,不用抽象方法,能用抽象方法,不用虛方法。
好了,針對以上問題,筆者就總結以上內容,感興趣的C#編程愛好者可以進行留言討論。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。