设计模式学习笔记(二):UML与面向对象设计原则
1 UML
1.1 UML
UML(Unified Modeling Language)是统一建模语言,1997年11月UML1.1版本提交给OMG并正式通过,成为建模语言的个那个也标准。2003年6月UML2.0获得正式通过。
1.2 UML特性
- U(Unified):统一,UML融合了多种优秀的面向对象建模方法以及多种得到认可的软件工程方法,消除了因方法林立且相互独立而带来的种种不便,集众家之长,股名“统一”。通过统一的表示方法可以让不同知识背景的领域专家,系统分析设计人员以及开发人员可以方便地交流
- M(Modeling):UML是一种通用的可视化建模语言,不同与编程语言,UML通过一些标准的图形符号和文字来对系统进行建模,用于对软件进行描述,可视化处理,构造系统制品的文档。UML适用于各种软件开发方法,软件生命周期的各个阶段,各种应用领域以及各种开发工具
- L(Language):UML是一种语言,也就意味着它有属于自己的标准表达规则,不是一种类似Java,C++的编程语言,而是一种分析设计语言,一种建模语言
1.3 UML结构
UML结构通常包括以下4个部分:视图,图,模型元素以及通用机制。
1.3.1 视图
UML视图用于从不同的角度来表示待建模的系统。视图是由许多图形组成的一个抽象集合,在建立一个系统模型时,只有通过定义多个视图,每个视图显示该系统的一个特定方面,才能构造出该系统的完整蓝图。
UML视图包括:
- 用户视图:以用户的观点表示系统的目标,是所有视图的核心,用于描述系统的需求
- 结构视图:系统的静态行为,描述系统的静态元素,比如包,类,对象以及它们之间的关系
- 行为视图:系统的动态行为,描述系统的组成元素在系统运行时的交互关系
- 实现视图:系统中逻辑元素的分布,描述系统中物理文件以及它们之间的关系
- 环境视图:系统中物理元素的分布,描述系统中硬件设备以及他们之间的关系
1.3.2 图
UML图是描述UML视图内容的图形,UML2.0提供了13种图,分别是用例图,类图,对象图,包图,组合结构图,状态图,活动图,顺序图,通信图,定时图,交互概览图,组件图和部署图。其中:
- 用例图对应用户视图
- 类图,对象图,包图和组合结构图对应结构视图
- 状态图,活动图,顺序图,通信图,定时图和交互概览图对应行为视图
- 组件图对应实现视图
- 部署图对应环境视图
1.3.3 模型元素
模型元素是指UML图中所使用的一些概念,对应于普通的面向对象概念,如类,对象,消息以及这些概念之间的关系,如关联关系,泛化关系等。
1.3.4 通用机制
UML提供的通用机制为模型元素提供额外的注释,信息和语义,这些通用机制也提供了扩展机制,允许用户对UML进行扩展,如定义新的建模元素,扩展原有的语义,添加新的特殊信息来扩展模型元素的规则说明等,以便适用于特定的方法或过程,组织和用户。
2 UML类图
2.1 类图
类封装了数据和行为,是具有相同属性,操作,关系的对象集合的总称。类图是用出现在系统中不同类来描述系统的静态结构,主要描述不同的类以及它们之间的关系。
在UML中,类图包含类名,属性以及操作。如下面的Employee类:

类一般由三部分组成:
- 类名
- 属性
- 操作
2.1.1 类名
类名就是类的名字,一个字符串。
2.1.2 属性
类的成员变量,一般的格式为
可见性 名称 : 类型 [ = 默认值]
可见性表示该属性对于类外的元素是否可见,包括:
- 公有:
+ - 私有:
- - 受保护:
# - 包:
~
2.1.3 操作
UML规定操作的定义方式为:
可见性 名称(参数列表)[ : 返回类型]
- 可见性与属性可见性的定义一致
- 参数列表表示方法的参数,语法与属性定义类似,用
,分隔
2.2 类之间的关系
UML提供了四种不同的方式表示类与类之间的关系:
- 关联关系
- 依赖关系
- 泛化关系
- 接口与实现关系
下面逐个看一下。
3 关联关系
关联关系是一种结构化关系,用于表示一类对象与另一类对象之间有联系。在UML中用实线连接有关联关系的类。可以在关联线上标注角色名,关系的两端代表两种不同的角色,因此在一个关联关系中可以包含两个角色名,角色名不是必须的,但可以使类之间的关系更加明确。
例如在一个登录界面类LoginForm包含一个JButton:

UML中关联通常包括以下六种形式:
- 双向关联
- 单向关联
- 自关联
- 多重性关联
- 聚合关系
- 组合关系
3.1 双向关联
默认情况下关联是双向的,例如顾客购买商品并拥有商品,反之卖出的商品总是某个顾客与之相关联:

3.2 单向关联
关联也可以是单向的,在UML中关联用带箭头的实线表示,比如顾客拥有地址:

3.3 自关联
系统中可能会存在一些类的属性对象类型为该类本身,这种特殊的关联关系为自关联,常见于链表:

3.4 多重性关联
多重性关联又称为多重性关联联系,表示两个关联对象在数量上的对应关系。在UML中,对象之间的多重性可以直接在关联直线上用一个数字或者一个数字范围来表示。常见的表示方式如下:

例如一个界面可以具有0个或多个按钮,但是一个按钮只能从属于一个界面:

3.5 聚合关系
聚合关系表示整体与部分的关系,使用空心菱形表示。聚合关系中部分是整体的一部分,但是部分可以脱离整体独立存在,比如引擎是汽车的一部分,但是引擎可以独立于汽车存在:

3.6 组合关系
组合关系也表示整体与部分之间的关系,但是部分不能脱离整体存在。组合关系使用实心菱形表示。比如人的头和嘴巴是组合关系:

4 依赖关系
依赖关系是一种使用关系,在需要表示“一个事物使用另一个事物”时使用依赖关系。UML中依赖关系用带箭头的虚线表示,由依赖的一方指向被依赖的一方。例如驾驶员开车,开车需要车,也就是驾驶员依赖于车:

5 泛化关系
泛化关系也就是继承关系,用于描述父类与之类之间的关系,父类又叫基类或者超类,子类又称作派生类。UML中泛化关系用带空心三角形的直线表示,箭头指向基类:

6 接口与实现关系
很多语言比如Java,C#都有接口的概念,接口通常没有属性,所有是操作都是抽象的,只有操作的声明没有操作的实现。UML中使用<<Interface>>表示接口:

类与接口之间的实现关系使用空心三角形+虚线表示:

7 面向对象设计原则
7.1 概述
面向对象设计的目标之一是支持可维护性复用,一方面需要实现设计方案或者源代码的重用,一方面要确保系统能够易于扩展和修改,具有较好的灵活性。面向对象设计原则由此诞生,它们蕴含于很多设计模式中,是从许多方案总结出来的指导性原则。常见的7种面向对象设计原则如下:
- 单一权责原则
- 开闭原则
- 里氏代换原则
- 依赖倒转原则
- 接口隔离原则
- 合成复用原则
- 迪米特法则
7.2 单一职责原则SRP
单一权责原则(Single Responsibility Principal):一个类只负责一个功能领域中的相应职责。
或者可以定义为:就一个类而言,应该只有一个引起它变化的原因。
单一权责原则的核心思想是:一个类不能太“累”。一个类(大到模块,小到方法)承担的职责越多,被复用的可能性越小,而且一个类承担的职责过多,就相当于将这些职责耦合在一起,当其中一个职责变化时,可能会影响其他职责的运作,因此需要将职责分离,封装在不同的类中,即将不同的变化原因封装在不同的类中。单一权责原则是实现高内聚,低耦合的指导方针。
7.3 开闭原则OCP
开闭原则(Open-Closed Principle):一个软件实体应当对扩展开放,对修改关闭。即软件实体应尽量不修改原有代码的情况下进行扩展。
其中软件实体可以是一个软件模块,一个由多个类组成的局部结构或者一个独立的类。
一个软件设计符合开闭原则,则可以非常方便地对其进行扩展,而且在扩展时无须修改现有代码,使得软件系统在拥有适应性和灵活性的同时具备较好的稳定性和延续性。
为了满足开闭原则,需要对系统进行抽象化设计,抽象化是开闭原则的关键。可以通过接口,抽象类等定义抽象层,通过具体类进行扩展,修改系统的行为时无需修改抽象层,只需要增加新的具体类来实现新的业务功能即可,实现在不修改已有代码的基础上扩展系统的功能,达到开闭原则的要求。
7.4 里氏代换原则LSP
里氏代换原则(Liskov Substitution Principle):所有引用基类/父类的地方必须能透明地使用其子类的对象。
简单地说就是父类出现的地方可以用子类代替,程序不会产生任何的错误和异常。使用里氏代换原则时,应该将父类设计为抽象类或者接口,让子类继承父类或实现父类接口,并实现父类中声明的方法,运行时,子类实例代替父类实例,可以很方便地扩展系统的功能,无须修改原有子类的代码,增加新的功能可以通过增加一个新的子类来实现。
7.5 依赖倒转原则DIP
依赖倒转原则(Dependency Inversion Principal):抽象不应该依赖具体细节,细节应当依赖于抽象,换言之要针对接口编程,而不是针对实现编程。
依赖倒转原则要求程序在源代码中传递参数时或者在关联关系中,尽量引用高层次的抽象层类,即使有接口和抽象类进行变量类型声明,参数类型声明,方法返回类型声明以及数据类型的转换等,而不是用具体类来做。一个具体类应该只实现接口或者抽象类中声明过的方法,而不要给出多余的方法,否则将无法调用在子类中新增的方法。
在实现依赖倒转原则时,需要针对抽象层进行编程,而将具体类的对象通过依赖注入(Dependency Injection)的方式注入到其他对象中。依赖注入是指当一个对象要与其他对象发生依赖关系时,通过抽象来注入所依赖的对象。常用的注入方式包括:
- 构造注入:通过构造函数来传入具体类的对象
- 设值注入(setter注入):通过setter来传入具体类对象
- 接口注入:通过实现在接口中声明的方法来传入具体类对象
上面的方法在定义时使用抽象类型,在运行时传入具体类型的对象,由子类对象来覆盖父类对象 。
7.6 接口隔离原则ISP
接口隔离原则(Interface Segregation Principal):使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖于那些它不需要的接口。
也就是说,当一个接口太大时需要划分为更小的接口,使用该接口的客户端仅需知道与之相关的方法。每一个接口应该承担一种相对独立的角色,这里的接口有两层意思:
- 一种是指一个类型所具有的方法特征的集合,仅仅是一种逻辑上的抽象
- 另一种是指某个语言具体接口的定义,有严格的定义和结构,比如Java中的interface
ISP对两种不同含义的表达方式有所不同:
- 当接口理解成一个类型所提供的所有方法特征的集合时,这就是一种逻辑上的概念,接口的划分将直接带来类型的划分,可以把接口理解成角色,一个接口只能代表一个角色,每个角色都有它特定的一个接口,此时这个原则可以叫“角色隔离原则”
- 把接口理解成狭义的特定语言的接口,ISP表达的意思是接口仅仅提供客户端需要的行为,客户端不需要的行为则隐藏起来,应当为客户端提供尽可能小的接口,而不提供大的总接口。接口应尽量细化,同时接口中的方法应该尽量少,每个接口中只包含一个客户端(如子模块或者业务逻辑类)所需的方法即可,这种机制也叫“定制服务”
使用接口隔离原则时,注意控制接口的粒度:
- 接口太小导致接口泛滥,不利于维护
- 接口太大将违背ISP,灵活性差,使用不方便
一般而言接口中仅包含为某一类用户定制的方法即可。
7.7 合成复用原则CRP
合成复用原则(Composite Reuse Principal):尽量使用对象组合而不是继承来达成复用目的。
合成复用原则又叫组合/聚合复用原则(Composition/Aggregate Reuse Principal),就是在一个新对象中通过关联关系(组合/聚合)对对象进行重用而不是使用继承。
面向对象设计中,可以通过两种方法在不同环境中复用已有的设计和实现:
- 继承
- 组合/聚合
7.7.1 继承
继承需要严格遵循里氏代换原则,有效使用继承会有助于对问题的理解,降低复杂度,而滥用继承反而会增加系统构建和维护的难度以及系统的复杂度。继承主要带来的问题是会破环系统的封装性,因为继承会将基类实现细节暴露给子类,由于基类内部细节对子类可见,因此叫“白箱复用”。
一般而言两个类之间的关系是“Is-A”关系就可以使用继承。
7.7.2 组合/聚合
尽管可以通过继承来对代码进行复用,一般来说优先考虑组合/聚合。组合/聚合可以使系统更加灵活,降低类与类之间的耦合度。由于新对象可以直接调用已有对象的功能,这样做可以使成员对象的内部实现细节对新对象不可见,所以这种复用叫“黑箱复用”。
一般而言两个类之间的关系是“Has-A”关系就可以使用组合/聚合。
7.8 迪米特法则LoD
迪米特法则(Law of Demeter):一个软件实体应当尽可能少地与其他实体发生相互作用。
迪米特法则又叫最少知识原则(Least Knowledge Principal,LKP),迪米特法则会对软件实体之间通信的宽度与深度进行限制,可以降低系统的耦合度,使类与类之间保持松耦合。
迪米特法则还有几种定义形式:不要和“陌生人”说话,只与直接朋友通信。对于一个对象“朋友”可以是以下几类:
- 对象本身(this)
- 以参数形式传入的对象
- 成员对象
- 如果成员对象是一个集合,那么集合中的元素也是“朋友”
- 当前对象所创建的对象
满足上述条件之一即是“朋友”,否则就是“陌生人”,不能和“陌生人”发生直接交互。
迪米特法则要求设计系统时尽量减少对象之间的交互,通过引入一个合理的中间类来降低现有对象之间的耦合度。应用迪米特法则时需要注意几点:
- 优先将类设计为不变类
- 类划分上尽量创建松耦合的类
- 类结构设计上尽量降低成员变量和成员函数的访问权限
- 在对其他类的引用上,一个对象对其他对象的引用应当降到最低
8 总结


如果觉得文章好看,欢迎点赞。
同时欢迎关注微信公众号:氷泠之路。

设计模式学习笔记(二):UML与面向对象设计原则的更多相关文章
- 设计模式学习(二):面向对象设计原则与UML类图
一.UML类图和面向对象设计原则简介 在学习设计模式之前,需要找我一些预备知识,主要包括UML类图和面向对象设计原则. UML类图可用于描述每一个设计模式的结构以及对模式实例进行说明,而模式结构又是设 ...
- Java设计模式学习笔记(二) 简单工厂模式
前言 本篇是设计模式学习笔记的其中一篇文章,如对其他模式有兴趣,可从该地址查找设计模式学习笔记汇总地址 正文开始... 1. 简介 简单工厂模式不属于GoF23中设计模式之一,但在软件开发中应用也较为 ...
- Java学习笔记二十一:Java面向对象的三大特性之继承
Java面向对象的三大特性之继承 一:继承的概念: 继承是java面向对象编程技术的一块基石,因为它允许创建分等级层次的类. 继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方 ...
- EC++学习笔记(六) 继承和面向对象设计
条款32:确定你的 public 继承塑模出 is-a 关系 public inheritance 意味着 is-a 关系class Derived 以 public 形式继承 class Base, ...
- 设计模式学习系列(一)——IOC设计原则
参考转载自IoC 之 2.1 IoC基础 ——跟我学Spring3
- [Firefly引擎][学习笔记二][已完结]卡牌游戏开发模型的设计
源地址:http://bbs.9miao.com/thread-44603-1-1.html 在此补充一下Socket的验证机制:socket登陆验证.会采用session会话超时的机制做心跳接口验证 ...
- ASP.NET MVC 学习笔记-7.自定义配置信息 ASP.NET MVC 学习笔记-6.异步控制器 ASP.NET MVC 学习笔记-5.Controller与View的数据传递 ASP.NET MVC 学习笔记-4.ASP.NET MVC中Ajax的应用 ASP.NET MVC 学习笔记-3.面向对象设计原则
ASP.NET MVC 学习笔记-7.自定义配置信息 ASP.NET程序中的web.config文件中,在appSettings这个配置节中能够保存一些配置,比如, 1 <appSettin ...
- 学习记录:《C++设计模式——李建忠主讲》2.面向对象设计原则
1.课程内容: 重新认识面向对象:面向对象设计原则: 2.重新认识面向对象 1)理解隔离变化:从宏观层面来看,面向对象的构建方式更能适应软件的变化,将变化所带来的影响减为最小: 2)各司其职:从微观层 ...
- C#设计模式学习笔记:(3)抽象工厂模式
本笔记摘抄自:https://www.cnblogs.com/PatrickLiu/p/7596897.html,记录一下学习过程以备后续查用. 一.引言 接上一篇C#设计模式学习笔记:简单工厂模式( ...
随机推荐
- js中国标准时间转换成datetime格式
var format = function (time, format) { var t = new Date(time); var tf = function (i) { return (i < ...
- Linux安装与使用
1.安装 1.1安装VMware 1.1.1VM12版本安装 1)下载:网盘:链接:https://pan.baidu.com/s/1Jnr--KIy3bSTvRhtB8nfiQ 提取码:czna 2 ...
- docker ssh秘钥免密登录
一.概述 有一台跳板机,已经实现了免密登录后端服务器.但是我写了一个django项目,它是运行在容器中的,也需要免密登录后端服务器. 虽然可以在容器中手动做一下免密登录,但是容器重启之后,之前做的设置 ...
- 后端程序员之路 42、Semaphore
前面学习了Pthreads,了解了线程和线程同步,而同步这个东西,与信号量是密不可分的.下面讨论的主要是Pthreads里的semaphore.h,而不是sys/sem.h [Linux]线程同步之信 ...
- Layui 源码浅读(模块加载原理)
经典开场 // Layui ;! function (win) { var Lay = function () { this.v = '2.5.5'; }; win.layui = new Lay() ...
- IDEA SVN 使用
转: IDEA SVN 使用 一.上传项目到 SVN VCS -> Import into Version Control -> Share Project(Subversion) 点击 ...
- MacOS如何调整JD-GUI反编译工具字体大小
how to change the fontsize of JD-GUI in MacOS? MacOS如何调整JD-GUI反编译工具字体大小? 问题描述 JD-GUI是一款比较好用的反编译工具,不小 ...
- 漏洞复现-ActiveMq反序列化漏洞(CVE-2015-5254)
0x00 实验环境 攻击机:Win 10 靶机也可作为攻击机:Ubuntu18 (docker搭建的vulhub靶场) 0x01 影响版本 Apache ActiveMQ 5.13.0之前 ...
- pyspider的环境安装
第一:确认自己的Python版本3.6.x(因该版本与pyspider较为适应,其他版本易出错) 如果不是3.6版本的,且想将版本替换成3.6版本的有以下处理方法: 1.再装一个3.6版本python ...
- C++11多线程编程(常见面试题)
[题目1] 子线程循环 10 次,接着主线程循环 100 次,接着又回到子线程循环 10 次,接着再回到主线程又循环 100 次,如此循环50次,试写出代码 [题解] 首先我们来分析一下这道题...( ...