来源:mikechen的互联网架构
无论是哪一种设计模式,都需要遵循设计原则。
如果说设计模式则是具体运用,那么设计原则就是理论指导。
因此,要想理解设计模式的精髓,就需要先深入 7 大设计原则。
本文,我们重点介绍设计模式的 7 大原则。
1.1 设计模式是什么
设计模式(Design Pattern)是软件开发经验的总结,是一套解决特定问题领域的通用、可重用的解决方案。
简单理解,就是通过复用成功的设计和体系结构,来解决反复出现的通用问题。
设计模式一共有 23 种,每一种模式都有明确的用途和适用场景,解决的问题都是不一样的。
1.2 设计模式的起源
1995 年,Gang of Four 联合出版了图书《设计模式:可复用面向对象软件的基础》。
在这本书中,第一次将设计模式提升到理论高度,并将之规范化,23 种经典的设计模式被首次提出。
1.3 设计模式的作用
设计模式通过复用成功的设计和体系结构,提高了代码的重用性、可读性、可靠性、可扩展性。
将通用功能封装在可重用的组件中,减少了代码的冗余;
提供了一种结构化的方式来组织代码,减少代码的复杂性;
有新的需求和变化时,可以轻松进行扩展,减少了对现有代码的破坏性更改。
在前面的系列文章中,我们介绍了 7 大设计原则。
它们分别是开闭原则、里氏替换原则、依赖倒置原则、单一职责原则、接口隔离原则、迪米特法则、合成复用原则。
7 大设计原则的简介(详解见下文):
设计模式的 7 大设计原则,以实现“高内聚、松耦合”的软件系统为目标。
下面,我们逐一详解。
单一职责原则,英文全称 Single Responsibility Principle,又称为单一功能原则。
单一职责原则是指一个类要职责单一,只负责一个特定的功能或任务。
单一职责原则让每个类都专注于一个明确的职责,降低了代码的复杂性,提高代码的可维护性。
单一职责原则的 UML 类图:
单一职责原则的难点是:
如何将类的职责划分得清晰明了,如何识别和消除不必要的职责......。
在实际开发中,我们很容易将不同的责任归类在一起,或者过度拆分。
是否需要拆分职责,参考思路:
当变化发生,只影响其中一个职责,那就需要拆分。如果变化都影响到这两个职责,那就不需要拆分。
完整内容,查看详解篇:一文吃透单一职责原则
开闭原则,英文全称 Open/Closed Principle。
开闭原则是指一个软件实体(类、函数等),应该 对扩展开放、对修改封闭 。
开闭原则是面向对象设计中“可复用设计”的基石。
开闭原则通过实现一个热插拔的效果,在不破坏现有功能、不修改原有代码的情况下,去扩展原有代码,让系统可维护、可扩展、可复用。
开闭原则的 UML 类图:
开闭原则的示例:
假设:
我们要实现一个绘制图表的功能,支持多种图表显示方式,例如柱状图、饼状图...等多种图表。
系统结构:
......
完整内容,查看详解篇: 3 分钟吃透开闭原则,架构设计筑基必知必会
里氏替换原则(LSP),又称为里氏代换原则,英文全称 Liskov Substitution Principle。
里氏替换原则中的“里氏”,取自其提出者麻省理工学院的 Liskov 女士。
里氏替换原则的定义:
所有引用基类(父类)的地方,必须能透明地使用其子类的对象,但不能修改父类已有的功能。
即,任何父类在的地方,都可以替换成子类,并且要保证原有程序的逻辑行为和正确性。
里氏替换原则提高了代码的重用性、兼容性、扩展性。
但是,里氏替换原则也有一些缺点。例如,继承是侵入性的,降低了代码的灵活性,增强了耦合性。
里氏替换原则的实例:
假设:
一个电商系统中,用户分为两类:VIP用户、普通用户。
现在,需要实现一个发送邮件的功能。
没有遵循里氏替换原则,是这样设计的:
......
完整内容,查看详解篇: 微信一面挂,痛失 50 W,只因里氏替换原则没说清楚
依赖倒置原则(DIP),英文全称 Dependency Inversion Principle。
依赖倒置原则是指要面向接口编程。
高级模块不应该依赖于低级模块,它们都应该依赖于抽象。抽象不应该依赖于具体实现,具体实现应该依赖于抽象。
依赖倒置的 UML 类图:
依赖倒置原则的两个核心思想:
1) 高层模块不应依赖于底层模块,高层模块、底层模块都应依赖于抽象。
2) 抽象不应该依赖于细节,细节应该依赖于抽象。
这意味着,
在软件设计中,应该使用抽象类、接口或抽象方法等抽象层,来定义模块之间的通信接口。
而具体实现,应该依赖于这些抽象。
一图释义:
依赖倒置原则的实现示例:
假设:
现在你需要实现一个面包店,你第一件想到的事情是什么?
我想到的是一个面包店,里面有很多具体的面包。
例如:法棍面包、全麦面包、白面包、牛角面包、荞麦面包、法式面包、甜甜圈面包、裸麦面包。
传统依赖关系:
面包店就是上层模块,面包是下层模块。如图:
代码示例:
......
完整内容,查看详解篇: 依赖倒置原则看这篇就够了
接口隔离原则(ISP),英文全称 Interface Segregation Principle。
接口隔离原则是指在设计接口时要精简单一,只包含客户端需要的方法,而不包含多余的方法。
简单理解:
客户端需要什么接口,就提供什么接口,不需要的就不依赖。
将大接口拆分成多个小接口,每个接口只专注服务于一个子模块或业务逻辑。
使用抽象类或默认方法,来减少接口的改动对原有代码的影响。
接口隔离原则的核心是接口细化,接口细化要合理,结合系统的实际需求,以适度设计为前提,既小而精确。
在软件设计最初,我们的想法是将相同功能的方法放在同一个接口里面。
理论上来说,这貌似没错,我们来看看如何设计。
例如:
类 A 通过接口 I 依赖类 B,类 C 通过接口 I 依赖类 D。
那么,对于类 A 和类 B 来说,接口 I 不是最小接口,则类 B 和类 D 必须去实现它们不需要的方法。
如图:
类 A 依赖接口 I 中的方法 1、方法 2、方法 3,类 B 是对类 A 依赖的实现。
类 C 依赖接口 I 中的方法 1、方法 4、方法 5,类 D 是对类 C 依赖的实现。
对于类 B 和类 D 来说,虽然都有用不到的方法(红色字体),但由于实现了接口 I,所以也必须要实现这些用不到的方法。
代码示例:
......
完整内容,查看详解篇: 精通接口隔离原则,轻松实现高内聚、低耦合架构
合成复用原则(CRP),又称为组合/聚合复用原则,英文全称 Composite Reuse Principle。
合成复用原则要求在实现代码复用时,尽量先使用组合或聚合等关联关系,其次考虑使用继承关系。
合成复用原则的核心思想:
将不同的类、模块或组件组合在一起,来创建新的类或对象。
尽量不通过继承已有的类来获得需要的功能。
合成复用原则的实现示例:
假设:
在一个汽车分类管理程序中,汽车有两种分类方式:
按动力源分类:汽油汽车、电动汽车等;
按颜色分类:白色汽车、黑色汽车和红色汽车等。
如果使用继承,就需要同时考虑这两种分类,将会产生6个组合。
这样的方式会带来两个问题:
导致子类过多。
任何一个分类发生变更,都要修改源代码,违背了开闭原则。
图例:
代码示例:
......
关于合成复用原则的概念、背景、作用、UML、示例、应用,以及组合和继承的选型思路的全面详解。
完整内容,查看详解篇: 合成复用原则全面解析(附图解及源码实例)
迪米特法则(LoD),又称为最少知识原则,英文全称 Least Knowledge Principle。
迪米特法则是指一个模块(类)只和与它高度相关的模块交流,尽量减少与其他模块(类)的直接交流。
大白话就是,模块之间的通信应该简单明了,每个模块只需要关心与它高度相关的模块,而不必了解整个系统的细节。
这有点像在现实生活中,你只与你的朋友直接交流,而不会与朋友的朋友(陌生人)交流。你的任何改变,都不会对朋友的朋友带来影响。
迪米特法则的由来
低耦合、高内聚是软件编程的总原则。
耦合:指模块之间的联系程度。
内聚:指模块内部元素的联系程度。
模块之间了解得越多,耦合就越紧。
任何一个模块的变化,都会影响到其他模块,系统就会变得很复杂。
只有尽可能降低各个模块之间的耦合,才能提高代码的复用率。
怎样才能实现低耦合呢?
这正是迪米特法则要去实现的。
迪米特法则的代码示例:
......
完整内容,查看详解篇: 2分钟通俗理解迪米特法则
本文主要详解设计模式的 7 大设计原则。
设计模式的提出,是为了解决一个常见的问题而总结出来的解决方案。设计模式一共有 23 种,不同的设计模式,解决的问题是不一样的,后续我将持续连载更新这个部分。