软件也像人一样,具有生命力,从出生到死亡,会经历多种变化。软件架构设计也不是一蹴而就的,是不断地演进发展。但为了能较好的发展,在软件设计时需要考虑一些原则。
清晰原则:使用简洁接口,简单部件组合
- 编程的本质就是要控制复杂度,后期维护会占用大部分的时间。
- 降低整体复杂度,用清晰的接口把若干简单模块组合成一个复杂的系统。
- 对外隐藏细节,“不要与陌生人说话”。
- 多数问题局限天一个局部,不要影响到全局。
小结:本质是分而治之,复杂问题简单化,抽象框架,有序组全。
清晰原则:清晰胜于机巧
- 代码直白易懂,代码是给人看的,不是给机器看的。
- 维护代码的人,其中也你包括你自己,善待代码,就是善待自己。
- 不要了一点性能提升而引入复杂的算法,不要为了炫耀技能而编写晦涩难懂的代码。
- 复杂晦涩的代码是Bugs的温床,高昂的维护成本将抵消可怜的性能提升。
小结:代码简洁晚懂,谨慎引入复杂度。
简洁原则:设计简洁,降低复杂
- 复杂问题简单化是一个设计者的能力体现,避免不必要的使用问题复杂化的因素。
- 在市场导向下,在不良的架构上很容易堆砌花哨无用的新特性,导致软件趋于复杂。
- 在进度压力下,不会做出各种折中的个性与不和谐的新特性开发。
小结:过滤排序需求,追求稳定简洁。
组合原则:组合,接拼,编排
- 把复杂问题分解为一条程序处理链条。
- 程序之间可以通信,前者的输出是后者输入。
- 通信方式尽量采用简单协议,并且与语言无关。
- 组合程序之间无内部状态依赖,互相独立,处理链上下游不做假设,可替换。
小结:面向微服务的设计。
透明原则:设计与实现分离可见
- 设计简单,包括流程、数据结构、接口,在代码级别容易理解。
- 支行时刻可通过输出信息或者接口查询运行状态,可控制程序是否正确运行。
- 执行调试链、健康状态可跟踪、可分析。
小结:简单设计,简明实现,状态监控。
吝啬原则:除非别无它法,不要编写庞大的程序
- 程序庞大包括两个方面:程序体积大与程序复杂而维护困难。
- 模块和函数尺寸都有一个上限,比如单模板代码10K,单函数100L。
- 导致程序庞大的因素:先天设计不良,后天维护增加新特性。
小结:合理设计,考虑扩展性,对庞大保护警惕。
吝啬原则:有的放矢,按需分配资源
- 若可能,当一个事情发生时候再分配资源,而不是预先分配,初始化时静态分配最小资源,随着业务动态增加资源分配。
- 异步消息环境下,消息通信带来上下文切换,代价高昂。
- 必要时对消息数据合并,打包发送,减少对网络资源的消耗。
- 业务处理与消息发送分离,数据组织独立,统一打包发送。
- 控制并发实例的数据,分批处理,防止下游过载各实例之间的恶性竞争通信资源。
小结:按需分配资源,珍惜消息通信机会。控制并发,避免消息突发。
健壮原则:健壮源于透明与简洁
- 软件在超过设计者设想的意外场景下也能够运行良好。
- Bugs是一种异常,复杂性和特殊处理都是Bugs的温床。
- 逻辑透明,接口简单有助于减少Bugs,即使出现Bugs也容易排除。
- 软件设计时要考虑异常输入,边界条件,和过载场景。
- 软件模块之间要处理流程上考虑能务匹配,流控,过载保护。
小结:简单透明,处理能力匹配;考虑异常场景,提升健壮性;匹配上下游处理能力,闭环控制为主,开环控制为辅。
表示原则:把知识叠入数据,简化统一处理逻辑
- 数据抽象建模,使用数据之间的关联关系来体现业务逻辑,或者领域知识,使得业务处理逻辑代码简单一致稳定。
- 可通过修改数据模型可以支持新业务,逻辑处理代码不用修改。
小结:领域模型驱动设计,数据数据提练来描述业务本质。
缄默原则:只输出有用的信息
- 保持沉默,良好的行为是默默地工作,决不唠唠叨叨,碍手碍脚,程序也是如此。
- 输出大量无用的信息会耗费资源,淹没重要信息,干扰维护人员定位问题。
小结:沉默是金,惜时亦如金。输出的信息是必要的,有用的,不重复的。
补救原则:异常时候,干净退出,输出详细信息
- 当程序出现异常时,输出必要信息,使用简单方法结束。
- 宽容地接受,严格地发送,提升程序的容错能力。
- 程序要么正确执行,要么响亮倒塌。
小结:看待这个问题一分为二,选择异常恢复方法要保证对用户的业务逻辑影响最小为基本原则。因此并不是说所有的异常都是进程退出重启,有时可以告警,事件通知,让管理员来处理,给出明确的修复方案说明。
经济原则:宁花机器一分钟,不花程序员一秒
- 对于一个问题,选择算法的时候宁愿选择一个简单,但可能是效率低一些的算法,也不要选择一个性能好但复杂度很高的算法。
- 硬件的进步来弥补性能的下降,换来的是程序简单可靠,维护成本低。
小结:辩证地看问题,选择简单,把复杂留给机器,解放程序员。
生成原则:避免手工Hack,让程序去生成代码
- 人最不适合干大量重复细致的工作,或多或少都会出错,因此导致Bugs。
- 与此相反,计算机最适合干规则明确,重复枯燥的工作,而且不会出错。
- 对于一个规则明确的工作,一种可行的办法就是编写一个我程序,根据输入规则生成代码,让生成的代码去解决问题。
小结:分析规则,能按照规则生成代码,可检查发现问题。
优化原则:雕琢前先有原型,跑之前先学会走
- 90%的功能能够实现,比100%功能永远不能实现要好得多。
- 先求可行,再求正确,最后求快。
- 原型可能有效地解决关键技术,保证系统是可以实现的。
- 让客户看到可以执行的原型,对需求的理解更为准确,防止做无用功。
- 过早优化是万恶之源,过早地优化会破坏程序结构,代码与数据结构杂乱无章;过早优化不清晰系统瓶颈,局部优化破坏整体优化。
小结:性能是设计出来,不是优化来出来。
多样原则:拒绝封闭和唯一
- 系统开放,只有开放,才能进步,才能得到最为广泛的验证。
- 开放,可扩展,用户可定制,符合实际需求和获到高可靠性。
小结:系统开发,留给用户定制空间,毕竟需求具体多样性,我们不能穷尽,最终只有用户才能准确理解自己的需求。
同源原则:避免重复,数据同源
- 重复的代码不仅仅是浪费,也是Bugs的滋生之地。
- 出现大段代码重复,说明开发者缺少对代码控制与抽象提炼能务,优秀的工程轻易不会在进度压力下让步。
- 数据重复危害更大,一则浪费存储空间,二则容易出现不一致。
- 在性能允许的情况下,尽量共享数据或者按需订阅,避免全量订阅。
- 如果上述条件不具备,那么采用数据同源技术和一致性校验来保证数据的一致性。
小结:拒绝重复,必须重复的时候考虑通过工具同源和一致性校验。
扩展原则:设计时着眼未来,未来总比想象来得快
- 对于一个一次性的程序来讲,架构和设计只会带来成本上升,复杂度提高,与性能的下降。
- 当开发一个支持多种产品的软件平台,其生命周期可能是十年以上。用户的需求是不断地变化,硬件环境也是不断地变化。
- 只有适应变化,不能拒绝变化,可扩展性是软件非功能属性中,在大部分情况下排在可靠性,高性能之前
小结:不对外界进行假设,这些假设在时间面前都是脆弱的。
扩展原则:给雪球寻找一个核心
- 雪球都有一个内核,尽管可能是一粒不起眼的小石头。
- 常说软件要内聚,内聚需要核心。
- 微内核,插件化机制。框架即核心,框架之上定制好插件,构建系统。
- 从问题知识域发现稳定部分进行抽象提升,成为系统的框架。
小结:从框架的角度来看系统,框架之间的有机组合构建系统,系统的演化就是在框架稳定情况下增加替换相关的插件。
扩展原则:扩展就是少修改
- 软件可扩展性是有前提和应用场景的,不存在可以随意扩展的软件。
- 增加新功能做到不修改既有软件,代码无需修改,通过修改数据模型或者重新配置获得新特性。
- 修改集中在新增加的软件之上,也就是说在增加新特性时,对老代码封闭,对新代码新特性开放。
小结:系统开闭原则,修改对系统稳定不构成影响。
注:以上内容参考了公司设计原则。