设计模式五篇(一):面向对象的设计原则

在软件工程中,设计模式(design pattern)是对软件设计中普遍存在(反复出现)的各种问题所提出的解决方案。这个术语是由埃里希·伽玛(Erich Gamma)等人在1990年代从建筑设计领域引入到计算机科学的。

设计模式并不直接用来完成代码的编写,而是描述在各种不同情况下,要怎么解决问题的一种方案。面向对象设计模式通常以类或对象来描述其中的关系和相互作用,但不涉及用来完成应用程序的特定类或对象。设计模式能使不稳定依赖于相对稳定、具体依赖于相对抽象,避免会引起麻烦的紧耦合,以增强软件设计面对并适应变化的能力。

面向对象的设计原则

要学习设计模式,我们先要了解七个设计原则。事实上,设计模式正是对这七个原则的实现。它们分别是单一职责原则开闭原则里氏替换原则依赖倒置原则接口隔离原则最少知识原则合成/聚合复用原则

单一职责原则(Single Responsiblity Principle , SRP)

不要存在多于一个导致类变更的原因。通俗的说,即一个类只负责一项职责。

理解:当你有两个不同的功能要实现时,把它们分派给两个类而不是由一个类承担。这样可以避免对一个功能的修改无意中使另一个功能产生故障。

开闭原则(Open Closed Principle , OCP)

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

理解:在编码时,要考虑到未来可能的变更,做出合理、高质量的抽象。这样,今后需要对功能进行扩展时,就不需要修改原有的代码,而是直接从抽象中扩展出新的实现。从而避免了潜在的风险。

里氏替换原则(Liskov Substitution Principle , LSP)

代码中任何基类可以出现的地方,其派生类一定可以出现。

理解:当使用继承时,尽量不要重写/重载父类的方法。父类中凡是已经实现好的方法(相对于抽象方法而言),实际上是在设定一系列的规范和契约,虽然它不强制要求所有的子类必须遵从这些契约,但是如果子类对这些非抽象方法任意修改,就会对整个继承体系造成破坏。

依赖倒置原则(Dependence Inversion Principle , DIP)

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

理解:依赖倒置原则的核心思想是面向接口编程
类A直接依赖类B,假如要将类A改为依赖类C,则必须通过修改类A的代码来达成。这种场景下,类A一般是高层模块,负责复杂的业务逻辑;类B和类C是低层模块,负责基本的原子操作;假如修改类A,会给程序带来不必要的风险。
若类A修改为依赖接口I,类B和类C各自实现接口I,类A通过接口I间接与类B或者类C发生联系,则会大大降低修改类A的几率。

在实际编程中,我们一般需要做到如下3点:

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

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

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

接口隔离原则(Interface Segregation Principle , ISP)

一个类对另一个类的依赖应该建立在最小的接口上。

理解:把一个大接口拆分成几个隔离的小接口。

最少知识原则(Least Knowledge Principle , LKP)

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

理解:类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大。对于被依赖的类来说,无论逻辑多么复杂,都尽量地的将逻辑封装在类的内部,对外除了提供的public方法,不对外泄漏任何信息。
迪米特法则还有一个更简单的定义:只与直接的朋友通信。
首先来解释一下什么是直接的朋友:每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。耦合的方式很多,依赖、关联、组合、聚合等。其中,我们称出现成员变量、方法参数、方法返回值中的类为直接的朋友,而出现在局部变量中的类则不是直接的朋友。也就是说,陌生的类最好不要作为局部变量的形式出现在类的内部。

合成/聚合复用原则(Composite/Aggregate Reuse Principle , CARP)

尽量使用合成/聚合,少用继承。

理解:通过继承来进行复用的主要问题在于:

  • 继承复用会破坏系统的封装性,因为继承会将基类的实现细节暴露给子类

  • 如果基类发生改变,那么子类的实现也不得不发生改变

  • 从基类继承而来的实现是静态的,不可能在运行时发生改变,没有足够的灵活性

  • 继承只能在有限的环境中使用(如类没有声明为不能被继承)

只有当以下的条件全部被满足时,才应当使用继承关系。

  1. 子类是超类的一个特殊种类,而不是超类的一个角色,也就是区分“Has-A”和“Is-A”.只有“Is-A”关系才符合继承关系,“Has-A”关系应当使用聚合来描述。

  2. 永远不会出现需要将子类换成另外一个类的子类的情况。如果不能肯定将来是否会变成另外一个子类的话,就不要使用继承。

  3. 子类具有扩展超类的责任,而不是具有置换掉或注销掉超类的责任。如果一个子类需要大量的置换掉超类的行为,那么这个类就不应该是这个超类的子类。

你可能感兴趣的:(设计模式,软件工程)