设计模式七大原则
1. 设计模式的目的
编写软件过程中,程序员面临着来自耦合性,内聚性以及可维护性,可扩展性,重用性,灵活性等多方面的挑战,设计模式是为了让程序(软件),具有更好的:
- 可靠性:当我们增加新的功能后,对原来的功能没有影响
- 可读性:编程规范性, 便于其他程序员的阅读和理解
- 可扩展:当需要增加新的功能时,非常的方便,称为可维护
- 用性:相同功能的代码,不用多次编写
- 使程序呈现高内聚,低耦合的特性
2. 设计模式七大原则
2.1 单一职责原则(Single Responsibility Principle, SRP)
定义:
类的职责要单一,一个类只负责一项职责。
分析:
一个类(或者大到模块,小到方法)承担的职责越多,它被复用的可能性越小,而且如果一个类承担的职责过多,就相当于将这些职责耦合在一起,当其中一个职责变化时,可能会影响其他职责的运作。
作用:
降低代码复杂度、系统解耦合、提高可读性。
注意:
通常情况下,我们应当遵守单一职责原则,只有逻辑足够简单,才可以在代码级违反单职责原则;只有类中方法数量足够少,可以在方法级别保持单一职责原则。
2.2 接口隔离原则(Interface Segregation Principle, ISP)
定义:
客户端不应该依赖那些它不需要的接口。 一旦一个接口太大,则需要将它分割成一些更细小的接口,使用该接口的客户端仅需知道与之相关的方法即可。
分析:
- 接口隔离原则是指使用多个专门的接口,而不使用单一的总接口。每一个接口应该承担一种相对独立的角色,不多不少,不干不该干的事,该干的事都要干。
- 一个接口就只代表一个角色,每个角色都有它特定的一个接口,此时这个原则可以叫做“角色隔离原则”。
- 接口仅仅提供客户端需要的行为,即所需的方法,客户端不需要的行为则隐藏起来,应当为客户端提供尽可能小的单独的接口,而不要提供大的总接口。
- 使用接口隔离原则拆分接口时,首先必须满足单一职责原则,将一组相关的操作定义在一个接口中,且在满足高内聚的前提下,接口中的方法越少越好。
- 可以在进行系统设计时采用定制服务的方式,即为不同的客户端提供宽窄不同的接口,只提供用户需要的行为,而隐藏用户不需要的行为。
作用:
避免接口过于臃肿职责不单一。
2.3 依赖倒转原则(Dependency Inversion Principle, DIP)
定义:
高层模块不应该依赖低层模块,它们都应该依赖抽象。抽象不应该依赖于细节,细节应该依赖于抽象。
分析:
简单来说,依赖倒转原则就是指:代码要依赖于抽象的类,而不要依赖于具体的类;要针对接口或抽象类编程,而不是针对具体类编程。
通过面向接口编程,使用接口或者抽象类制定好规范和契约,而不去涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成。
作用:
避免需求变化导致过多的维护工作。
注意:
依赖注入,就是将一个类的对象传入另一个类,注入式应该尽量注入父类对象,而在程序运行时再通过子类对象来覆盖父类对象。
继承时遵循里氏替换原则。
2.4 里氏代换原则(Liskov Substitution Principle, LSP)
定义:
所有引用基类(父类)的地方必须能透明地使用其子类的对象。
分析:
- 子类可以实现父类的抽象方法,但是不能覆盖父类的非抽象方法;
- 子类中可以增加自己特有的方法;
- 当子类覆盖或实现父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松;
- 当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。
- 如果子类不能完整地实现父类的方法,或者父类的一些方法在子类中已经发生畸变,则建议断开继承关系,采用依赖,聚合,组合等关系代替继承。
- 里氏代换原则是实现开闭原则的重要方式之一,由于使用基类对象的地方都可以使用子类对象,因此在程序中尽量使用基类类型来对对象进行定义,而在运行时再确定其子类类型,用子类对象来替换父类对象。
作用:
避免系统继承体系被破坏。
2.5 开闭原则(Open-Closed Principle, OCP)
定义:
一个软件实体应当对扩展开放(对提供方),对修改关闭(对使用方)。也就是说在设计一个模块的时候,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。
分析:
设计模式前面6大原则以及23种设计模式遵循的好,开闭原则自然遵守的好。对需求的变更保持前瞻性和预见性,就可以使抽象具有更广泛适用性,设计出的软件架构就能相对稳定。软件需求中易变的细节,通过从抽象派生出实现类来扩展。
作用:
提高扩展性、便于维护。
2.6 迪米特法则(Law of Demeter, LoD)
定义:
- 又称为最少知识原则。
- 不要和“陌生人”说话。
- 只与你的直接朋友通信。
- 每一个软件单位对其他的单位都只有最少的知识,而且局限于那些与本单位密切相关的软件单位。
分析:
- 一个对象应该对其他对象保持最少的了解。
- 类与类关系越密切,耦合度越大。
- 迪米特法则(DemeterPrinciple)又叫最少知道原则,即一个类对自己依赖的类知道的越少越好。也就是说,对于被依赖的类不管多么复杂,都尽量将逻辑封装在类的内部。对外除了提供的 public 方法,不对外泄露任何信息。
- 迪米特法则还有个更简单的定义:只与直接的朋友通信。
- 直接的朋友:每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。
耦合的方式很多,依赖,关联,组合,聚合等。其中,我们称出现成员变量,方法参数,方法返回值中的类为直接的朋友,而出现在局部变量中的类不是直接的朋友。也就是说,陌生的类最好不要以局部变 量的形式出现在类的内部。
作用:
降低类与类之间的耦合。
注意:
迪米特法则的核心是降低类之间的耦合,但是,由于每个类都减少了不必要的依赖,因此迪米特法则只是要求降低类间(对象间)耦合关系, 并不是 要求完全没有依赖关系
2.7 合成复用原则(Composite Reuse Principle, CRP)
定义:
尽量使用对象组合,而不是继承来达到复用的目的。
分析:
- 合成复用原则就是指在一个新的对象里通过关联关系(包括组合关系和聚合关系)来使用一些已有的对象,使之成为新对象的一部分;
新对象通过委派调用已有对象的方法达到复用其已有功能的目的。简言之:要尽量使用组合/聚合关系,少用继承。 - 在面向对象设计中,可以通过两种基本方法在不同的环境中复用已有的设计和实现,即通过组合/聚合关系或通过继承。
- 组合/聚合可以使系统更加灵活,类与类之间的耦合度降低,一个类的变化对其他类造成的影响相对较少,因此一般首选使用组合/聚合来实现复用;其次才考虑继承,在使用继承时,需要严格遵循里氏代换原则,有效使用继承会有助于对问题的理解,降低复杂度,而滥用继承反而会增加系统构建和维护的难度以及系统的复杂度,因此需要慎重使用继承复用。
目的:
防止类的体系庞大。