复杂度中的可扩展性

2017, Dec 01    

可扩展性指系统为了应对将来需求变化而提供的一种扩展能力,当有新的需求出现时, 系统不需要或者仅需要少量修改就可以支持,无须整个系统重构或者重建。

设计具备良好可扩展性的系统,有两个基本条件:正确预测变化、完美封装变化

预测变化

软件系统在发布后还可以不断地修改和演进,这就意味着不断有新的需求需要实现。

“唯一不变的是变化”,如果按照这个标准去衡量,架构师每个设计方案都要考虑可扩展性。例如,架构师准备设计一个简单的后台管理系统,当架构师考虑用 MySQL 存储数据时,是否要考虑后续需要用 Oracle 来存储?当架构师设计用 HTTP 做接口协议时,是否要考虑要不要支持 ProtocolBuffer?甚至更离谱一点,架构师是否要考虑 VR 技术对架构的影响从而提前做好可扩展性?如果每个点都考虑可扩展性,架构师会不堪重负,架构设计也会异常庞大且最终无法落地。但架构师也不能完全不做预测,否则可能系统刚上线,马上来新的需求就需要重构,这同样意味着前期很多投入的工作量也白费了。

综合分析,预测变化的复杂性在于:

  • 不能每个设计点都考虑可扩展性。
  • 不能完全不考虑可扩展性。
  • 所有的预测都存在出错的可能性。

应对变化

预测变化是一回事,采取什么方案来应对变化,又是另外一个复杂的事情。 即使预测很准确,如果方案不合适,则系统扩展一样很麻烦。

第一种应对变化的常见方案是将“变化”封装在一个“变化层”,将不变的部分封装在一个独立的“稳定层”

无论采取哪种形式,通过剥离变化层和稳定层的方式应对变化,都会带来两个主要的复杂性相关的问题。

  • 系统需要拆分出变化层和稳定层

对于哪些属于变化层,哪些属于稳定层, 很多时候并不是像前面的示例(不同接口协议或者不同数据库)那样明确,不同的人有不同的理解。

  • 需要设计变化层和稳定层之间的接口

对于稳定层来说,接口肯定是越稳定越好;但对于变化层来说, 在有差异的多个实现方式中找出共同点,并且还要保证当加入新的功能时原有的接口设计不需要太大修改, 这是一件很复杂的事情。

第二种常见的应对变化的方案是提炼出一个“抽象层”和一个“实现层”。

抽象层是稳定的,实现层可以根据具体业务需要定制开发,当加入新的功能时,只需要增加新的实现,无须修改抽象层。这种方案典型的实践就是设计模式和规则引擎。

规则引擎和设计模式类似,都是通过灵活的设计来达到可扩展的目的,但“灵活的设计”本身就是一件复杂的事情, 不说别的,光是把 23 种设计模式全部理解和备注,都是一件很困难的事情。封装变化,隔离可变性

衡量一个软件系统具备良好可扩展性主要表现但不限于:

  • (1)软件自身内部方面。在软件系统实现新增的业务功能时, 对现有系统功能影响较少,即不需要对现有功能作任何改动或者很少改动。
  • (2)软件外部方面。软件系统本身与其他存在协同关系的外部系统之间存在松耦合关系,软件系统的变化对其他软件系统无影响, 其他软件系统和功能不需要进行改动。反之,则是一个可扩展性不好的软件系统。

设计具备良好可扩展性的系统,有两个思考角度:

  • (1)从业务维度。对业务深入理解,对可预计的业务变化进行预测。
  • (2)从技术维度。利用扩展性好的技术,实现对变化的封装。