程序设计原则

遵循正确的设计原则,能在一定程度上保证程序、软件架构的整体质量,从而具备高可靠性、高稳定性。

而经过开发的漫漫长路,广集大智慧,程序猿们已经总结出了程序乃至架构开发的一些设计原则,并得到了广泛的认可。

只有站在巨人的肩膀上,才能走能更远。掌握这些原则,则是一个优秀的程序员的必经之路。

SOLID 原则(5个原则首字母的组合)

其中 solid 表示 可靠、结实的意思,若想设计出稳定的、整洁的架构,则要尽量遵循这五大原则。

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

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

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

   解释:顾名思义,一个类或一个方法应该只负责一个功能,也就是一个Class/Interface/Method 只负责一项职责。这样设计,可以降低类的复杂度,提高类的可读性, 提高系统的可维护性, 降低变更引起的风险 。

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

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

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

  解释:该原则又简称开闭原则,是面向对象设计中最基础的设计原则,指一个软件实体(类、模块和函数)对功能业务模块的扩展是开放的,对已经定好的功能业务模块的修改是关闭的。一般来说,我们应该将那些容易变化的部分进行抽象,利用对抽象的多个实现来进行对扩展开放,而不是直接在类中去修改。而所谓的开闭,强调的就是用抽象来构建框架,用实现来扩展细节,从而可以提高软件系统的可复用性及可维护性。     

  示例:要对功能的扩展提供可扩展的空间,如spring的AOP的实现就很方便,对于处理可以一直扩展;又比如责任链模式,对于某一职责提供可预留的空间给将来的功能点,spring mvc中的interceptor、web中的filter、netty中的channel等都是很好的设计。而对于已写好的代码要做好闭,如用private修饰,阻止外界调用,用final修饰不让其重写,比如单例模式时,常用private修饰构造函数,避免其他类又生成新的对象。

  另外,有的文章描述开闭原则,没有强调将变化的部分抽象出来,认为修改是不可避免的。而对于不可避免的修改,我们需要把控好修改的影响,确保高层组件的修改不会影响底层组件,而且组件层次越低越稳定。比如我们修改controller,那么service其实是无感知的、不会受影响;但如果我们修改service,dao是不会受影响的,但是controller是会受影响的;所以越底层的组件应该越稳定。对此,我们可以通过将系统划分为一系列组件,并且将这些组件间的依赖关系按层次结构进行组织,使得高阶组件不会因低阶组件被修改而受到影响,从而控制修改范围的影响。

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.

  If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T.

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

  如果对每一个类型为 T1 的对象 o1,都有类型为 T2 的对象 o2,使得以 T1 定义的所有程序 P 在所有的对象 o1 都替换成o2 时,程序 P 的行为没有发生变化,那么类型 T2 是类型 T1 的子类型。

  解释:可以理解为一个软件实体如果适用一个父类的话,那一定是适用于其子类,所有引用父类的地方必须能透明地使用其子类的对象。子类对象替换父类对象后,程序的功能逻辑不受任何影响。即 子类可以扩展父类的功能,但不能改变父类原有的功能。大多数人认为LSP其实就是指导如何使用继承关系的一种方法,尤其是我们在开发的过程中用spring依赖注入的基本都是基类而非具体的实现类,这个的确也是LSP的一种实现手段。LSP也在逐渐演变成一种更广泛的,指导接口与其实现方式的设计原则。

3.1)子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。
3.2)子类中可以增加自己特有的方法。
3.3)当子类的方法重载父类的方法时,方法的前置条件(即方法的输入/入参)要比父类方法的输入参数更宽松。
3.4)当子类的方法实现父类的方法时(重写/重载/实现 抽象方法),方法的后置条件(即方法的输出/返回值)要比父类更严格或相等。

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

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

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

  解释:指用多个专门的接口,而不使用单一的总接口,不要依赖不需要的东西。类间的依赖关系应该建立在最小的接口之上;建立单一接口,不要建立庞大臃肿的接口;尽量细化接口,接口中的方法尽量少(不是越少越好,一定要适度)。接口隔离原则符合我们常说的高内聚低耦合的设计思想,从而使得类具有很好的可读性、可扩展性和可维护性。

5. 依赖倒置原则(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.

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

   解释:即所谓的 面对接口编程。依赖于抽象类而不依赖于具体类。不与具体类交互,而与具体类的上层接口交互,其中具体类依赖抽象类。通过依赖倒置,可以减少类与类之间的耦合性,提高系统的稳定性,提高代码的可读性和可维护性,并能够降低修改程序所造成的风险。

   示例:定义一个UserService接口,其实现类为UserServiceImpl,然后会这样子调用 UserService userService = new UserviceImpl() 。或通过Spring的IOC注入进去对象。这种设计在spring的IOC框架中得到了充分体现,系统的许多集合类也是这样的,如Set set = new TreeSet();List list = new ArrayList() 等等。

所谓 六大原则 = SOLID 原则 + 迪米特原则

6. 迪米特原则(Law of Demeter LoD)

原文:Only talk to you immediate friends.

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

  解释:该原则也称 最少知识原则(Least Knowledge Principle - LKP),指一个对象应该对其他对象保持最少的了解,尽量降低类与类之间的耦合。即一个组件或者是对象不应该知道其他组件或者对象的内部实现细节。

  示例:比如SpringBoot就充分体现了这种原则,只暴露出几个类和方法,对于其实现的细节进行了很好的封装,这点又体现了开闭原则中的闭的特点。设计模式中的门面模式(Facade Pattern)也很好的体现了这一原则。

所谓 七大原则 = 六大原则 + 合成复用原则

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

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

其他原则 

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

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

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

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

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

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

4. 好莱坞原则(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)

  多做一些默认的约定或自动设置,从而减少手动配置的操作,尽量做到“零配置”,这样才能提高开发效率。很多开发框架都是这样做的,springboot就是一个典型的例子。

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

  在定义接口时,要做到哪些是命令,哪些是查询,要将它们分离,而不要揉到一起。从业务上分离 修改 (Command:增、删、改,会对系统状态进行修改) 和 查询(Query,查,不会对系统状态进行修改) 的行为。从而使得逻辑更加清晰,便于对不同部分进行针对性的优化。(参考:命令查询职责分离(CQRS)模式 

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

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

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

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

参考;

  软件架构设计的六大原则

  架构设计(2)-架构设计原则

 

共同学习,共同进步,若有补充,欢迎指出,谢谢!

原文地址:https://www.cnblogs.com/dengguangxue/p/11541076.html