SOLID总结

之前已经把SOLID的每人原则都阐述过一遍,此篇主要是从全局角度复述一下SOLID,对于细节概念再做少许补充

SOLID原则的历史已经很悠久,早在20世纪80年代末期,都已经开始逐渐成型了

通常来讲,想构建一个好的软件系统,应该从写整洁的代码开始做起。毕竟如果建筑的砖头质量不佳,那么架构所能起到的作用也会很有限。反之亦然,如果建筑的架构设计不佳,那么其所用砖头质量再好也没用

SOLID原则的主要作用就是告诉我们如何将数据和函数组织成为类,以及如何将这些类链接起来成为程序,类似于指导我们如何将砖块彻成墙与房间

对照几张前辈们画的图,看图说话

这张图把SOLID的整体关系描述清楚了,不再是把各个原则单独看待

单一职责是所有设计原则的基础,开闭原则是设计的终极目标。

里氏替换原则强调的是子类替换父类后程序运行时的正确性,它用来帮助实现开闭原则。

而接口隔离原则用来帮助实现里氏替换原则,同时它也体现了单一职责。

依赖倒置原则是过程式编程与OO编程的分水岭,同时它也被用来指导接口隔离原则


这些原则每个单独看都是简单的,但他们却是优秀代码的指导思想,不得不常读,常思;犹如设计模式,很多时候你感觉懂了,不过只是懂了介绍模式的示例,并没有真正理解模式

反观这些原则,道理类似

如SRP:是公认最容易理解的原则,却是被违反得最多的设计原则之一;再比如ISP,看着简单,更小和更具体的瘦接口比庞大臃肿的胖接口好,很多时候都没有明白接口的定义

在实现编写代码时,只要是service都会加上一个 service interface,但想想,从项目开启到后期维护,几乎没有一个 service interface 有一个以上的实现,那为什么要加个接口呢?美其名曰面向接口编程,其实是人云亦云,让自己也让别人看着是那么一回事而已

面向接口编程所指的“接口”并非Java语言中的interface类型,而是指面向调用者对外暴露的接口,代表一种交互与协作,是对信息的隐藏和封装,而不是具体的interface类型。即使是普通的java方法仍然满足隐藏细节的原则,如果是public的,就可以认为该方法是“面向接口设计”中的接口,也就是说:不要针对实现细节编程,而是针对接口编程

接口之所以存在,是为了解耦。开发者常常有一个错误的认知,以为是实现类需要接口。其实是消费者需要接口,实现类只是提供服务,因此应该由消费者(客户端)来定义接口。理解了这一点,才能正确地站在消费者的角度定义Role interface,而不是从实现类中提取Header Interface。

对于Role interface 与 header interface , Martin Fowler给出了定义:

A role interface is defined by looking at a specific interaction between suppliers and consumers. A supplier component will usually implement several role interfaces, one for each of these patterns of interaction. This contrasts to a HeaderInterface, where the supplier will only have a single interface

如果你先定义了一个类,然后因为你需要定义接口对其抽象,然后就简单地将这个类的所有公有方法都提取到抽象的接口中,这样设计的接口,被Martin Fowler称为Header Interface,这种接口也正是胖接口的来源,而 Role interface 才是能达到瘦接口目标

想起一位投资前辈说的话,成功就是对简单道理的深刻理解和灵活运用;我们很多时候有种无力感,为什么这么简单的道理都做不好,落地不了呢?其实是没有深刻理解而自以为懂了


Kent Beck对软件设计的定义:软件设计是为了在让软件在长期范围内容易应对变化

为了软件更容易应对变化,就需要符合软件的道:高内聚低耦合

单一职责和开放封闭,更多的在强调类划分时的高内聚;而里氏替换,依赖倒置,接口隔离则更多的强调类与类之间协作接口(即API)定义的低耦合,单独应用SOLID的某一个原则并不能让收益最大化。应该把它作为一个整体来理解和应用,从而更好地指导软件设计。

这个同心圆的原图本来是:

要实现道就得遵循正交设计四原则:

  1. 消除重复
  2. 分离关注点
  3. 缩小依赖范围
  4. 向稳定的方向依赖

「正交设计」的理论、原则、及其方法论出自前ThoughtWorks软件大师「袁英杰」先生。这一块对我来讲很新颖,消化之后再总结


这幅图揭示了模块化设计的全部:首先将一个低内聚的模块首先拆分为多个高内聚的模块;然后再考虑这多个模块之间的API设计,以降低这些高内聚的软件单元之间的耦合度。

除了内聚与耦合之外,上面这幅图还揭示了另外一种关系:正交。具备正交关系的两个模块,可以做到一方的变化不会影响另外一方的变化。换句话说,双方各自独自变化,互不影响。

而这幅图的右侧,正是我们模块化的目标。它描述了永恒的三方关系:客户,API,实现,以及它们之间的关系。这个三方关系图清晰的指出了我们应该关注的内聚性,耦合性,以及正交性都发生在何处

总结:

软件的复杂性已经是世界性难题,但最原始的道是相当简单的,就是要高内聚低耦合,在追求道的过程中,前人总结出了很多原则,这些原则相互协作、相互碰撞,我们需要平衡,取舍,这考验架构师的功力,也要求架构师对这些基本概念有深刻理解

参考:

《正交设计,OO 与 SOLID》

你真的了解SOLID吗?

RoleInterface

朱兴生 wechat
最新文章尽在微信公众号『码农戏码』