软件设计原则-里氏代换原则

125 阅读2分钟

里氏代换原则

里氏代换原则是面向对象设计的基本原则之一

里氏代换原则: 任何基类可以出现的地方,子类一定可以出现。通俗理解:子类可以扩展父类的功能,但不能改变父类原有的功能。换句话说,子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法。 如果通过重写父类的方法来完成新的功能,这样写起来虽然简单,但是整个继承体系的可复用性会比较差,特别是运用多态比较频繁时,程序运行出错的概率会非常大。 下面看一个里氏替换原则中经典的一个例子 【例】正方形不是长方形. 在数学领域里,正方形毫无疑问是长方形,它是一个长宽相等的长方形。所以,我们开发的一个与几何图形相关的软件系统,就可以顺理成章的让正方形继承自长方形。

//长方形类
class Rectangle{
    var length:Double
    var width:Double
    init(length:Double,width:Double){
        self.length = length
        self.width  = width
    }
}
//正方形类 继承自长方形
class Square:Rectangle{
    override var length: Double{
        set{
            super.width = newValue
            super.length = newValue
        }
        get {
            return super.length
        }
    }
    override var width: Double{
        set {
            super.width = newValue
            super.length = newValue
        }
        get {
            return super.width
        }
    }
}

测试

//    扩宽方法
func resize(rectangle:Rectangle){
//        如果宽比长小,进行扩宽的操作
    while(rectangle.width <= rectangle.length){
        print(rectangle.width)
        rectangle.width += 1
    }
}
func printWidthAndLength(rectangle:Rectangle){
    print("长:",rectangle.length,"\n宽:",rectangle.width)
}
​
//创建长方形对象
var rectangle:Rectangle = Rectangle(length: 20, width: 10)
resize(rectangle: rectangle)
printWidthAndLength(rectangle: rectangle)
print("====================================")
//创建正方形对象
var square = Square(length: 20, width: 20)
resize(rectangle: square)
printWidthAndLength(rectangle: square)
​

image-20220909170616816

正方形继承自长方形,根据里氏替换原则,正方形可以出现在长方形出现的位置,然而正方形调用扩宽方法的时候。陷入了死循环,直到 系统产生溢出错误。所以该代码违背了里氏替换原则,他们之间的继承关系不成立,正方形不是长方形。那么如何改进呢?我们需要重新设计他们之间的关系,定义一个四边形的接口,让长方形和正方形继承自该接口,同时实现接口定义的方法。

//四边形接口
protocol Quadrilateral{
    func getWidth()->Double
    func getLength()->Double
}
//正方形类
class Square:Quadrilateral{
    var side:Double = 0
    init(side:Double) {
        self.side = side
    }
    func getWidth() -> Double {
      return  side
    }
    
    func getLength() -> Double {
       return side
    }
}
//长方形类
class Rectangle:Quadrilateral{
    var width:Double = 0
    var length:Double = 0
    init(width:Double,length:Double) {
        self.width  = width
        self.length = length
    }
    func getWidth() -> Double {
        return self.width
    }
    
    func getLength() -> Double {
        return self.length
    }
    
}
//    扩宽方法
func resize(rectangle:Rectangle){
//        如果宽比长小,进行扩宽的操作
    while(rectangle.width <= rectangle.length){
        print(rectangle.width)
        rectangle.width += 1
    }
}
func printWidthAndLength(quadrilateral:Quadrilateral){
    print("长:",quadrilateral.getLength(),"\n宽:",quadrilateral.getWidth())
}
//创建长方形对象
var rectangle:Rectangle = Rectangle(width: 10, length: 20)
resize(rectangle: rectangle)
printWidthAndLength(quadrilateral: rectangle)
​

此时正方形跟长方形没有继承关系,正方形不能调用扩宽方法,符合里氏代换原则.

感谢黑马程序员课程