设计模式之七大原则

Posted lwx-apollo

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式之七大原则相关的知识,希望对你有一定的参考价值。

简介

俗话说,无规矩不成方圆。那设计这种一听就很有格局的工作的规矩又是什么呢?我们一起来学习一下它的七大原则:

  1. 开闭原则 (Open-Closed Principle)
  2. 里氏替换原则 (Liskov Substitution Principle)
  3. 依赖倒转原则 (Dependence Inversion Principle)
  4. 单一职责原则 (Single Responsibility Principle)
  5. 接口隔离原则 (Interface Segregation Principle)
  6. 迪米特法则(Law Of Demeter)
  7. 组合/聚合复用原则 (Composite/Aggregate Reuse Principle)

开闭原则 OCP

定义

Software entities should be open for extension,but closed for modification .
软件实体应当对扩展开放,对修改关闭

以上的软件实体包括但不局限于:已有的项目模块,类、接口、方法函数。

优点

  • 遵守开闭原则的软件,能够扩展已存在的系统,提供新的功能满足新的需求,因此该软件有着很强的适应性和灵活性。
  • 遵守开闭原则的软件,已存在的功能与模块,不可被修改,系统稳定性高和持久性强,易于维护。

实现

       通过 “抽象共性,封装变化” 核心理念实现开闭原则,即是通过接口或者抽象类为软件实体抽象一个具有共性的上层,而将可变因素封装在具体实现类中。

里氏替换原则 LSP

定义

Inheritance should ensure that any property proved about supertype objects also holds for subtype objects
继承必须确保超类所拥有的特性在子类中仍然存在

里氏替换原则主要阐述了有关继承的一些原则,也就是什么时候应该使用继承,什么时候不应该使用继承,以及其中蕴含的原理。里氏替换原是继承复用的基础,它反映了基类与子类之间的关系,是对开闭原则的补充,是对实现抽象化的具体步骤的规范。

优点

  • 里氏替换原则是实现开闭原则的重要方式之一。
  • 它克服了继承中重写父类造成的可复用性变差的缺点。
  • 它是动作正确性的保证。即类的扩展不会给已有的系统引入新的错误,降低了代码出错的可能性。
  • 加强程序的健壮性,变更时可以做到非常好的兼容性,提高程序的维护性、可扩展性,降低需求变更时引入的风险。

实现

       通过“子类可以扩展父类的功能,但不能改变父类原有的功能”进行实现。可总结区分为一下几种情形:

  • 子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法
  • 子类中可以增加自己特有的方法
  • 当子类的方法重载父类的方法时,方法的前置条件(即方法的输入参数)要比父类的方法更宽松
  • 当子类的方法实现父类的方法时(重写/重载或实现抽象方法),方法的后置条件(即方法的的输出/返回值)要比父类的方法更严格或相等
    重写父类的方法来完成新的功能写起来虽然简单,但是整个继承体系的可复用性会比较差,特别是运用多态比较频繁时,程序运行出错的概率会非常大。

依赖倒置原则 DIP

定义

High level modules shouldnot depend upon low level modules.Both should depend upon abstractions.Abstractions should not depend upon details. Details should depend upon abstractions
高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象

要面向接口编程,不要面向实现编程

优点

  • 依赖倒置原则可以降低类间的耦合性。
  • 依赖倒置原则可以提高系统的稳定性。
  • 依赖倒置原则可以减少并行开发引起的风险。
  • 依赖倒置原则可以提高代码的可读性和可维护性。

实现

       依赖倒置原则的目的是通过要面向接口的编程来降低类间的耦合性,所以我们在实际编程中只要遵循以下4点,就能在项目中满足这个规则。

  • 每个类尽量提供接口或抽象类,或者两者都具备。
  • 变量的声明类型尽量是接口或者是抽象类。
  • 任何类都不应该从具体类派生。
  • 使用继承时尽量遵循里氏替换原则。

单一职责原则 SRP

定义

There should never be more than one reason for a class to change
一个类应该有且仅有一个引起它变化的原因

一个类有且仅有一项职责

优点

  • 降低类的复杂度。一个类只负责一项职责,其逻辑肯定要比负责多项职责简单得多。
  • 提高类的可读性。复杂性降低,自然其可读性会提高。
  • 提高系统的可维护性。可读性提高,那自然更容易维护了。
  • 变更引起的风险降低。变更是必然的,如果单一职责原则遵守得好,当修改一个功能时,可以显著降低对其他功能的影响。

实现

       单一职责原则是最简单但又最难运用的原则,需要设计人员发现类的不同职责并将其分离,再封装到不同的类或模块中。

单一职责同样也适用于方法函数

接口隔离原则 ISP

定义

The dependency of one class to another one should depend on the smallest possible interface
一个类对另一个类的依赖应该建立在最小的接口上

Clients should not be forced to depend on methods they do not use
客户端不应该被迫依赖于它不使用的方法

要为各个类建立它们需要的专用接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用

优点

  • 将臃肿庞大的接口分解为多个粒度小的接口,可以预防外来变更的扩散,提高系统的灵活性和可维护性。
  • 接口隔离提高了系统的内聚性,减少了对外交互,降低了系统的耦合性。
  • 如果接口的粒度大小定义合理,能够保证系统的稳定性;但是,如果定义过小,则会造成接口数量过多,使设计复杂化;如果定义太大,灵活性降低,无法提供定制服务,给整体项目带来无法预料的风险。
  • 使用多个专门的接口还能够体现对象的层次,因为可以通过接口的继承,实现对总接口的定义。
  • 能减少项目工程中的代码冗余。过大的大接口里面通常放置许多不用的方法,当实现这个接口的时候,被迫设计冗余的代码。

实现

       接口隔离原则的目的是通过约束接口,对接口的依赖的隔离来降低类间的耦合性,所以我们在实际编程中只要遵循以下4点,就能在项目中满足这个规则。

  • 接口尽量小,但是要有限度。一个接口只服务于一个子模块或业务逻辑。
  • 为依赖接口的类定制服务。只提供调用者需要的方法,屏蔽不需要的方法。
  • 了解环境,拒绝盲从。每个项目或产品都有选定的环境因素,环境不同,接口拆分的标准就不同深入了解业务逻辑。
  • 提高内聚,减少对外交互。使接口用最少的方法去完成最多的事情。

迪米特法则 LOD

定义

Talk only to your immediate friends and not to strangers
只与你的直接朋友交谈,不跟“陌生人”说话

       如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。其目的是降低类之间的耦合度,提高模块的相对独立性。

迪米特法则中的“朋友”是指:当前对象本身、当前对象的成员对象、当前对象所创建的对象、当前对象的方法参数等

优点

  • 降低了类之间的耦合度,提高了模块的相对独立性。
  • 由于亲合度降低,从而提高了类的可复用率和系统的扩展性。

缺点

会使系统产生大量的中介类,从而增加系统的复杂性,使模块之间的通信效率降低

实现

从迪米特法则的定义和特点可知,它强调以下两点:

  1. 从依赖者的角度来说,只依赖应该依赖的对象。
  2. 从被依赖者的角度说,只暴露应该暴露的方法。

所以,在运用迪米特法则时要注意以下 6 点。

  • 在类的划分上,应该创建弱耦合的类。类与类之间的耦合越弱,就越有利于实现可复用的目标。
  • 在类的结构设计上,尽量降低类成员的访问权限。
  • 在类的设计上,优先考虑将一个类设置成不变类。
  • 在对其他类的引用上,将引用其他对象的次数降到最低。
  • 不暴露类的属性成员,而应该提供相应的访问器(set 和 get 方法)。
  • 谨慎使用序列化(Serializable)功能。

合成复用原则 CRP

定义

它要求在软件复用时,要尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。

如果要使用继承关系,则必须严格遵循里氏替换原则。合成复用原则同里氏替换原则相辅相成的,两者都是开闭原则的具体实现规范。

优点

  • 它维持了类的封装性。因为成分对象的内部细节是新对象看不见的,所以这种复用又称为“黑箱”复用。
  • 新旧类之间的耦合度低。这种复用所需的依赖较少,新对象存取成分对象的唯一方法是通过成分对象的接口。
  • 复用的灵活性高。这种复用可以在运行时动态进行,新对象可以动态地引用与成分对象类型相同的对象。

实现

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

总结

       这 7 种设计原则是软件设计模式必须尽量遵循的原则,是设计模式的基础。在实际开发过程中,并不是一定要求所有代码都遵循设计原则,而是要综合考虑人力、时间、成本、质量,不刻意追求完美,要在适当的场景遵循设计原则。这体现的是一种平衡取舍,可以帮助我们设计出更加优雅的代码结构。

原则总结目的
开闭原则对扩展开放,对修改关闭降低维护带来的新风险
依赖倒置原则高层不应该依赖低层,要面向接口编程更利于代码结构的升级扩展
单一职责原则一个类只干一件事,实现类要单一便于理解,提高代码的可读性
接口隔离原则一个接口只干一件事,接口要精简单一功能解耦,高聚合、低耦合
迪米特法则不该知道的不要知道,一个类应该保持对其它对象最少的了解,降低耦合度只和朋友交流,不和陌生人说话,减少代码臃肿
里氏替换原则不要破坏继承体系,子类重写方法功能发生改变,不应该影响父类方法的含义防止继承泛滥
合成复用原则尽量使用组合或者聚合关系实现代码复用,少使用继承降低代码耦合

以上是关于设计模式之七大原则的主要内容,如果未能解决你的问题,请参考以下文章

设计模式—七大原则

设计模式 设计模式的七大原则

设计模式七大原则

GOF 的23种JAVA常用设计模式总结 03 面向对象七大设计原则

设计模式:七大设计原则

Java设计模式--设计模式七大原则