资讯专栏INFORMATION COLUMN

读懂 SOLID 的「接口隔离」原则

wing324 / 807人阅读

摘要:接口隔离原则是什么客户端代码不应当被迫依赖于它们不需要的方法。

这是理解SOLID原则,关于接口隔离原则如何帮助我们创建简单的抽象接口,并使客户端代与接口之间存在的更少的依赖关系。
接口隔离原则是什么
Clients should not be forced to depend on methods that they do not use.

客户端代码不应当被迫依赖于它们不需要的方法。

这个原则本身与单一职责原则关系十分紧密,它意味着当你在定义你的抽象层代码时,不应当在客户端代码在实现抽象逻辑时,暴露一些客户端代码不需要使用或者关心的方法。

进一步说明的话,就是当你有意地在抽象层中暴露的方法时,这意味着所有实现这些抽象逻辑的客户端代码都必须要实现所有的抽象方法,尽管这些方法并不一定都对客户端代码有意义。

将你的接口的保持精简和小颗粒度,并且不要在它们中间增加无用的抽象方法,当你在对新的抽象接口进行命名时,你就会拥有更好的选择,因为你已有了若干小颗粒的命名类型。这样做的意义在于当你在需要提供一个更加大颗粒度的抽象接口时,你可以拥有足够的灵活性来将已有的小颗粒度接口进行组合。

如何实践接口隔离原则

这个例子是关于一个ATM用户界面的抽象接口,这个接口会处理诸如存款请求、取款请求等逻辑,从这个例子中我们会了解到,我们如何对这个接口进行隔离,使其进一步划分为多个独立的、更加具体的若干接口。

首先我们应当有一个工具函数库接口,这个接口会描述我们想要暴露的关于byte操作逻辑的方法,让我们创建这样一个接口,如下

type ByteUtils interface {
    Read(b []byte) (n int, err error) // Read into buffer
    Write(b []byte)(n int, err error) // Write into buffer
    Trim(b []byte, exclusions string)[]byte // Trim buffer by removing bytes from the exclusion chars
}

它可以正常工作一段时间,但是很快我们就会发现以下两个问题:

它的命名ByteUtils太过于通用,如果我们仅通过命名本身,基本无法获取任何具体的信息

当使用它时,会有一些古怪的感觉,因为当你根据不同的优化场景来按不同逻辑实现trim方法时,你所实现的readwrite几乎没什么差别,但是你却需要重复地实现它们,同时在某些不需要读或者写的场景,仍然需要实现它们。

所以它虽然能够正常工作,但是却不够好。

我们可以通过创建三个更精简、更具体的接口来替代原先通用的接口:

type Reader interface {
    Read(b []byte) (n int, err error) 
}
type Writer interface {
    Write(b []byte)(n int, err error) 
}
type Trimmer interface {
    Trim(b []byte, exclusions string)[]byte 
}

这种颗粒度比较细的接口也可以称为角色接口,因为它们更易于重构和改变,甚至对于已经定义好的角色和目的也可以很容易的进行重新部署和定义。

在这三个基础上,我们可以通过组合它们来获取一个更有关联性的接口列表,比如:

type ReadWriter interface {
    Reader
    Writer 
}
type TrimReader interface {
    Trimmer
    Reader
}

这意味客户端代码拥有了可以根据它们各自的需求来组合抽象层接口的灵活性,这样就会避免在实现抽象接口时不必要的麻烦(比如必须要实现某些无用的方法),比如上面的TrimReader的实现并未包含多余的Write方法的声明。

总结

正如你所看到的,通用的接口往往会无意识的将自己和类的实现耦合在了一起,所以你应当尽量的避免这种情况的发生。在设计接口时,你应当时刻提醒自己,我是否需要使用所有在接口中声明的方法呢?如果不是的话,将接口细分为更多个更精简、更具体的接口。

正如甘地曾经说过:

你的行动决定你的习惯,你的习惯决定你的价值,你的价值会决定你的命运。

如果在架构中,你每次都会经过仔细思考,会按照好的模式来进行设计,它将会成为一种习惯,自然慢慢会转变为你的价值或者原则,最终则会成为你的命运,比如成为了一个始终给予完善解决方案的软件架构师。

我的观点是,始终通过挑战自己来变的更好,在某些时刻,你可能会遇到问题,但是往往你可能已经拥有了答案。

Happy coding!

译者注

对于接口隔离原则的理解,我一直觉的它本身其实是单一职责原则的一个扩展,但是它们之间也有细微的不同:

单一职责原则往往面向实现层,比如具体的类或者某个方法

接口隔离原则往往面向抽象层,比如一些抽象类或者抽象方法

所以将两个原则结合起来看的话,可以很容器得到当时提出这两个原则的人的意图,那就是一定要时刻保持简单

在实际工作中,我深知保持简单是一件十分困难的事情,因为工程师本身的使命便是解决问题,而问题往往充满了未知性,而未知性往往代表着改变,这还没有考虑到在项目实施过程中,产品经理天马行空的设计思路,客户们五花八门的需求等等。在这些外界条件下,我们的代码往往会变得复杂无比,充满了各种反模式和冗余代码,最终会使自己陷入无尽的bug修复和维护工作中,怎么还会有时间进行自我提升呢?

所以,为了能够按时下班,为了能够及早回家,为了能够让我们的拥有更多的时间来提升自己和陪伴家人,在软件设计之初,尽可能地针对将来所面临的改变,在设计层面降低软件抽象模块间的耦合程度,在项目实施时,提高每个具体实现模块内部的内聚程度,同时使它们保持简单,这样便是一个好的开始。

关注公众号 全栈101,只谈技术,不谈人生

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/107333.html

相关文章

  • 读懂 SOLID 「里氏替换」原则

    摘要:什么是里氏替换原则某个对象实例的子类实例应当可以在不影响程序正确性的基础上替换它们。除了在编程语言层面,在前端实际工作中,你可能会听到一个叫作的概念,这个概念我认为也是里氏替换原则的一直延伸。 这是理解SOLID原则,关于里氏替换原则为什么提倡我们面向抽象层编程而不是具体实现层,以及为什么这样可以使代码更具维护性和复用性。 什么是里氏替换原则 Objects should be rep...

    vibiu 评论0 收藏0
  • 读懂 SOLID 「依赖倒置」原则

    这是理解SOLID原则中,关于依赖倒置原则如何帮助我们编写低耦合和可测试代码的第一篇文章。 写在前头 当我们在读书,或者在和一些别的开发者聊天的时候,可能会谈及或者听到术语SOILD。在这些讨论中,一些人会提及它的重要性,以及一个理想中的系统,应当包含它所包含的5条原则的特性。 我们在每次的工作中,你可能没有那么多时间思考关于架构这个比较大的概念,或者在有限的时间内或督促下,你也没有办法实践一些好...

    Snailclimb 评论0 收藏0
  • 读懂 SOLID 「开闭」原则

    摘要:事件驱动模型对于一些复杂的事件驱动模型,比如拖拽,往往使用开闭原则会达到意想不到的效果。 这是理解SOLID原则,介绍什么是开闭原则以及它为什么能够在对已有的软件系统或者模块提供新功能时,避免不必要的更改(重复劳动)。 开闭原则是什么 Software entities (classes, modules, functions, etc.) should be open for ext...

    awkj 评论0 收藏0
  • PHP面向对象设计五大原则SOLID)梳理总结

    摘要:设计原则梳理,参考核心技术与最佳实践敏捷开发原则模式与实践,文章面向对象设计的五大原则设计模式原则单一职责原则定义特性仅有一个引起类变化的原因一个类只承担一项职责职责变化的原因避免相同的职责分散到不同的类,功能重复问题一个类承担的职责过多, PHP设计原则梳理,参考《PHP核心技术与最佳实践》、《敏捷开发原则、模式与实践》,文章PHP面向对象设计的五大原则、设计模式原则SOLID 单一...

    王晗 评论0 收藏0
  • 什么是SOLID原则(第2部分)

    摘要:让我们启程吧在原则中,最具神秘色彩的就是里氏替换原则,简称了。这显然违背了里氏替换原则。这些神奇的其实可以通过遵守里氏替换原则来避免。如前所述,遵循里氏替换原则也有助于开闭原则的实现。 翻译自:What’s the deal with the SOLID principles? (part 2) 在文章的 第1部分,我们主要讨论了前两个 SOLID 原则,它们分别是单一职责原则和开闭...

    xiaoxiaozi 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<