读懂 SOLID 的「里氏替换」原则
这是理解
什么是里氏替换原则Objects should be replaceable with instances of their subtypes without altering the correctness of that program. 这句话的意思是说,当我们在传递一个父抽象的子类型时,你需要保证你不会修改任何关于这个父抽象的行为和状态语义。 如果你不遵循里氏替换原则,那么你可能会面临以下问题:
通常打破这条原则的情况发生在修改父类中在其他方法中使用的,与当前子类无关联的内部或者私有变量。这通常算得上是一种对于类本身的一次潜在攻击,而且这种攻击可能是你在不经意间自己发起的,而且不仅在子类中。 反面例子让我们通过一个反面例子来演示这种修改行为和它所产生的后果。比如,我们有一个关于 代码如下: interface Store { store(message: string); retrieveMessages(): string[]; } const STORE_LIMIT = 5; class BasicStore implements Store { protected stash: string[] = []; protected storeLimit: number = STORE_LIMIT; store(message: string) { if (this.storeLimit === this.stash.length) { this.makeMoreRoomForStore(); } this.stash.push(message); } retrieveMessages(): string[] { return this.stash; } makeMoreRoomForStore(): void { this.storeLimit += 5; } } 之后通过继承 class RotatingStore extends BasicStore { makeMoreRoomForStore() { this.stash = this.stash.slice(1); } } 注意 在使用 const st: Store = new RotatingStore() st.store("hello") st.store("world") st.store("how") st.store("are") st.store("you") st.store("today") st.store("sir?") st.retrieveMessages() // 一些消息丢失了 一些消息会无故消失,当前这个类的表现逻辑与所有消息均可以被取出的基本需求不一致。 如何实践里氏替换原则为了避免这种奇怪现象的发生,里氏替换原则推荐我们通过在子类中调用父类的公有方法来获取一些内部状态变量,而不是直接使用它。这样我们就可以保证父类抽象中正确的状态语义,从而避免了副作用和非法的状态转变。 它也推荐我们应当尽可能的使基本抽象保持简单和最小化,因为对于子类来说,有助于提供父类的扩展性。如果一个父类是比较复杂的,那么子类在覆盖它的时候,在不影响父类状态语义的情况下进行扩展绝非易事。 对于内部系统做可行的后置条件检查也是一个不错的方式,这种检查通常会验证是否子类会搅乱一些关键代码的运行路径(译者注:也可以理解为状态语义),但是我本身对这个实践并没有太多的经验,所以无法给予具体的例子。 代码评论也可以一定程度上给予好的帮助。当你在开发一些你可能无意间做出一些对已有系统的破坏,但是你的同事可能会很容易地发现这些(当局者迷旁观者清)。软件设计保持一致性是一件十分重要的事情,因此应当尽早、尽可能多地查明那些对对象继承链作出潜在修改的代码。 最后,在单一职责原则中,我们曾提及,考虑使用组合模式来替换继承模式。 总结正如你所看到的,在开发软件时,我们往往需要额外花一些努力和精力来使它变得更好。将这些原则牢记于心,理解它们所存在的意义以及它们想要解决的问题,这样会使你的工作变得更加容易、更具条理性,但是同时记住,这并不是一件容易的事,相反,你应当在构思软件时,花相当多的事件思考如何更好地实践这些原则。 试着让自己设计的软件系统具备可适应性,这种适应性可以抵御各种不利的变化以及潜在的错误,这样自然而然地可以使你少加班和早回家(译者注:看来加班是每个程序员都要面临的问题啊) 译者注这是SOLID原则中我所接触和了解较少的一个原则,但经过仔细思考后,发现其实我们还是经常会在实际工作中运用它的。 在许多面向相对的编程语言中,关于对象的继承机制中,都会提供一些内部变量和状态的修饰符,比如 那么问题来了,一些静态语言有这些修饰符,但是像 除了在编程语言层面,在前端实际工作中,你可能会听到一个叫作 写在最后经过这五篇文章,我们来分别总结一下这五条基本原则以及它们带来的好处:
可以注意到我这里刻意使用了 为了便于对比,特别绘制了下面的表格,希望大家从真正意义上做到将这些原则牢记于心,并付诸于行。
Note: (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- iphone – 如何在Objective-C中的视图控制器之间传递对象?
- flex 学习笔记 RemoteObject(2)
- swift – 将Realm与Amazon DynamoDB一起使用
- [设计模式as3版]五.工厂方法
- Swift 2 iOS – 按创建日期排序的文件列表 – 更简洁的解决
- 在Swift中返回struct的可变版本
- Swift 正向传值以及利用闭包(closure)实现反向传值(七)
- c# – 当使用AutoMapper时,此平台不支持IDictionaryFactory
- xml – 如何在不更改名称的情况下在不同的命名空间中扩展复
- 正则表达式 – 使用FilesMatch进行.htaccess缓存