1、概述
约 4826 字大约 16 分钟
2025-06-22
[!NOTE] Info 设计模式本人是跟随爱编程的大丙学习,且该博主博客中的笔记也写的极为详细。而又因为本人比较懒的原因大部分内容都是从该博主博客中复制而来。版权所属:爱编程的大丙。以下是丙哥的博客:爱编程的大丙 - 知识分享
介绍
软件设计模式(Software DesignPattern),又称设计模式,是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。它描述了在软件设计过程中的一些不断重复发生的问题,以及该问题的解决方案。也就是说,它是解决特定问题的一系列套路,是前辈们的代码设计经验的总结,具有一定的普遍性,可以反复使用。
作用
设计模式的本质是面向对象设计原则的实际运用,是对类的封装性、继承性和多态性以及类的关联关系和组合关系的充分理解。
正确使用设计模式具有以下优点。
- 可以提高程序员的思维能力、编程能力和设计能力。
- 使程序设计更加标准化、代码编制更加工程化,使软件开发效率大大提高,从而缩短软件的开发周期。
- 使设计的代码可重用性高、可读性强、可靠性高、灵活性好、可维护性强。
分类
- 创建型模式 用于描述"怎样创建对象",它的主要特点是"将对象的创建与使用分离"。GoE(四人组)书中提供了单例、原型、工厂方法、抽象工厂、建造者等5种创建型模式。
- 结构型模式 用于描述如何将类或对象按某种布局组成更大的结构,GoE(四人组)书中提供了代理、适配器、桥接、装饰、外观、享元、组合等7种结构型模式。
- 行为型模式 用于描述类或对象之间怎样相互协作共同完成单个对象无法单独完成的任务,以及怎样分配职责。gof(四人组)书中提供了模板方法、策略、命令、职责链、状态、观察者、中介者、迭代器、访问者、备忘录、解释器等11种行为型模式。
UML
统一建模语言(Unified Modeling Language,UML)是用来设计软件的可视化建模语言。它的特点是简单、统一、图形化、能表达软件设计中的动态与静态信息。
UML从目标系统的不同角度出发,定义了用例图、类图、对象图、状态图、活动图、时序图、协作图、构件图、部署图等9种图。
在设计模式的学习中我们重点学习和使用的时uml类图
类图
类图(Classdiagram)是显示了模型的静态结构,特别是模型中存在的类、类的内部结构以及它们与其他类的关系等。类图不显示暂时性的信息。类图是面向对象建模的主要组成部分。
作用
在软件工程中,类图是一种静态的结构图,描述了系统的类的集合,类的属性和类之间的关系,可以简化了人们对系统的理解; 类图是系统分析和设计阶段的重要产物,是系统编码和测试的重要模型。
表示
在UML类图中,类使用包含类名、属性(filed)和方法(method)且带有分割线的矩形来表示,比如下图 表示一个Employee类,它包含name,age和address这三个属性,以及work()方法。
属性/方法名称前加的加号和减号表示这个属性/方法的可见性,UML类图中表示可见性的符合有三种:
+:表示public -: 表示private #:表示protected 属性的完整表示方式是:可见性 名称 :类型 [ = 缺省值] 方法的完整表示方式是:可见性 名称(参数列表) [ : 返回类型]
类与类之间关系的表示方式
单向关联 在UML类图中单向关联用一个带箭头的实线表示。上图表示每个顾客都有一个地址,这通过让Customer类持有一个类型为Address的成员变量类实现。
双向关联 双方各自持有对方类型的成员变量。
在UML图中,双向关联用一个不带箭头的直线表示。上图中在Customer类中维护一个List,表示一个顾客可以购多个商品;在Product类中维护一个Customer类型的成员变量表示这个产品被哪个顾客所购买。自关联 自关联在UML类图中用一个带有箭头且指向自身的线表示。上图的意思就是Node类包含类型为Node的成员变量,也就是"自己包含自己"。【LinkedList底层用到了自关联】
聚合关联 聚合关联是关联关系的一种,是强关联关系,是整体和部分之间的关系。 聚合关系也是通过成员对象来实现的,其中成员对象是整体对象的一部分,但是成员对象可以脱离整体对象而独立存在。例如,学校与老师的关系,学校包含老师,但如果学校停办了,老师依然存在。 在UML类图中聚合关系可以用带空心菱形的实线来表示,菱形指向整体。下图所示是大学和教师的关系图。
组合关系 组合关系表示类之间的整体与部分的关系,但它是一种更强烈的聚合关系。 在组合关系中,整体对象可以控制部分对象的生命周期,一旦整体对象不存在,部分对象也将不存在,部分对象不能脱离整体对象而存在。 在UML类图中,组合关系用带实心菱形的实线来表示,菱形指向整体。下图所示是head和mouth的关系图:
依赖关系 依赖关系是一种使用关系,它是对象之间耦合度最弱的一种关联方式,是临时性的关联。在代码中,某个类的方法通过局部变量,方法的参数或者对静态方法的调用来访问另一个类(被依赖类)中的某些方法来完成一些职责。 在UML类图中,依赖关系使用带箭头的虚线来表示,箭头从使用类指向被依赖的类。下图所示是司机和汽车的关联关系图,司机驾驶汽车:
继承关系 继承关系是对象之间耦合度最大的一种关系,表示一般与特殊的关系,是父类与子类之间的关系,是一种继承关系。 在UML类图中,泛化关系用带空心三角箭头的实线来表示,箭头从子类指向父类。在代码实现时,使用面向对象的继承机制来实现泛化关系。例如,Student类和Teacher类都是Person类的子类,其类图如下图所示:
实现关系 实现关系是接口与实现类之间的关系。在这种关系中,类实现了接口,类中的操作实现了接口中所声明的所有的抽象操作。
在UML类图中,实现关系使用带空心三角箭头的虚线来表示,箭头从实现类指向接口。例如,汽车和船实现了交通工具,其类图如图所示。
软件设计原则
开闭原则 对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果工简言之,是为了使程序的扩展性好,易于维护和升级。
里氏代换原则 里氏代换原则:任何基类可以出现的地方,子类一定可以出现。通俗理解:子类可以扩展父类的功能,但不能改变父类原有的功能。换句话说,子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法。
依赖倒转原则 高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,,细节应该依赖抽象。简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合。
接口隔离原则 客户端不应该被迫依赖于它不使用的方法;一个类对另一个类的依赖应该建立在最小的接口上。
迪米特法则 迪米特法则又叫最少知识原则。其含义是:如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。其目的是降低类之间的耦合度,提高模块的相对独立性。
合成复用原则 合成复用原则是指:尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。
通常类的复用分为继承复用和合成复用两种。 继承复用虽然有简单和易实现的优点,但它也存在以下缺点:
- 继承复用破坏了类的封装性。因为继承会将父类的实现细节暴露给子类,父类对子类是透明的,所以这种复用又称为"白箱"复用。
- 子类与父类的耦合度高。父类的实现的任何改变都会导致子类的实现发生变化,这不利于类的扩展与维护。
- 它限制了复用的灵活性。从父类继承而来的实现是静态的,在编译时已经定义,所以在运行时不可能发生变化。
采用组合或聚合复用时,可以将已有对象纳入新对象中,使之成为新对象的一部分,新对象可以调用已有对象的功能,它有以下优点:
- 它维持了类的封装性。因为成分对象的内部细节是新对象看不见的,所以这种复用又称为“黑箱"复用。
- 对象间的耦合度低。可以在类的成员位置声明抽象。
- 复用的灵活性高。这种复用可以在运行时动态进行,新对象可以动态地引用与成分对象类型相同的对象。
设计模式的几种类型
创建者模式
创建型模式的主要关注点是“怎样创建对象?”,它的主要特点是“将对象的创建与使用分离” 这样可以降低系统的耦合度,使用者不需要关注对象的创建细节。 创建型模式分为:
- 单例模式
- 工厂方法模式
- 抽象工程模式
- 原型模式
- 建造者模式
UML类图
unified modeling language统一建模语言 设计程序之前画 分为三部分:类名,成员变量,成员函数
成员变量中
符号 | 含义 |
---|---|
+ | 表明访问权限是public |
成员变量下有下划线 | 静态成员变量 |
# | 受保护的成员变量 |
- | 访问权限是私有的 |
保护权限 | 变量名:变量类型=默认值 |
类的成员变量下面画一条线表明分离线的下边是成员函数
成员函数中
保护权限 函数名(参数列表):返回类型 如果类是一个抽象类(里面有纯虚函数),在uml类图中类名要用斜体表示,虚函数也要用斜体,纯虚函数不仅要用斜体还有在后面加上=0标记
纯虚函数:只有函数名没有函数实现,在声明结尾处用=0标记,其作用是让子类根据多态实现,拥有纯虚函数的类(抽象类不能被实例化)
继承关系
继承也叫泛化(Generalization)用于描述父子类之间的关系,父类又称为基类或超类,子类又称派生类,在uml中泛化关系用带空心三角的实线表示
继承有两种:普通继承和抽象继承,不管哪种表示继承关系的线的样式都不变
关联关系
关联(Assocition)关系是类与类之间最常见的一种关系,它是一种结构化的关系,表示一个对象与另一个对象之间有联系,如汽车和轮胎、师傅和徒弟、班级和学生等。 在UML类图中,用(带接头或不带箭头的)实线连接有关联关系的类。在C++中这种关联关系在类中是这样体现的,通常将一个类的对象作为另一个类的成员变量。
类之间的关联关系有三种,分别是:单向关联、双向关联、目关联。
单向关联:关联只有一个方向,比如每个孩子(Child)都拥有一个父亲(Parent) 如:b类中有一个a类的成员变量(包含与被包含) 使用带单向箭头的实线哪个类作为当前类的成员变量,剪头就指向哪个类
双向关联:a类中有一个b类的成员变量,b类中有一个a类的成员变量 一般使用没有箭头的实线来连接有双向关联关系的两个类(但有些uml绘图软件使用的是带双向箭头的实线连接)
自关联:自关联指的就是当前类中包含一个自身类型的对象成员,这在链表中非常常见,单向链表中都会有一个指向自身节点类型的后继指针成员,而双向链表中会包含一个指向自身节点类型的前驱指针和一个指向自身节点类型的后继指针。 使用单向箭头的实线指向自己
聚合关系Aggregation
表示整体与部分的关系,在聚合关系中,成员对象是整体的一部分,但成员对象可以脱离整体对象独立存在,在uml中聚合关系使用带空心菱形的直线表示,直线由部分引出,菱形指向整体 整体析构部分不会被析构
组合关系Compoaition
表示的整体和部分的关系,但是在组合关系中整体对象可以控制成员对象的生命周期,一旦整体对象不存在,成员对象也不存在,整体对象和成员对象之间具有同生共死的关系。 在UML中使用实心菱形的直线表示
依赖(Dependency)关系
一种使用关系,特定事物的改变有可能会影响到使用该事物的其他事物,在需要表示一个事物使用另一个事物时便用依赖关系,大多数情况下依赖关系体现在某个类的方法使用另一个类的对象作为参数。 在UML中,依赖关系用带箭头的虚线表示,由依赖的一方指向被依赖的一方 依赖关系通常通过三种方式来实现:
- 一个类的对象作为另一个类中方法的参数
- 在一个类的方法中将员一个类的对象作为其对象的局部变量
- 在一个类的方法中调用另一个类的静态方法 类之间的关系强弱顺序:继承(泛化)>组合>聚合>关联>依赖
三大原则
单一职责原则
C++面向对象三大特性之一的封装指的就是将单一事物抽象出来组合成一个类,所以我们在设计类的时候每个类中处理的是单一事物而不是某些事物的集合 如果一个类承担的职责过多,就等于把这些职责耦合到了一起,一个职责的变化可能会削弱或者抑制这个类完成其他职责的能力。这种耦合会导致设计变得脆弱,当变化发生时,设计会遭受到意想不到的破坏。
开放-封闭原则
软件实体(类、模块、函数等)可以扩展,但是不可以修改。也就是说对于扩展是开放的,对于修改是封闭的。 该原则是程序设计的一种理想模式,在很多情况下无法做到完全的封闭。但是作为设计人员,应该能够对自己设计的模块在哪些位置产生何种变化了然于胸,因此 需要在这些位置创建抽象类来隔离以后发生的这些同类变化(其实就是对多态的应用,创建新的子类并重写父类虚函数,用以更新处理动作)。 此处的抽象类,其实并不等价与C++中完全意义上是抽象类(需要有纯虚函数),这里所说的抽象类只需要包含虚函数(纯虚函或非纯虚函数)能够实现多态即可。
依赖倒转原则
- 高层模块不应该依赖低层模块,两个都应该依赖抽象
- 抽象不应该依赖细节,细节应该依赖抽象。
高层模块:可以理解为上层应用,就是业务层的实现。 低层模块:可以理解为底层接口,比如封装好的API、动态库等。 抽象:指的就是抽象类或者接口,在C++中没有接口,只有抽象类
里氏代换原则合理的 所谓的里氏代换原则就是子类类型必须能够声换掉它们的父类类型。使用子类类型替换掉了它们的父类类型。 这个原则的要满足的第一个条件就是继承,其次还要求子类继承的所有父类的属性和方法对于子类来说都是合理的 抽象类中提供的接口是固定不变的 低层模块是抽象类的子类,继承了抽象类的接口可以重写这些接口的行为 高层模块想要实现某些功能,调的是抽象类中的函数接口,并且是通过抽象类的父类指针引用其子类的实例对象(用子类类型替换父类类型)这样就实现了多态 这样低层模块发生变化对应高层模块是没有任何影响的,这样工作量就降低了,代码就更加容易维护。(依赖倒转原则就是对多态的典型应用)
贡献者
版权所有
版权归属:wynnsimon