一个好的系统应该具备可扩展性、灵活扩展性。
一个系统越复杂,就越应该让他易维护,而实现可复用性是实现可维护性的一种手段。
可维护性
什么是可维护性?
可维护性指的是系统被修改能力和可修复能力。
细化来说:
- 被修改能力指的是系统随需求、环境、容量等需求变化带来的修改适应。
- 可修复能力指的是在系统发生故障时,可以快速排查问题并修复问题的能力,并且低成本方式实现流程的正常运行的可能。
总结来说,可维护性体现在以下几个特性:
- 易分析:系统发生故障时,可以快速定位并分析出原因;
- 易改变:面对新需求时,拥有快速实现需求的能力,体现在代码、组件、文档等方面;
- 易测试:系统的变更可以快速被验证;
- 稳定性:兼容新变更的基础之上,不影响原有系统运行的能力;
那如何度量系统的可维护性呢?
一、可理解性
通过阅读代码或设计文档快速了解系统架构、功能、运行逻辑的难易程度。
一个可理解性高的系统具备以下特征:
- 模块化:系统各个模块结构良好、功能完整;
- 程序代码清晰;
- 编程风格一致:(代码风格、设计风格一致);
- 完整性:(对数据数据有完整性检查);
- 有意义的数据命名和函数命名;
二、可靠性
可靠性表明一个系统在给定的时间段内正确执行的概率。
可以用MTTF作为可靠性的预测指标。
三、可测试性
可测试性表明可以用测试的方法来验证程序正确性的难易度。
系统的可测试性取决于系统的可理解性、复杂性、设计的合理性、测试用例的覆盖度等。
四、可修改性
可修改性描述了橙心可以被正确修改的难易程度。
一个可修改的程序应该是可理解的、通用的、简单的、灵活的。通用性是指程序适用于各种功能变化而无需修改。灵活性指的是容易的对系统进行修改。
系统的可维护性不但和架构师采用的分析设计方法和技术熟练程度有关,还和项目管理技术有密切的联系,所以可以建立如下规范:
- 开发人员有统一的约束规范;
- 从需求--业务分析--架构设计--编码实现--测试发布--线上运营是否采用了统一的文档结构和文档形式;
- 是否有完整的代码说明文档;
- 是否有持续更新保存规范化的测试用例信息;
复用性
复用是架构思维中非常重要的一种思维,是面向对象的核心。所以我们会做很多业务能力组件化、组件能力服务化、共享平台、业务中台、公共服务下沉等动作,本质上都是为了提升系统能力或业务能力的复用。
实现好的的复用性,需要我们做很多取舍的设计,很多人只考虑了代码的复用性而忽略了更高层次的复用,当然代码的复用是最基本的,但为了提高复用的价值,应该从更高层次看待复用性。
复用性的好处
- 有较高的生产效率
- 有较高的系统质量
- 可以提高系统的可维护性
复用分类
复用可以分为代码复用、算法复用、数据结构复用、设计复用、分析复用、组件复用、服务复用、系统复用、功能复用等。
代码复用是最常用的复用方式,代码复用一般可以通过共享通用类、函数集合来实现,即便是最差的情况下,代码复用也可以通过拷贝和修改源代码来实现。
代码复用的最大好处是可以减少你的代码量,无形中降低了维护成本。当然更高层面或更大量代码的复用,可以通过构建大量代码组件库实现。
做好代码复用,可以遵守以下原则。
面向接口编程,是面向对象设计的第一个基本原则。
面向接口编程是要我们先把业务逻辑线抽象出来作为接口,具体业务实现可以通过对该接口的实现来完成。
当客户需求变化时,只需要编写改业务逻辑的实现类,或修改配置文件即可,而不需要现成代码,减少了系统的侵入。
组合大于继承
组合大于继承是面向对象的第二个基本原则。
继承是在程序开发过程中通过重构得到的,而不是程序设计之初就采用多重继承去实现代码扩展性。这也是继承常常导致代码难以维护的一个主要原因。
其实组合大于继承这个思想不仅仅是代码编写、面向对象的思想,在很多中台框架层面同样有类似的设计思想,比如中台系统下不同业务个性化逻辑的实现本质上就是原子功能的再组合实现的。
当然继承也并不是一无是处,如果想用好某些特性,我们就需要考虑他的优缺点,以便在合适的时候选择不同的方案。
继承的优缺点
优点:
- 继承直观,关系在编译时静态定义;
- 被复用的实现易于修改,sub实现可以覆盖super实现;
缺点:
- 无法在运行时变更从super继承来的实现;
- sub的部分实现通常定义在super里面;
- sub直面super的实现细节,破坏了封装;
- super的实现的任何变更,都会强制sub的实现变更,因为他们联系在一起了;
- 如果新问题不能通过继承实现,则必须重写super或继承来实现;
用好继承的一个方法是对协议层抽象,也就是通过继承尽量解决那些标准化的。
组合的优缺点
优点:
- 不会破坏封装,因为只通过接口来访问对象;
- 减少依赖关系,因为是通过接口来定义的;
- 可以在运行时将任意对象替换为其他同类型对象;
- 可以保持类的封装以专注于单一任务;
- 类和他的层次结构保持整洁,不至于过度膨胀而无法管理;
缺点:
- 涉及对象多;
- 系统行为将依赖不同对象关系,而不是定义于单个类中;
- 现成的组件总是不太够用,从而导致不停的定义新的对象;
变与不变的分离
变与不变的分离是面向对象设计的第三个原则。
如果使用继承达到复用的目的,可以在抽象基类中定义好不可变的部分,而由其子类实现可变的部分,不可变部分不需要重复定义,且便于维护。
如果使用组合达到复用的目的,可以定义好不变的部分,可变的部分可以由不同的组件实现,根据需求在运行时动态配置。
经过拆分,我们可以将更多的时间关注到可变的部分,对于组合技术而言,每个组件只需完成较小的功能,相互之间耦合松散,复用率高,通过组合,可以获得新功能。
控制代码长度
太长的方法难以理解,如果太长了,我们需要进行重新设计。方法重构有以下三个原则:
- 三十秒原则:如果程序员不能在30s内说明白函数的why、what、how的话,就说明需要维护;
- 单一职能原则:如果发现一个方法不止做一件事情,那么就需要拆出来;
- 一屏原则:如果一个函数的代码长度超过一个屏幕,就应该拆成多个小的函数,代码尽量短,只做一件事情;
减少case、if语句
避免代码中过多使用switch/case、if/else语句,代码的可读性差,同时违背了面向对象的原则。
减少参数个数
大量参数传递的方法通常难阅读。可以将所有的参数封装到一个对象中完成参数的传递,封装是面向对象的基本要求之一,每个对象功能尽量简单也是面向对象的基本原则。
通过抽象类实现多重继承
在一些场景下,提供一个抽象基类有利于做特性扩展,一个类可以实现多个接口,从而实现了“多重继承”,为设计提高了灵活性。
减少对变量的直接访问
不要把类的属性直接暴露给其他类,而应该通过访问方法去保护他们,如果属性名字修改,只需要修改他的访问方法,而不是修改所有相关代码。
拆分过大的类
如果一个类有太多方法(超过50个),那么他可能做的事情太多了,应该试着将它们的功能拆分到不同的类中。
作用不同的对象应该拆分
有同样数据结构,但不同作用的对象应该拆分,比如do、po、vo等。
设计复用
设计复用可以涵盖从用例设计、标准文档、领域模型、过程指导全研发周期。
设计复用提升了项目间的一致性,减少了各个项目的管理成本。
为了引入不必要的复杂度,需要设计一个复用的底线,就是当你需要时去使用它,比如从现有系统的设计结果中提取一些可复用的设计组件,将这些用于新的系统设计中。
分析复用
分析复用听起来比较抽象,它是针对业务领域中某些设计或者问题抽象出来的组件,受技术影响较小,所以复用价值更大。
比如领域驱动思想,在很多系统中分析时一致的。
适当的使用复用,可以提高可维护性,比如一些组件的复用。
其实复用是一种态度,在设计系统架构时,就应该考虑后续的架构应该以怎样的方式在其他场合被复用。另一种方式可以主动分享你的工作成果,这样大家才能知道他,才可以复用他。