设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。
设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了重用代码、让代码更容易被他人理解、保证代码可靠性。
设计模式在项目开发的重要性已经不言而喻,下面就让我们一起来走进设计模式的主题中
首先先来认识一下为更好的理解设计模式打基础的 UML 建模
UML中类图以及类图之间的关系
统一建模语言(Unified Modeling Language,UML)是用来设计软件蓝图的可视化建模语言,1997 年被国际对象管理组织(OMG)采纳为面向对象的建模语言的国际标准。它的特点是简单、统一、图形化、能表达软件设计中的动态与静态信息。
UML 从目标系统的不同角度出发,定义了用例图、类图、对象图、状态图、活动图、时序图、协作图、构件图、部署图等 9 种图。
类、接口和类图
类
类(Class)是指具有相同属性、方法和关系的对象的抽象,它封装了数据和行为,是面向对象程序设计(OOP)的基础,具有封装性、继承性和多态性等三大特性。
类有类名、属性和方法,下面是一个表示类图的例子
上面的 no、name、school、age、sex 都表示的是属性,下面 display() 表示的是方法。
接口
接口(Interface)是一种特殊的类,它具有类的结构但不可被实例化,只可以被子类实现。接口一般用来设计类之间的关系
上面的手机接口有两个方法,可以表示call() 打电话,也可以表示receive() 接电话。
类图
类图(ClassDiagram)是用来显示系统中的类、接口、协作以及它们之间的静态结构和关系的一种静态模型。
类与类之间的关系
在软件系统中,类不是孤立存在的,类与类之间存在各种关系。根据类与类之间的耦合度从弱到强排列,UML 中的类图有以下几种关系:依赖关系、关联关系、聚合关系、组合关系、泛化关系和实现关系。其中泛化和实现的耦合度相等,它们是最强的。
- 依赖关系
依赖(Dependency
关系是一种使用关系,它是对象之间耦合度最弱的一种关联方式,是临时性的关联。在代码中,某个类的方法通过局部变量、方法的参数或者对静态方法的调用来访问另一个类(被依赖类)中的某些方法来完成一些事情。
在 UML 类图中,依赖关系使用带箭头的虚线来表示,箭头从使用类指向被依赖的类,例如下面这个例子
现代人需要依赖手机进行通讯和交流。在 UML 类图中,依赖关系使用带箭头的虚线来表示,箭头从使用类指向被依赖的类
- 关联关系
关联(Association)
关系是对象之间的一种引用关系,用于表示一类对象与另一类对象之间的联系,如老师和学生、师傅和徒弟、丈夫和妻子等。关联关系是类与类之间最常用的一种关系,分为一般关联关系、聚合关系和组合关系。
关联可以是双向的,也可以是单向的。在 UML 类图中,双向的关联可以用带两个箭头或者没有箭头的实线来表示,单向的关联用带一个箭头的实线来表示,箭头从使用类指向被关联的类。也可以在关联线的两端标注角色名,代表两种不同的角色。
例如一个老师和学生的关系是多对多的,一个老师可以教多个学生,一个学生有多个上课老师,那么一个学生同时也可以选择多门课程,学生和课程之间的关系也是多对多。
- 聚合关系
聚合(Aggregation)
关系是关联关系的一种,是强关联关系,是整体和部分之间的关系,是has-a
的关系。
聚合关系也是通过成员对象来实现的,其中成员对象是整体对象的一部分,但是成员对象可以脱离整体对象而独立存在。例如,学校与老师的关系,学校包含老师,但如果学校停办了,老师依然存在。
在 UML 类图中,聚合关系可以用带空心菱形的实线来表示,菱形指向整体
一个学校会有多个老师,但是学校没有了,老师还依然会很存在。
- 组合关系
组合(Composition)
关系也是关联关系的一种,也表示类之间的整体与部分的关系,但它是一种更强烈的聚合关系,是 contains-a
关系。
在组合关系中,整体对象可以控制部分对象的生命周期,一旦整体对象不存在,部分对象也将不存在,部分对象不能脱离整体对象而存在。
在 UML 类图中,组合关系用带实心菱形的实线来表示,菱形指向整体
钱包和钱是一个组合的关系,钱包是放钱的口袋,钱包丢了钱也就没了,所以是一种组合关系。
- 泛化关系
泛化(Generalization)关系是对象之间耦合度最大的一种关系,表示一般与特殊的关系,是父类与子类之间的关系,是一种继承关系,是 is-a
的关系。
在 UML 类图中,泛化关系用带空心三角箭头的实线来表示,箭头从子类指向父类。在代码实现时,使用面向对象的继承机制来实现泛化关系。例如,Student 类和 Teacher 类都是 Person 类的子类,
- 实现关系
实现(Realization)关系是接口与实现类之间的关系。在这种关系中,类实现了接口,类中的操作实现了接口中所声明的所有的抽象操作。
在 UML 类图中,实现关系使用带空心三角箭头的虚线来表示,箭头从实现类指向接口。
比如表示一个交通工具的接口,其中有一个 move 方法,有两个类 Bike 和 Car 分别实现了 Vehicle,那么也就实现了 move() 方法
设计模式的原则
在了解完上面一个简单的UML建模后,下面来聊一下设计原则,在设计模式中有六种设计原则,它们分别是
开闭原则(Open Closed Principle)
核心思想是对扩展开放,对修改关闭。也就是说,对已经使用的类的改动是通过增加代码进行的,而不是修改现有代码,实现一个热插拔的效果。
例如:你手机中的桌面主题,你无法修改已有的桌面主题,只能从网上下载新的桌面主题
单一职责原则(Single Responsiblity Principle)
单一职责原则的核心就是控制类的粒度大小、将对象解耦、提高其内聚性。其实就是开发人员经常说的”高内聚,低耦合”,每个类应该只有一个职责,对外只能提供一种功能,而引起类变化的原因应该只有一个。
比如一个班级会有很多课代表,语文课代表、数学课代表、英语课代表等等,那么课代表只应该负责班级特定学科的工作,语文课代表不能插手数学课代表和英语课代表的工作。
里式替换原则(Liskov Substitution Principle)
里式替换的原则认为子类可以扩展父类的功能,但不能改变父类原有的功能,也就是说:子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法。
例如鸟有一个fly() 方法,燕子是一种鸟,燕子继承鸟类并重写了 fly() 方法,企鹅是一种鸟,企鹅继承了鸟类但是企鹅不能飞,这就相当于是改变了鸟类的功能,不能说企鹅不能飞,所以鸟不能飞
依赖倒转原则(Dependency Inversion Principle)
依赖倒转原则的核心思想是:要依赖于抽象和接口,不要依赖于具体的实现。
其实就是说:在应用程序中,所有的类如果使用或依赖于其他的类,则应该依赖这些其他类的抽象类或者接口,而不是直接依赖这些其他类的具体类。为了实现这一原则,就要求我们在编程的时候针对抽象类或者接口编程,而不是针对具体实现编程。
比如女人去商场购物,她可能买很多东西,不局限于只买一个包,她还可能买鞋,化妆品等。那么我们可以针对商品建模
接口分离原则(Interface Segregation Principle)
要求程序员尽量将臃肿庞大的接口拆分成更小的和更具体的接口,让接口中只包含客户感兴趣的方法。
接口隔离原则和单一职责都是为了提高类的内聚性、降低它们之间的耦合性,体现了封装的思想,但两者是不同的:
- 单一职责原则注重的是职责,而接口隔离原则注重的是对接口依赖的隔离。
- 单一职责原则主要是约束类,它针对的是程序中的实现和细节;接口隔离原则主要约束接口,主要针对抽象和程序整体框架的构建。
迪米特原则(Principle of Least Knowledge)
迪米特法则又叫最少认知原则,它的核心思想是一个对象应当对其他对象尽可能少的了解
其实就是说:降低各个对象之间的耦合,提高系统的可维护性。在模块之间应该只通过接口编程,而不理会模块的内部工作原理,它可以使各个模块耦合度降到最低,促进软件的复用。
拿我们身边的朋友圈举例子,朋友圈的确定,如果两个人有一个共同的好友,那么他们的朋友圈点赞和留言也是可见的,如果没有共同好友,那么彼此不可见。
二十三种设计模式
设计模式概述
- 设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。
- 设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。
- 设计模式是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。
- 设计模式不是一种方法和技术,而是一种思想。
- 设计模式和具体的语言无关,学习设计模式就是要建立面向对象的思想,尽可能的面向接口编程,低耦合,高内聚,使设计的程序可复用。
学习设计模式能够促进对面向对象思想的理解,反之亦然。它们相辅相成。
设计模式的类型
总体来说,设计模式分为三类23种:
创建型(5种) :工厂模式、抽象工厂模式、单例模式、原型模式、构建者模式
- 结构型(7种):适配器模式、装饰模式、代理模式、外观模式、桥接模式、组合模式、享元模式
- 行为型(11种):模板方法模式、策略模式、观察者模式、中介者模式、状态模式、责任链模式、命令模式、迭代器模式、访问者模式、解释器模式、备忘录模式