設計模式(六)之里氏替換原則

里氏替換原則,為繼承定義規(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é)果如下圖所示:


1782fd89779042708c6d57e5176ade31_tplv-k3u1fbpfcp-zoom-1.png

輸出的結(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

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容