里氏替換原則,為繼承定義規(guī)范。
里氏替換原則有如下特點:
代碼共享,減少創(chuàng)建類的工作量
提高代碼的重用性
提高代碼的可擴展性
提高產(chǎn)品代碼的開放性
繼承侵入性 只要繼承,必須擁有父類的內(nèi)容
降低代碼的靈活性,子類必須擁有父類的屬性和方法
增強耦合性。
里氏替換原則:
有一功能P1,由類A完成?,F(xiàn)需要將功能P1進行擴展,擴展后的功能為P,其中P由原功能P1與新功能P2組成。新功能P由類A的子類B來完成,則子類B在完成新功能P2的同時,有可能會導致原有功能P1發(fā)生故障。當使用繼承時,遵循里氏替換原則。類B繼承類A時,除添加新的方法完成新增功能P2外,盡量不要重寫父類A的方法,也盡量不要重載父類A的方法。
舉個例子:
我這里有一個長方形類,一個正方形類,正方形是一個特殊的長方形。那么正方形類繼承自長方形類:
代碼如下所示:
Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Displace
{
/// <summary>
/// 高層模塊:調(diào)用長方形及正方形類
/// </summary>
class Program
{
static void Main(string[] args)
{
Rectangle rectangle = new Rectangle();
rectangle.SetHeight(10);
rectangle.SetWidth(20);
Console.WriteLine(rectangle.GetHeight());
Console.WriteLine(rectangle.GetWidth());
Console.WriteLine("------------ 我是分割線 -------------");
Square square = new Square();
square.SetHeight(10);
square.SetWidth(20);
Console.WriteLine(square.GetHeight());
Console.WriteLine(square.GetWidth());
Console.ReadKey();
}
}
}
長方形類:Rectangle.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Displace
{
/// <summary>
/// 長方形類
/// </summary>
public class Rectangle
{
public double height;
public double width;
public void SetHeight(double height)
{
this.height = height;
}
public double GetHeight()
{
return height;
}
public void SetWidth(double width)
{
this.width = width;
}
public double GetWidth()
{
return width;
}
}
}
正方形類:Square.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Displace
{
/// <summary>
/// 正方形類
/// </summary>
public class Square: Rectangle
{
public void SetHeight(double height)
{
this.height = height;
this.width = height;
}
public double GetHeight()
{
return height;
}
public void SetWidth(double width)
{
this.height = width;
this.width = width;
}
public double GetWidth()
{
return width;
}
/// <summary>
/// 求面積
/// </summary>
public double GetArea()
{
return width * height;
}
}
}
輸出結(jié)果如下圖所示:

輸出的結(jié)果明顯是不對的,我調(diào)用父類與子類的統(tǒng)一方法輸出的結(jié)果應該是一致的。所以上面的栗子不符合里氏替換原則。
里氏替換原則:
只要有父類出現(xiàn)的地方,都可以用子類來替代,而且不會出現(xiàn)任何錯誤和異常。但是反過來則不行,有子類出現(xiàn)的地方,不能用其父類替代。
包含以下四種約束:
1:子類必須實現(xiàn)父類的抽象方法,但不得重寫(覆蓋)父類的非抽象(已實現(xiàn))方法。
有時候父類有多個子類,但在這些子類中有一個特例。要想滿足里氏替換原則,又想滿足這個子類的功能時,有的伙伴可能會修改父類的方法。但是,修改了父類的方法又會對其他的子類造成影響,產(chǎn)生更多的錯誤。這是怎么辦呢?我們可以為這個特例創(chuàng)建一個新的父類,這個新的父類擁有原父類的部分功能,又有不同的功能。這樣既滿足了里氏替換原則,又滿足了這個特例的需求。
2:子類中可以增加自己特有的方法。
3:當子類覆蓋或?qū)崿F(xiàn)父類的方法時,方法的前置條件(即方法的形參)要比父類方法的輸入?yún)?shù)更寬松。(子類的參數(shù)范圍要比父類的參數(shù)范圍大)
4:當子類的方法實現(xiàn)父類的抽象方法時,方法的后置條件(即方法的返回值)要比父類更嚴格。
我上面的那段程序很顯然是不符合里氏替換原則的,那么上邊我們那段程序該怎么修改呢,我們創(chuàng)建一個接口,接口中包含獲取寬度及高度的兩個方法,長方形類和正方形類分別實現(xiàn)這個接口,那么其二者現(xiàn)在就為同級,父類可以的地方,他們二者都可以。下邊是我修改之后的代碼:
Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Displace
{
/// <summary>
/// 高層模塊:調(diào)用長方形及正方形類
/// </summary>
class Program
{
static void Main(string[] args)
{
Rectangle rectangle = new Rectangle();
rectangle.SetHeight(10);
rectangle.SetWidth(20);
Console.WriteLine(rectangle.GetHeight());
Console.WriteLine(rectangle.GetWidth());
Console.WriteLine("------------ 我是分割線 -------------");
Square square = new Square();
square.SetHeight(10);
square.SetWidth(20);
Console.WriteLine(square.GetHeight());
Console.WriteLine(square.GetWidth());
Console.ReadKey();
}
}
}
Rectangle.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Displace
{
/// <summary>
/// 長方形類
/// </summary>
public class Rectangle: Interface1
{
public double height;
public double width;
public void SetHeight(double height)
{
this.height = height;
}
public double GetHeight()
{
return height;
}
public void SetWidth(double width)
{
this.width = width;
}
public double GetWidth()
{
return width;
}
}
}
Square.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Displace
{
/// <summary>
/// 正方形類
/// </summary>
public class Square: Interface1
{
public double height;
public double width;
public void SetHeight(double height)
{
this.height = height;
this.width = height;
}
public double GetHeight()
{
return height;
}
public void SetWidth(double width)
{
this.height = width;
this.width = width;
}
public double GetWidth()
{
return width;
}
/// <summary>
/// 求面積
/// </summary>
public double GetArea()
{
return width * height;
}
}
}
父類接口:Interface1.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Displace
{
interface Interface1
{
double GetHeight();
double GetWidth();
}
}
這個程序基本上就實現(xiàn)了上邊所說的四個約束,例子有點爛,但是基本上融匯了里氏替換原則的思想。
說到底,里氏替換原則就是對繼承的約束~
歡迎訪問個人博客
https://guanchao.site