转载

常见的架构设计原则总结

在上一篇文章《学了那么多技术,为何依然成不了架构师》中分享了为什么要学习架构设计原则,本篇文章作为该文章的一个延伸,总结了一些常用的架构设计原则,希望对你有用。

通用原则

KISS原则(Keep it simple and stupid)

最小代价(努力)原则

尽可能在一开始的时候,作出正确的选择,因为一旦这个架构设计出现,后面的人很有可能不愿意为了更好的架构而改进,而是遵循已有的设计。

最小意外原则

好的设计应该避免意外,遵守通用的规范和习惯,代码也是。这些意外其实是软件架构和设计中的复杂因素。最小意外也就意味着尽可能的简单。

DRY原则(Dont repeat yourself)

重复是软件的原罪之一,“Dont repeat yourself” 告诉我们应该尽可能的消灭重复和冗余。重复使得软件的阅读,修改,测试变得复杂,消灭重复,是使软件变得简单的手段之一。

面向对象的设计原则

一、单一职责原则(SRP)

概念

一个类只负责一项职责

问题由来

类T负责两个不同的职责:职责P1,职责P2。当由于职责P1需求发生改变而需要修改类T时,有可能会导致原本运行正常的职责P2功能发生故障。

解决方案

遵循单一职责原则。分别建立两个类T1、T2,使T1完成职责P1功能,T2完成职责P2功能。这样,当修改类T1时,不会使职责P2发生故障风险;同理,当修改T2时,也不会使职责P1发生故障风险。

职责扩散

  • 所谓职责扩散,就是因为某种原因,职责P被分化为粒度更细的职责P1和P2

  • 职责扩散会引起对单一职责的冲突

类级别和方法级别的单一职责

  • 类级别

    比较严格,整个类应该只负责一个职责

  • 方法级别

    当修改类代价比较大时,可以采用方法级别的单一职责。即一个类中不同方法负责不同职责

优点

  • 可以降低类的复杂度,一个类只负责一项职责,其逻辑肯定要比负责多项职责简单的多;

  • 提高类的可读性,提高系统的可维护性;

  • 变更引起的风险降低,变更是必然的,如果单一职责原则遵守的好,当修改一个功能时,可以显著降低对其他功能的影响。

二、开闭原则(OCP)

概念

  • 一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。

  • 开闭原则是面向对象设计中最基础的设计原则,它指导我们如何建立稳定灵活的系统。

问题由来

在软件的生命周期内,因为变化、升级和维护等原因需要对软件原有代码进行修改时,可能会给旧代码中引入错误,也可能会使我们不得不对整个功能进行重构,并且需要原有代码经过重新测试。

解决方案

当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。

如何做到开闭原则

用抽象构建框架,用实现扩展细节。

三、里氏替换原则(LSP)

概念

  • 所有引用基类的地方必须能透明地使用其子类的对象

  • 通俗讲,子类可以扩展父类的功能,但不能改变父类原有的功能

问题由来一功能P1,由类A完成。现需要将功能P1进行扩展,扩展后的功能为P,其中P由原有功能P1与新功能P2组成。新功能P由类A的子类B来完成,则子类B在完成新功能P2的同时,有可能会导致原有功能P1发生故障。

在实际编程中,我们常常会通过重写父类的方法来完成新的功能,这样写起来虽然简单,但是整个继承体系的可复用性会比较差,特别是运用多态比较频繁时,程序运行出错的几率非常大。如果非要重写父类的方法,比较通用的做法是:原来的父类和子类都继承一个更通俗的基类,原有的继承关系去掉,采用依赖、聚合,组合等关系代替。

总结

  • 子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。

  • 子类中可以增加自己特有的方法。

  • 当子类的方法重载父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。

  • 当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。

三、接口隔离原则(ISP)

概念

客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上

问题由来

常见的架构设计原则总结

类A通过接口I依赖类B,类C通过接口I依赖类D,如果接口I对于类A和类B来说不是最小接口,则类B和类D必须去实现他们不需要的方法

解决方案

将臃肿的接口I拆分为独立的几个接口,类A和类C分别与他们需要的接口建立依赖关系。也就是采用接口隔离原则

总结

  • 接口尽量小,但是要有限度。对接口进行细化可以提高程序设计灵活性是不争的事实,但是如果过小,则会造成接口数量过多,使设计复杂化。所以一定要适度。

  • 为依赖接口的类定制服务,只暴露给调用的类它需要的方法,它不需要的方法则隐藏起来。只有专注地为一个模块提供定制服务,才能建立最小的依赖关系。

  • 提高内聚,减少对外交互。使接口用最少的方法去完成最多的事情。

四、依赖倒置原则(DIP)

概念

高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。

问题由来

类A直接依赖类B,假如要将类A改为依赖类C,则必须通过修改类A的代码来达成。这种场景下,类A一般是高层模块,负责复杂的业务逻辑;类B和类C是 低层 模块,负责基本的原子操作;假如修改类A,会给程序带来不必要的风险。

解决方案

  • 将类A修改为依赖接口I,类B和类C各自实现接口I,类A通过接口I间接与类B或者类C发生联系,则会大大降低修改类A的几率。

  • 依赖倒置原则基于这样一个事实:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建起来的架构比以细节为基础搭建起来的架构要稳定的多。在java中,抽象指的是接口或者抽象类,细节就是具体的实现类,使用接口或者抽象类的目的是制定好规范和契约,而不去涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成。

依赖倒置原则的核心思想是面向接口编程

总结

  • 低层

    模块尽量都要有抽象类或接口,或者两者都有

  • 变量的声明类型尽量是抽象类或接口

  • 使用继承时遵循里氏替换原则

控制反转(IOC)

依赖注入(DI)

五、迪米特法则(LOD)

概念

  • 一个对象应该对其他对象保持最少的了解。

  • 迪米特法则又叫最少知道原则,最早是在1987年由美国Northeastern University的Ian Holland提出。通俗的来讲,就是一个类对自己依赖的类知道的越少越好。也就是说,对于被依赖的类来说,无论逻辑多么复杂,都尽量地的将逻辑封装在类的内部,对外除了提供的public方法,不对外泄漏任何信息。

问题由来

类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大。

解决方案

尽量降低类与类之间的耦合。

使用适度

迪米特法则的初衷是降低类之间的耦合,由于每个类都减少了不必要的依赖,因此的确可以降低耦合关系。但是凡事都有度,虽然可以避免与非直接的类通信,但是要通信,必然会通过一个"中介"来发生联系,例如本例中,总公司就是通过分公司这个"中介"来与分公司的员工发生联系的。过分的使用迪米特原则,会产生大量这样的中介和传递类,导致系统复杂度变大。所以在采用迪米特法则时要反复权衡,既做到结构清晰,又要高内聚低耦合。

六、合成复用原则(CRP)

概念

指尽量使用对象组合(has-a)/聚合(contanis-a),而不是继承关系达到软件复用的目的。

问题由来

  • 继承复用破坏了类的封装性。因为继承会将父类的实现细节暴露给子类,父类对子类是透明的,所以这种复用又称为“白箱”复用。

  • 子类与父类的耦合度高。父类的实现的任何改变都会导致子类的实现发生变化,这不利于类的扩展与维护。

  • 它限制了复用的灵活性。从父类继承而来的实现是静态的,在编译时已经定义,所以在运行时不可能发生变化。

解决方案

合成复用原则是通过将已有的对象纳入新对象中,作为新对象的成员对象来实现的,新对象可以调用已有对象的功能,从而达到复用。

常见的架构设计原则总结 继承方式转换成组合方式

优点

  • 它维持了类的封装性。因为成分对象的内部细节是新对象看不见的,所以这种复用又称为“黑箱”复用。

  • 新旧类之间的耦合度低。这种复用所需的依赖较少,新对象存取成分对象的唯一方法是通过成分对象的接口。

  • 复用的灵活性高。这种复用可以在运行时动态进行,新对象可以动态地引用与成分对象类型相同的对象。

总结

  • 单一职责原则告诉我们实现类要职责单一

  • 里氏替换原则告诉我们不要破坏继承体系

  • 依赖倒置原则告诉我们要面向接口编程

  • 接口隔离原则告诉我们在设计接口的时候要精简单一

  • 迪米特法则告诉我们要降低耦合

  • 合成复用原则告诉我们少用继承

  • 开闭原则是总纲,他告诉我们要对扩展开放,对修改关闭

数据库设计原则

ACID

  • 原子性(Atomicity)

原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。

  • 一致性(Consistency)

事务前后数据的完整性必须保持一致。

  • 隔离性(Isolation)

事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。

  • 持久性(Durability)

持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响

范式

  • 第一范式:1NF是对属性的原子性约束,要求属性具有原子性,不可再分解;

  • 第二范式:2NF是对记录的惟一性约束,要求记录有惟一标识,即实体的惟一性;

  • 第三范式:3NF是对字段冗余性的约束,即任何字段不能由其他字段派生出来,它要求字段没有冗余。

没有冗余的数据库设计可以做到。但是,没有冗余的数据库未必是最好的数据库,有时为了提高运行效率,就必须降低范式标准,适当保留冗余数据。

分布式设计原则

CAP理论

根据CAP理论,一致性(C),可用性(A),分区容错性(P),三者不可兼得,必须有所取舍。因此系统架构师不要把精力浪费在如何设计才能同时满足CAP三者的完美分布式系统,而是应该研究如何进行取舍,满足实际的业务需求。

Eventual Consistency(最终一致性)

过程松,结果紧,最终结果必须保持一致性。

BASE理论

  • 基本可用(Basically Available)

  • 软状态(Soft State)

  • 最终一致性(Eventually Consistent)

BASE模型反ACID模型,完全不同ACID模型,牺牲高一致性,获得可用性或可靠性。BASE是对CAP中一致性和可用性权衡的结果,是基于CAP定律逐步演化而来。

摩尔定律

当价格不变时,集成电路上可容纳的元器件的数目,约每隔18-24个月便会增加一倍,性能也将提升一倍。换言之,每一美元所能买到的电脑性能,将每隔18-24个月翻一倍以上。再简单点就是硬件越来越便宜!

作为一名系统架构师,应该充分考虑摩尔定律的影响,使用更经济、合理和简单的方法来实现系统性能目标。

微服务设计原则

高内聚低耦合

  • 单一职责

  • 轻量级通信方式

  • 服务之间的契约

高度自治

  • 能独立开发,部署,发布

  • 进程隔离

  • 独立的代码库,流水线

以业务为中心

  • 每个服务代表了特定的业务逻辑

  • 能更快的响应业务的变化

  • 围绕业务组织团队

弹性设计

  • 容错

  • 服务降级

日志与监控

  • 日志聚合

  • 监控与警告

自动化

  • 持续集成

  • 持续交付

常见的架构设计原则总结

常见的架构设计原则总结

私信“设计原则”,将该脑图文件分享给你!

常见的架构设计原则总结

《程序员的思维修炼》是菜根老谭结合自己的工作经历和对这个岗位发展的认知而专门开辟的一个专栏,旨在分析程序员的职业发展的过程中我们应该锻炼哪些思维意识,如何去合理正确的看待事物,如何形成适合自己发展的思维体系。本专栏既是对过去思考的总结,也是对自己发展所具备的能力的思考,希望这些内容能陪我成长,帮大家解疑答惑。 该专栏首发今日头条,关注我的同名头条号获取更及时的内容推荐。

同时,微信为菜根老谭开通了视频号,配合着一些短视频,讲讲产品研发的那点事,分析产品研发过程中出现的问题以及思考。

常见的架构设计原则总结

菜根老谭,微信公众号:CGLT_TAN,人人都是产品经理专栏作家。经历程序员、技术Leader、研发Leader等多种岗位。关注医疗,早教领域,擅长企业IT架构及互联网产品架构。

点击“阅读原文”,获取《程序员的思维修炼》专栏文章


很遗憾的说,推酷将在这个月底关闭。人生海海,几度秋凉,感谢那些有你的时光。

原文  https://mp.weixin.qq.com/s/B1t29lHUaD4N0mDCvZjIPg
正文到此结束
Loading...