架构设计原则总结

在系统设计时,应该多思考墨菲定律: 1、任何事都没有表面看起来那么简单; 2、所有的事情比你预计的时间长; 3、可能出错的事总会出错; 4、如果你担心某

在系统设计时,应该多思考墨菲定律: 
1、任何事都没有表面看起来那么简单; 
2、所有的事情比你预计的时间长; 
3、可能出错的事总会出错; 
4、如果你担心某种情况发生,那么它就更有可能发生; 
在系统划分时,也要考虑康威定律: 
1、系统架构是组织架构的反应; 
2、应该按照业务闭环进行系统拆分/组织架构划分,实现闭环/高内聚/低耦合,减少沟通成本; 
3、如果沟通出现问题,那么就应该考虑进行系统和组织架构的调整; 
4、在适合的时机进行系统拆分,不要一开始就把系统/服务拆分得非常细,虽然闭环,但是每个人维护系统多,维护成本高。 

1. 单一职责原则(Single Responsibility Principle - SRP)

原文:There should never be more than one reason for a class to change.

译文:永远不应该有多于一个原因来改变某个类。

理解:对于一个类而言,应该仅有一个引起它变化的原因。说白了就是,不同的类具备不同的职责,各施其责。这就好比一个团队,大家分工协作,互不影响,各做各的事情。

应用:当我们做系统设计时,如果发现有一个类拥有了两种的职责,那就问自己一个问题:可以将这个类分成两个类吗?如果真的有必要,那就分吧。千万不要让一个类干的事情太多!

2. 开放封闭原则(Open Closed Principle - OCP)

原文:Software entities like classes, modules and functions should be open for extension but closed for modifications.

译文:软件实体,如:类、模块与函数,对于扩展应该是开放的,但对于修改应该是封闭的。

理解:简言之,对扩展开放,对修改封闭。换句话说,可以去扩展类,但不要去修改类。

应用:当需求有改动,要修改代码了,此时您要做的是,尽量用继承或组合的方式来扩展类的功能,而不是直接修改类的代码。当然,如果能够确保对整体架构不会产生任何影响,那么也没必要搞得那么复杂了,直接改这个类吧。

3. 里氏替换原则(Liskov Substitution Principle - LSP)

原文:Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.

译文:使用基类的指针或引用的函数,必须是在不知情的情况下,能够使用派生类的对象。

理解:父类能够替换子类,但子类不一定能替换父类。也就是说,在代码中可以将父类全部替换为子类,程序不会报错,也不会在运行时出现任何异常,但反过来却不一定成立。

应用:在继承类时,务必重写(Override)父类中所有的方法,尤其需要注意父类的 protected 方法(它们往往是让您重写的),子类尽量不要暴露自己的 public 方法供外界调用。

4. 最少知识原则(Least Knowledge Principle - LKP)

原文:Only talk to you immediate friends.

译文:只与你最直接的朋友交流。

理解:尽量减少对象之间的交互,从而减小类之间的耦合。简言之,一定要做到:低耦合,高内聚。

应用:在做系统设计时,不要让一个类依赖于太多的其他类,需尽量减小依赖关系,否则,您死都不知道自己怎么死的。

5. 接口隔离原则(Interface Segregation Principle - ISP)

原文:The dependency of one class to another one should depend on the smallest possible interface.

译文:一个类与另一个类之间的依赖性,应该依赖于尽可能小的接口。

理解:不要对外暴露没有实际意义的接口。也就是说,接口是给别人调用的,那就不要去为难别人了,尽可能保证接口的实用性吧。她好,我也好。

应用:当需要对外暴露接口时,需要再三斟酌,如果真的没有必要对外提供的,就删了吧。一旦您提供了,就意味着,您将来要多做一件事情,何苦要给自己找事做呢。

6. 依赖倒置原则(Dependence Inversion Principle - DIP)

原文:High level modules should not depends upon low level modules. Both should depend upon abstractions. Abstractions should not depend upon details. Details should depend upon abstractions.

译文:高层模块不应该依赖于低层模块,它们应该依赖于抽象。抽象不应该依赖于细节,细节应该依赖于抽象。

理解:应该面向接口编程,不应该面向实现类编程。面向实现类编程,相当于就是论事,那是正向依赖(正常人思维);面向接口编程,相当于通过事物表象来看本质,那是反向依赖,即依赖倒置(程序员思维)。

应用:并不是说,所有的类都要有一个对应的接口,而是说,如果有接口,那就尽量使用接口来编程吧。

将以上六大原则的英文首字母拼在一起就是 SOLID(稳定的),所以也称之为 SOLID 原则。只有满足了这六大原则,才能设计出稳定的软件架构!但它们毕竟只是原则,只是四人帮给我们的建议,有些时候我们还是要学会灵活应变,千万不要生搬硬套,否则只会把简单问题复杂化

补充设计原则

1. 组合/聚合复用原则(Composition/Aggregation Reuse Principle - CARP)

当要扩展类的功能时,优先考虑使用组合,而不是继承。这条原则在 23 种经典设计模式中频繁使用,如:代理模式、装饰模式、适配器模式等。可见江湖地位非常之高!

2. 无环依赖原则(Acyclic Dependencies Principle - ADP)

当 A 模块依赖于 B 模块,B 模块依赖于 C 模块,C 依赖于 A 模块,此时将出现循环依赖。在设计中应该避免这个问题,可通过引入“中介者模式”解决该问题。

3. 共同封装原则(Common Closure Principle - CCP)

应该将易变的类放在同一个包里,将变化隔离出来。该原则是“开放-封闭原则”的延生。

4. 共同重用原则(Common Reuse Principle - CRP)

如果重用了包中的一个类,那么也就相当于重用了包中的所有类,我们要尽可能减小包的大小。

5. 好莱坞原则(Hollywood Principle - HP)

好莱坞明星的经纪人一般都很忙,他们不想被打扰,往往会说:Don't call me, I'll call you. 翻译为:不要联系我,我会联系你。对应于软件设计而言,最著名的就是“控制反转”(或称为“依赖注入”),我们不需要在代码中主动的创建对象,而是由容器帮我们来创建并管理这些对象。

其它设计原则

1. 不要重复你自己(Don't repeat yourself - DRY)

不要让重复的代码到处都是,要让它们足够的重用,所以要尽可能地封装。

2. 保持它简单与傻瓜(Keep it simple and stupid - KISS)

不要让系统变得复杂,界面简洁,功能实用,操作方便,要让它足够的简单,足够的傻瓜。

3. 高内聚与低耦合(High Cohesion and Low Coupling - HCLC)

模块内部需要做到内聚度高,模块之间需要做到耦合度低。

4. 惯例优于配置(Convention over Configuration - COC)

尽量让惯例来减少配置,这样才能提高开发效率,尽量做到“零配置”。很多开发框架都是这样做的。

5. 命令查询分离(Command Query Separation - CQS)

在定义接口时,要做到哪些是命令,哪些是查询,要将它们分离,而不要揉到一起。

6. 关注点分离(Separation of Concerns - SOC)

将一个复杂的问题分离为多个简单的问题,然后逐个解决这些简单的问题,那么这个复杂的问题就解决了。难就难在如何进行分离。

7. 契约式设计(Design by Contract - DBC)

模块或系统之间的交互,都是基于契约(接口或抽象)的,而不要依赖于具体实现。该原则建议我们要面向契约编程。

8. 你不需要它(You aren't gonna need it - YAGNI)

不要一开始就把系统设计得非常复杂,不要陷入“过度设计”的深渊。应该让系统足够的简单,而却又不失扩展性,这是其中的难点。

 

      系统设计的好坏在根本上决定了软件系统的优劣。可以说“差的系统设计必定产生差的软件系统”,但是不能保证“好的系统设计必定产生好的软件系统”。因为在设计之前有需求开发工作,在设计之后还有编码,测试和维护工作,无论哪个环节出了差错,都会把好事搞砸了。

据说上帝把所有的女士都设计成天使,可是天使们在下凡的时候,有些人双脚先着地,有些人脸先着地。上帝的这一疏忽让很多女士伤透了心。所以我们在开发软件的时候,一定要吸取这个教训。

一.合适性

系统设计的源头是需求,这是由商业目标决定的。高水平的设计师高就高在“设计出恰好满足客户需求的软件,并且使开发方和客户方”获取最大的利益,而不是不惜代价设计出最先进的软件。

评估体系结构好不好的第一个指标就是“合适性”,即体系结构是否符合适合于软件的“功能性需求”和“非功能性需求”。人们一般不会在需求文档中指定软件的体系结构,需求与体系结构之间并没有一一对应的关系,甚至没有明显的对应关系。所以设计师可以充分发挥主观能动性,根据需求的特征,通过推理和归纳的方法设计出合适的体系结构。经验不丰富的设计师往往把注意力集中在“功能性需求”而疏忽了“非功能性需求”,殊不知后者恰恰是最能体现设计水平的地方。

比如设计住宅。住宅的最基本的功能性需求是“吃喝拉撒睡”,全世界人民“吃喝拉撒睡”的方式都是差不多的。住宅的非功能性需求主要是让人住得舒服。住宅的种类非常多,如茅草屋、窑洞、筒子楼、酒店、别墅等等。窑洞的体系结构与别墅的有天壤之别,你能说后者一定比前者好吗?不能,因为没有可比度。

如果一名建筑师受政府委托给广大的中国陕北农民设计住宅,他应当设计更好的窑洞而非别墅。理由很简单,因为广大的陕北农民住不起别墅,而那里的条件特别适合于建窑洞。窑洞对于当地的农民而言是非常实用而且成本低廉的住宅。

对于软件系统而言,能够满足需求的设计方案可能有很多种,究竟该选择哪一种呢?这时候商业目标是决策依据,即选择能够为开发方和客户方带来最大利益的那个方案。大部分开发人员天生有使用新技术的倾向,而这种倾向对开发商业产品而言可能是不利的,切记切记!

二.结构稳定性

体系结构是系统设计的第一要素,详细设计阶段的工作如用户界面设计,数据库设计,模块设计,数据结构设计等等,都是在体系结构确定之后开展的,而编程和测试是最后面的工作。如果体系结构经常变动,那么建筑在体系结构之上的用户界面、数据库、模块、数据结构等也跟着经常变动,用“树倒猢狲散”来比喻很恰当,这将导致项目发生混乱。

当前中国有几句流行的至理名言:“稳定压倒一切”、“发展才是硬道理”。发展的前提条件是稳定,社会如此,开发软件产品也是如此。

所以体系结构一旦设计完成,应当在一定的时间内保持稳定不变,只有这样才能使后续工作顺利开展。

前面讲了,体系结构是依据需求而设计的。如果需求变更了,很有可能导致体系结构发生变更,那么“保持结构稳定”岂不是成了空想?

高水平的设计师应当能够分析需求文档,判断出哪些需求是稳定不变的,哪些需求是可能变动的。于是根据那些稳定不变的需求设计体系结构,而根据那些可变的需求设计软件的“可扩展性”。

三.可扩展性

可扩展性是指软件扩展新功能的容易程度。可扩展越好,表示软件适应“变化”的能力越强。由于软件是“软”的,那是否所有的软件必须设计能扩展新功能呢?这要视软件的规模和复杂性而定。

如果软件规模很小,问题很简单,那么扩展功能的确比较容易。要是软件的代码只有100行,这时就无所谓“可扩展性”了,你想怎么扩展都可以。如果软件规模很大,问题很复杂,倘若软件的可扩展性不好,那么该软件就像用卡片造成的房子,抽出或者塞进去一张卡片都有可能使房子倒塌。

是否任何软件在设计的时候都要考虑可扩展性呢?不见得,如果确信某个软件在它淘汰之前永远都不会变更(如一次性产品),那么在设计阶段就没必要考虑可扩展性,这样省事省力

可扩展性越来越重要,社会的商业越来越发达,需求变化就越快。需求变化必将导致修改(或扩展)软件的功能,如果软件的扩展性比较差的话,那么修改(或扩展)功能的代价会很高。

现代软件产品通常采用“增量开发模式”开发商不断地推出软件产品的新版本,从而不断地获取增值利润。如果软件的可扩展性比较差的话,每次开发新版本的代价就会很高。虽然开发商抓住了商机,但却由于设计水平差而导致没有赚取多少利润,真是要活活气死。体系结构的稳定性是根据那些稳定不变的需求而设计的,体系结构的可扩展性则是依据那些可变的需求而设计的。从字面上看,稳定性和可扩展性似乎有点矛盾。两者之间存在辩证的关系:如果系统不可扩展的话,那么就没有发展前途,所以不能只关心稳定性而忽视可扩展性;而软件系统“可扩展”的前提条件是“保持结构稳定”,否则软件难以按计划开发出来,稳定性是使系统能够持续发展的基础。所以稳当性和扩展性都是体系结构设计的要素。

人们对物质有喜新厌旧的天性,你可以经常改变房子的装潢和摆设,但不能每次都去拆墙,挖地基。在软件开发过程中,变化是司空见惯的事情。如果每次变化都导致体系结构发生大的变化,那简直就是“伤筋动骨”,这样的体系结构无疑是败笔之作。

分层开发是一种重要的体系结构,有着良好的可扩展性,而且在扩充或修改功能时,基本不会破坏原有结构的稳定性。可以参看我的分层开发思想与小笼包 一文。

四.可复用性

复用就是指“重复利用已经存在的东西”。复用不是人类懒惰的表现,而是智慧的表现。因为人类总是在继承了前人的成果,不断加以利用、改进或创新后才会进步。

复用有利于提高产品的质量、提高生产效率和降低成本。由经验可知,通常在一个新系统中,大部分的内容是成熟的,只有小部分内容是创新的。一般地,可以相信成熟的东西总是比较可靠的(即具有高质量),而大量成熟的工作可以通过复用来快速实现(即具有高生产效率)。勤劳并且聪明的人们应该把大部分的时间用在小比例的创新工作上,而把小部分的时间用在大比例的成熟工作中,这样才能把工作做的又快又好。

企业成功地开发了某个软件产品之后,如果下个新产品能够复用上个产品的体系结构的话,那么新产品的系统设计的成本和风险将大大降低。

可复用性是设计出来的,而不是偶然碰到的。要使体系结构具有良好的可复用性,设计师应当分析应用域的共性问题,然后设计出一种通用的体系结构模式,这样的体系结构才可以被复用。

 

1.全面解耦原则:对业务进行抽象建模,业务数据与业务逻辑解耦,软硬件解耦,平台和产品解耦,系统各部件解耦。模块、组件高内聚,低耦合。
2.服务化/组件化原则:以服务、数据为中心,构建服务化、组件化架构,具备灵活,按需组合的能力。
3.接口隔离及服务自治原则:通过接口隐藏服务/组件实现细节,服务/组件只能通过接口进行交互,接口契约化,标准化,跨版本兼容;服务/组件可独立发展、独立发布、独立升级,服务自治,可视、可管、可控、可测、可维,故障自愈。
4.弹性伸缩原则:构建全分布云化架构,或借鉴云化架构思想,每个服务具备横向扩展能力,支持按需使用,自动弹性伸缩,可动态替换、灵活部署,支撑高性能、高吞吐量、高并发、高可用业务场景。
5.安全可靠环保原则:构建最小权限,纵深防御、最小公共化、权限分离、不轻信、开放设计、完全仲裁、失效安全、保护薄弱环节、安全机制、经济性、用户接受度以及加强隐私保护的安全体系,确保系统、网络和数据的机密性、完整性、可用性、可追溯性;以业务系统零故障为导向;按需构筑分层分级的可靠性,通过故障的预流、预防、快速故障恢复、避免故障发生;系统资源使用有效最大化,实现节能、节地、节材、环保。
6.用户体验和自动化运维原则:面向业务获取和使用场景,构建实时、按需、在线、自助、社区化、方便易用的用户体验;支持远程、自动、智能、安全、高效地完成网规/网设、安装、部署、调测、验收、扩缩容、软件升级、打补丁、日常维护、问题处理。
7.开放生态原则:面向生态场景,按需开放平台设施、中间件、数据、业务逻辑、UI等能力;构建开放生态、支持分层、远程、自动、自助、简单高效地完成定制、集成、第三方应用开发。
8.高效开发原则:创建支持迭代、增量、持续交付的架构,支持部件独立开发、自动化编译构建、测试、集成验证、并易于高效修改和持续优化;支持开发组织小型化,扁平化,支持小团队独立高效并行开发。
9.柔性供应制造原则:模块化设计,模块/物料归一化、标准化,支持自动化、数字化、智能化、随需应变的柔性制造。
10.持续演进原则:架构并非一蹴而就,需要有效地管理架构需求;持续构建和发展架构,适应业务需求变化,适时引入业界最佳实践,及时重构,确保架构生命力和竞争力。