当前课程知识点:软件工程与软件自动化 > 第三章 OO与UML > 3.3 通用职责分配模式(GRASP) > 通用职责分配模式
嗨,大家好
今天我们开始讨论GRASP
也就是通用职责分配软件模式
顾名思义,这些模式描述了
在面向对象设计过程中
为类进行职责分配的基本原则
对软件设计工作具有非常重要的指导意义
GRASP总共有9个,包括4个基本模式
和5个扩展模式
在具体讨论GRASP之前
我们先来看一下什么是类的职责
职责就是一个类要承担的契约或义务
从编程实现上来看
职责可以分为两种类型
一种是Do型,就是做
这种职责是通过类的方法来亲自实现的
它不借助其他对象的帮助
二是Know型,就是知道型
这种职责是类借用其他对象来实现的
也就是借力打力
就比如老板给你的直接上司布置了一项任务
他转身安排你去做
从职责上来说,是你的直接上司承担了这项职责
但他不是自己亲自去做
而是调用了你去做
你可能又把这个任务交给了另外一个人去做
但最终总会有一个Do型的类去完成这个任务
下面我们一起来简要地看一看
这9个模式都是什么内容
专家模式是说谁的数据谁负责
因为职责需要用到数据,也就是属性
所以应该把职责所对应的属性封装在一起
创建者模式主要关注对象的生命周期
借用股票市场的一句提醒语就是
new字有风险,使用需谨慎
对象的创建可不仅仅是new一下这么简单
我们后面再详细讨论这一点
低耦合,高内聚,多态
这也是面向对象的基本原则,这里不解释
而控制者模式相当于一个调度中心
它负责根据业务需要把不同的系统消息
分派给不同的控制类
纯虚构模式的主要思路是用纯虚构类
来协调内聚与耦合之间的对立
中介者模式呢,也是为了隔离耦合
在两个耦合度过大的类之间增加一个中间人
最后一个模式是把那些可能产生变化的地方
单独拿出来封装在一起
和稳定的部分隔离开来
将来只对那些容易发生改变的地方进行修改和扩展
下面我们来看一下这个最基本的设计原则
就是信息专家模式
具体来说,如果某个类
拥有它完成某个职责所需要的所有信息
那么这个职责就应该分配给这个类来实现
这里需要注意的是
信息专家应该尽可能是单一职责类型的专家
而不是复合型职责专家
不能因为某个类封装了过多的属性
就把所有的相关职责都给它
这就会破坏单一职责基本原则
如果你想把属性分割到多个类中
而这些属性之间相关性比较大
如果强制切割,那在业务逻辑上可能又太别扭了
下面看一个简单的例子
在网上商店购物车设计的时候
我们需要让每种商品在购物车内只出现一次
如果你购买相同商品,那只需要更新商品的数量
如果仅仅从信息专家的角度出发
判断商品是否相同那我们要用到了它的一个
唯一的商品ID这个属性
这个属性毫无疑问应该是在商品类当中
但如果从全局来考虑
使用这个商品ID的职责太多了
如果把这些职责都放在这个商品类中
就会严重违反单一职责原则
那怎么办呢?其中一种方法
就是把这个职责转移到容器类中
也就是购物车类
在购物车的AddProduct方法中
调用这个职责
如果有相同的就更新购物车里面
有个商品列表,就更新它的数量属性
如果不同就新增一个商品
第二个职责分配模式是创建者模式
一个对象创建另外一个对象
肯定会导致耦合和依赖的产生
所以一般建议在这五种情况下可以创建一个新对象
通俗地说就是
你归我使用
我是你的容器
我经常使用你
等等这些情况下
让我来创建你,不要让别人来创建你
除了确定这种创建关系之外
我们还可能需要进一步地使用更复杂的技术
比如设计模式,来让这种创建更加灵活
而不单单是用一个new了,我们可以用工厂方法
抽象工厂等等这些技术
低耦合是我们设计面向对象系统的一个主要目标
如果两个对象之间耦合低
说明一个类的变化对其他类的影响很低
从而使系统更简单,更容易理解
那么什么情况下会引起这种耦合呢?
一般情况下有这四种
A是B的属性,A调用了B的方法
A的方法中引用了B
比如B作为A的方法的形参或返回类型等
还有就是继承或对接口的实现
可以看出要想避免引起类之间的耦合几乎是不可能的
我们能做的就是尽量降低它们之间的耦合
并且可以使用一些原则和设计模式
来达到降低耦合度的目的
比如通过职责迁移来满足信息专家模式
通过迪米特原则来隔离陌生类之间的接触
避免跨模块之间的类的直接访问等等
通过这些方式来降低耦合
和低耦合相对应的自然是高内聚
基本方法就是从功能上把相关的职责
尽量在同一个类中进行封装
但同时要注意,不要破坏单一职责原则
按照业务逻辑进行职责迁移
高内聚把相关的东西放在一起
便于理解和维护
同时也是一种隔离
也就是说在高内聚设计的同时
也满足了低耦合的要求
所以高内聚和低耦合往往是如影相随的
控制者模式通过派发的方法把接收到的职责
指定给特定的类去实现
从宏观上看,控制者承担了过多的职责
但这些职责对控制者来说都属于Know型职责
它不需要自己去实现
只需要根据某种业务规则完成路由就可以了
从系统级别来看
控制者转发的职责也往往是下一级组织的控制者
从而完成一个职责树从根到叶子的职责流动
这棵树的叶子节点,就是具有Do型职责的那些类
这里的多态模式和面向对象的基本特性中
的多态是一个含义
这个模式告诉我们
尽量对抽象层编程,对接口编程
然后在系统运行的时候指定特定的
子类或实现类,来完成相关功能
从职责分配来说
就是要把职责具体交给那些子类来实现
继承树上上层的那些类尽可能的是写抽象类
纯虚构模式就是利用面向对象语言的
纯虚构函数的特征
来协调类之间的内聚和耦合
它试图调和内聚和耦合之间天生的这种关系
这种关系是怎么产生的呢?
我们来看,要想达到高内聚要求
我们应该分拆出更小的,单一职责的类
这就导致了类的数量快速增加
类多了,类之间的关系也就复杂了
类之间的依赖也快速增长了
类之间的耦合度大幅增加了
也就是说,你想高内聚,结果导致高耦合了
反过来也一样,你想低耦合
要减少依赖,把职责迁移了
合并了不必要的类,结果发现类变大了
合并了不必要的类,结果发现类变大了
职责混乱了,导致内聚性降低了
纯虚构模式通过抽象分层
来调和这样的一种矛盾
但可能会导致代码复杂性增加,维护困难
因此,在设计和编码的过程中
大家不要死记硬背某一个原则或者是模式
顾此失彼,要做好平衡
中介者就是中间人,就是调停者,就是媒婆
当多个类之间存在着复杂的信息交互的时候
可以引入一个中介者类
把多个类之间的关联职责给它
从而降低了那些类之间的耦合程度
比如这里有6个对象,它们之间有很复杂的关联关系
一个类发生变化
其他好多类都会受到影响
怎么降低这种耦合度呢?
就可以把这种复杂的关系封装一下
形成一个类
这个类就负责维护多对象之间的关联关系
这样,当一个类发生变化的时候
只有中介者受到影响,它需要修改
其他类之间的耦合度就降低很多
这里大家需要注意的是
在设计模式里也有一个中介者模式
它与我们现在讨论的GRASP通用职责分配模式里
的中介者模式相似
只不过我们这里只是提到这个原则
不限制具体怎么做
而设计模式则给出了具体的实现方法,它更具操作性
或者可以说,设计模式中的中介者模式
是GRASP中的中介者模式的一种具体实现
最后一个GRASP模式是受保护的变化
这个模式顾名思义就是要把那些变化的部分拿出来
保护起来。为什么要保护起来呢?
实际上保护的不是这些要变的部分
而是保护那些不变的稳定的部分不受影响
从这个角度来看
受保护变化这种模式与开闭原则十分相似
都是允许扩展它,不允许修改它
在使用这个原则的时候
建议和接口隔离原则结合起来
也就是把那些不稳定的部分分割成
尽可能小的接口单独封装
不要生成胖接口
把接口变化造成的影响降到最低
很多初学者在学习这些
面向对象设计原则和模式的时候
往往是看了后面的忘了前面的
每个原则和模式看起来都挺有道理的
有时候又觉得相互冲突,这样一来二去的就蒙了
前面我们提到过学习三部曲
首先是记忆规则,然后实践规则,最后忘记规则
不要试图一开始就很功利的
一定要用哪几种原则,哪几种模式
而是在不停的迭代过程中
自然而然地使用这些原则和模式
同时要牢牢记住,使用这些原则和模式的根本目的
就是为了满足需求的变更
需要不需要使用原则和模式
先要看这部分的需求是否可能会变更
当然,对变更的精确预测是不可能的
所以要加快迭代速度
尽快把需求的变化暴露出来
然后根据变化做应对
也就是:敌不动,我不动,敌欲动我先动
最后我们来对比一下GRASP和设计模式
作为这部分的小结
一言蔽之,GRASP是类的设计原则和职责分配原则
它不具体告诉你怎么做
而设计模式则是前人专家的经验的总结
它的解决方案比较具体
而且综合遵循了GRASP和面向对象的基本原则
突出解决某一个方面的问题
GRASP我们就讨论这么多,谢谢大家
-1.1 软件工程的前生今世
--开篇阅读
--授课视频
-第一章 软件工程基础--1.1 软件工程的前生今世
-1.2 万变不离其宗
--授课视频1/3
--授课视频2/3
--授课视频3/3
-第一章 软件工程基础--1.2 万变不离其宗
-1.3 唯一不变的是变化
--授课视频1/3
--授课视频2/3
--授课视频3/3
--外部链接
-第一章 软件工程基础--1.3 唯一不变的是变化
-1.4 亡羊补牢为时不晚
--授课视频1/2
--授课视频2/2
-第一章 软件工程基础--1.4 亡羊补牢为时不晚
-扩展阅读与话题讨论
--扩展阅读
--话题讨论
-2.1 方法论来源于恐惧
--授课视频
-第二章 敏捷开发--2.1 方法论来源于恐惧
-2.2 敏捷是什么
--授课视频
-第二章 敏捷开发--2.2 敏捷是什么
-2.3 典型敏捷开发方法
--XP敏捷开发方法
-第二章 敏捷开发--2.3 典型敏捷开发方法
-2.4 敏捷不是万能药
--授课视频
-第二章 敏捷开发--2.4 敏捷不是万能药
-专家谈敏捷
-扩展阅读与话题讨论
--外部链接
--话题讨论
-3.1 面向对象核心概念和基本特性
-第三章 OO与UML--3.1 面向对象核心概念和基本特性
-3.2 面向对象设计基本原则
-第三章 OO与UML--3.2 面向对象设计基本原则
-3.3 通用职责分配模式(GRASP)
--通用职责分配模式
-3.3 通用职责分配模式(GRASP)--作业
-3.4 从重构到模式
--模式和设计模式
-第三章 OO与UML--3.4 从重构到模式
-3.5 使用UML设计面向对象系统
--UML综述
-第三章 OO与UML--3.5 使用UML设计面向对象系统
-3.6 主要UML模型图绘制技巧
--UML用例图
--UML类图
-第三章 OO与UML--3.6 主要UML模型图绘制技巧
-扩展阅读与话题讨论
--设计模式有毒么?
--话题讨论
-4.1 案例简介
--书籍参考
--案例说明
-4.2 对象模型之一
--授课视频1/2
--授课视频2/2
-第四章 对象模型分析--4.2 对象模型之一
-4.3 对象模型之二
--授课视频1/2
--授课视频2/2
-第四章 对象模型分析--4.3 对象模型之二
-4.4 对象模型之交互
--授课视频
-第四章 对象模型分析--4.4 对象模型之交互
-扩展阅读与话题讨论
--图书推荐
--话题讨论
-5.1 软件自动化概述
--软件自动化概述
-第五章 软件自动化技术--5.1 软件自动化概述
-5.2 典型自动化方法和工具
-第五章 软件自动化技术--5.2 典型自动化方法和工具
-5.3 文档自动化
--文档自动化视频
-第五章 软件自动化技术--5.3 文档自动化
-5.4 测试自动化
--测试自动化视频
-第五章 软件自动化技术--5.4 测试自动化
-专家访谈
-扩展阅读与话题讨论
--话题讨论
-6.1 持续集成
-第六章 CI/CD与DevOps--6.1 持续集成
-6.2 持续交付和部署
-第六章 CI/CD与DevOps--6.2 持续交付和部署
-6.3 DevOps
-第六章 CI/CD与DevOps--6.3 DevOps
-专家访谈
-扩展阅读与话题讨论
--DevOps专题
--话题讨论
-7.1 质量和质量保证
--授课视频
-第七章 软件质量保证--7.1 质量和质量保证
-7.2 软件质量模型
--授课视频
-第七章 软件质量保证--7.2 软件质量模型
-7.3 SQA组织与职责
--授课视频
-第七章 软件质量保证--7.3 SQA组织与职责
-7.4 全面软件质量管理
--授课视频
-第七章 软件质量保证--7.4 全面软件质量管理
-专家访谈
--专家访谈
-扩展阅读与话题讨论
--外部链接
--话题讨论
-8.1 软件过程综述
--授课视频
-第八章 软件过程改进--8.1 软件过程综述
-8.2 软件过程改进
--授课视频
-第八章 软件过程改进--8.2 软件过程改进
-8.3 能力成熟度模型
--授课视频
-第八章 软件过程改进--8.3 能力成熟度模型
-8.4 过程改进标准框架
--授课视频
-第八章 软件过程改进--8.4 过程改进标准框架
-扩展阅读与话题讨论
--话题讨论
-9.1软件复用综述
--授课视频
-第九章 软件复用--9.1软件复用综述
-9.2 软件构件技术
--授课视频
-第九章 软件复用--9.2 软件构件技术
-9.3 软件复用实施
--授课视频
-第九章 软件复用--9.3 软件复用实施
-9.4 微服务架构
--授课视频
-第九章 软件复用--9.4 微服务架构
-扩展阅读与话题讨论
--微服务扩展
--话题讨论
-文档提交处--文档提交