软件架构设计原则一切都是为了下面这两点,别忘了。
LSP 这个原则比较倾向是在物件导向才会有的设计原则,这也正常毕竟我们在讨论的软件设计原则 SOLID 最开始为『 物件导向设计原则 』,但是在很多地方事实上也通用,不过这个原则就比较算是在物件导向才能用的。
根据 《 Clean Architecture 》这本书提到 1988 年,Barbara Liskov 写下定义子型态的方式 :
若对型态 S 的每一个物件 o1,都存在一个型态为 T 的件 o2,使得在所有针对 T 编写的程序 P 中,用 o1 替换 o2 後,程序 P 的行为功能不变,则 S 是 T 的子型态
我觉得比较白话文的说法为 :
在使用父类别的地方,如果替换成子类别,则行为功能不变
为什麽会要有这个准则呢 ? 你想想如果替换成子类别不能用,那是不是就有机率这个子类别实际上,不适合长在这个父类别下,因为子类别已经破坏了父类别的继承体系,就有可能会在未来发生可能的 Bug。
像我在实务上的确有看过,有个子类别已经违反 LSP,但是因为一些原因与时间很难改,结果导致父类别有很多地方,要为了这个子类别写特殊判断,结果特殊判断和原本的行为打架。
不过在 《 Clean Architecture 》 有提到一个重点 :
LSP 视为指导 『 继承 』的使用,但是多年来已涉及到介面与实作,它已经演变成更广泛的软件原则
也就是说这个原则,已经慢慢变成『 除了继承以外的准则 』
这个范例是 《 Clean Architecture 》中最有名的正方形与长方形问题,正方形只是长方形长与宽相同的情况,所以这里范例正方形继承长方形。
然後这个范例违反 LSP 的情况为,如果是用长方形父类别来计算面积答案就对,而如果改成用正方形来计算面积就会错。别忘了 LSP 的定义为 :
在使用父类别的地方,如果替换成子类别,则行为功能不变
class Rectangle{
h:number
w:number
setHeight(h): void{}
setWidth(w): void{}
getArea(){
return this.w * this.h
}
}
class Square extends Rectangle{
setHeight(h): void {
this.h = h
this.w = h
}
setWidth(w): void {
this.h = w
this.w = w
}
}
const square = new Square()
square.setHeight(10)
square.setWidth(20)
// 这里就会有问题,行为和答案不合
const area = square.getArea()
LSP 目前应该是我实务上比较少碰到的,比较大的原因在於比较少用到继承,主要的原因在於继承是个依赖性很高的东西,而果真的要用到继承我也会倾向以『 做什麽 』来设计这个父类别。详细为什麽不用的原因我觉得这篇文章写的很清楚了 ~ 可参考
老样子,来问一下三个问题,来加深记忆
LSP 原则基本上是在讨论什麽情况使用继承会有问题,而事实上很多在研究这个原则时,也慢慢的发现使用继承的问题。
LSP 让我连想到以前在讨论继承的可能问题,例如 :
<<: Day 19 - Unreal Webcam Fun [更新]
>>: [DAY 04] EC2 Security Group
昨天我们提过我们的目标是成功分类一组资料,那...资料哪来啊 OAO?总不能每次我要练习之前,还要...
接下来介绍的章节,会使用到instance_eval, class_eval,加上我们已经在 Day...
思考重点 TCP如何确认对方收到消息? 讯息收发中的头部消息变化? 关闭连接操作? 核心知识 封包的...
环境 Windows 10 21H1 Visual Studio 2019 前情提要 在【Day 2...
思考重点 网页浏览器怎麽获取网站消息 当我们输入网时会发生什麽事 常见的404 not found意...