目录
4.1. Common Closure Principle (CCP) 共同封闭原则
4.2. Common Reuse Principle (CRP) 共同重用原则
4.3. Reuse-Release Equivalency Principle (REP) 重用发布等价原则
4.4. The Acyclic Dependencies Principle (ADP) 无环依赖原则
4.5. Stable Dependencies Principle (SDP) 稳定依赖原则
4.6.Stable Abstractions Principle (SAP) 稳定抽象原则
参考:南软考研大书,软工二
这位大佬:Blog of Samperson (samperson1997.github.io)
还有这位大佬:SpriCoder的博客
第八章 软件设计基础
1.软件设计(名词解释)
a)为使一软件系统满足规定的需求而定义系统或部件的体系结构、部件、接口和其他特征的过程;
b)设计过程的结果。
2.软件设计的核心思想
复杂度控制
分解、抽象、层次性
第九、十章 软件体系结构设计与构建
1.体系结构概念
高层抽象(体系结构 = 部件 + 连接件 + 配置)
部件+连接件+配置
部件:承载系统主要功能,包括处理与数据
连接件:定义部件间的交互,是连接的抽象表示
配置:定义了部件和连接件之间的关联方式,将它们组织称系统的总体结构
2.体系结构的风格的优缺点
常见风格模式:
(1)主程序/子程序风格
- 部件:程序、函数、模块
- 连接件:它们之间的调用
-
设计决策与约束
- 基于声明-使用(程序调用)关系建立连接件,以层次分解的方式建立系统部件, 共同组成层次结构。
- 每⼀个上层部件可以"使用"下层部件,但下层部件不能"使用"上层部件,即不允许逆方向调用。(层次性分解,基于定义使用关系)
- 系统应该是单线程执⾏。主程序部件拥有初的执⾏控制权,并在"使用"中将控制权转移给下层子程序。
- 子程序只能够通过上层转移来获得控制权,可以在执⾏中将控制权转交给下层的子程序,并在自身执行完成之后必须将控制权还交给上层部件。
- 隐含子系统结构。
- 优点:流程清晰,易于理解;强控制性。
- 缺点:
- 程序调用是一种强耦合的连接方式,非常依赖交互方的接口规格,使得系统难以修改和复用;
- 基于程序调用(声明-使用)关系的连接方式限制了各部件之间的数据交互,可能会使得不同部件使用隐含的共享数据交流,产生不必要的公共耦合,进而破坏它的“正确性”控制能力。
- 实现:功能分解、集中控制,每个构件一个模块实现,主要是单向依赖。使用utility或tools等基础模块
(2)面向对象式风格
- 部件: 对象或模块
- 连接件: 功能或调用(方法)
- 数据表示对于其他对象是隐藏的;对象负责保持数据表示的完整性(例如一些不变量);每个对象都是自主代理;数据表示和相关操作封装在抽象数据类型,例: 抽象数据类型
- 优点:内部实现的可修改性;易开发、易理解、易复用的结构组织。
- 缺点:接口的耦合性;标识的耦合性;副作用(Ex:A和B都修改C)。
- 适用于核心问题是识别和保护相关机构信息(数据)的应用
- 实现:任务分解,(委托式)分散式控制,每个构件一个模块实现,使用接口将双向依赖转换为单向依赖。将每个构件分割为多个模块,以保证单向依赖,使用utility或tools等基础模块
(3)分层风格
- 上层调用下层,禁止逆向调用,跨层调用
- 部件: 通常是程序或对象的集合
- 连接件: 通常是有限可见度下的程序调用或方法调用
- 优点:设计机制清晰,易于理解;支持并行开发;更好的可复用性与内部可修改性。
- 缺点:交互协议难以修改;性能损失;难以确定层次数量和粒度。
- 应用:适用于包含不同类服务的应用,而且这些服务能够分层组织。尤其当应用可能在某些层改变,例如交互,通信,硬件,平台等。例子:分层通信协议,每一层在某种程度的抽象上提供了通信的基底,低层定义了低层次的交互最底层是硬件连接(物理层)
- 实现:关注点分离(每层逐次抽象),层间接口使用固定协议(固定控制),每层一或多个模块实现,单向依赖,层间数据传递建立专门模块,使用utility或tools等基础模块
(4)MVC风格
- 模型——视图——控制器
- Model子系统被设计成不依赖任何View或Controller子系统,它们状态的改变会传播到View子系统
- 部件:Model部件负责维护领域知识和通知视图变化;View部件负责给用户显示信息和将用户手势发送给控制器;Controller改变模型的状态、将用户操作映射到模型更新、选择视图进行响应
- 连接件: 程序调用,消息,事件
- 优点:易开发性;视图和控制的可修改性;适宜于网络系统开发的特征。
- 缺点:复杂性;模型修改困难。
- 适用于以下应用:在运行时,用户界面的改变很容易且是可能的。用户界面的调整或移植不会影响该应用功能部分的设计和编码。例:Web 应用
- 实现:特定技术,通常专用于WEB:Model与Controller单向,Controller与View双向,Model与View双向
- 典型实现:View:JSP,HTML;Controller:Servlet;Model:JavaBean
题目
- 按照功能分解的方式进行模块分割能够实现高内聚的软件设计:√
- 软件系统设计的主要目的是为系统制定蓝图, (D)并不是软件设计模型所关注的。
- 系统总体结构
- 数据结构
- 界面模型
- 项目范围(这个是在需求部分已经完成)
- 体系结构设计是软件非功能性的实现,而详细设计主要是软件功能性的实现。√
3.体系结构设计的过程
- 分析关键需求和项目约束:分析用例文档和需求规格说明书(包含需求规格和项目约束)。注意既要考虑功能性需求,又要考虑非功能性需求,甚至很大意义上体系结构设计是为了满足非功能性需求。
- 通过选择体系结构风格:选择分层风格(信息系统、并行开发、非web应用),进行评审。
- 进⾏软件体系结构逻辑(抽象)设计:产生逻辑包图
- 依赖逻辑设计进行软件体系结构(实现)设计:产生物理类图
- 完善体系结构设计:关键类图,持久化数据格式的定义等
- 添加构件接口:包、重要文件的创建,定义接口
- 迭代过程3-6
以超市系统设计为例,看书。
4.包的原则
最高原则:包与包之间不能有重复和冗余、复用发布等价原则、共同封闭原则、共同复用原则、无环依赖原则、稳定依赖原则、稳定抽象原则
- 重用发布等价原则(REP):重用的粒度就是发布的粒度
- 共同封闭原则(CCP):包中所有类对于同一类性质的变化应该是共同封闭的,一个变化若对一个包产生影响,则对该包中的所有类产生影响,而对于其他包不造成任何影响。
- 共同重用原理(CRP):一个包中的所有类应该是能够共同重用的。
- 无环依赖原则(ADP):在包的依赖关系图中不能存在环。
- 稳定依赖原则(SDP):朝着稳定的方向进行依赖
- 稳定抽象原则(SAP):包的抽象程度应该和其稳定程度一致
前三条描述的是依赖性,后三条描述的是耦合性
4.1. Common Closure Principle (CCP) 共同封闭原则
- 一起修改的类应该放在一起
- 最小化修改对程序员的影响
- 当需要更改时,对程序员有利
- 如果更改由于编译和链接时间以及重新验证而影响了尽可能少的软件包
- 示例
- 总结
- 将具有相似闭包的类分组
- 面向可以预期的变更封闭包
- 将更改限制为几个软件包
- 降低包装释放频率
- 减少程序员的工作量
- 只对可预测的变更有作用,不可预测的变更会为系统带来极大的破坏能力,并且无法进行预测。
4.2. Common Reuse Principle (CRP) 共同重用原则
- 一起被重用的应该在一起
- 包应重点突出,用户应使用包中的所有类
- 总结
- 根据常见重用对类进行分组:避免给用户不必要的依赖
- 遵循CRP通常会导致软件包拆分:获得更多,更小,更专注的包
- 减少重新使用者的工作
共同封闭原则和共同重用的原则的折衷
- CCP和CRP原则是互斥的,即不能同时满足它们。
- CRP使重用者的生活更加轻松,而CCP使维护者的生活更加轻松。
- CCP致力于使包装尽可能大,而CRP则尝试使包装很小。
- 在项目的早期,架构师可以设置包结构,使CCP占主导地位,并协助开发和维护。后来,随着体系结构的稳定,架构师可以重构程序包结构,以使外部重用程序的CRP最大化。
- 也就是说在软件开发的不同阶段CCP和CRP的重视重用程度时不同的,要根据项目进展来进行不同程度的学习。
4.3. Reuse-Release Equivalency Principle (REP) 重用发布等价原则
- 重用的单位是发布单位
- 关于重用软件
- 可重用软件是外部软件,您可以使用它,但其他人可以维护它。
- 商业和非商业外部软件之间的重用没有区别。
- 例子
- 总结
- 为重用器分组组件(类)
- 单个类通常是不可重用的:几个协作类组成一个包
- 包中的类应构成可重用和可释放的模块:模块提供一致的功能
- 减少重新使用者的工作
- 和相关联的类一起发布,而不是单独进行发布
4.4. The Acyclic Dependencies Principle (ADP) 无环依赖原则
- 程序包的依赖关系结构必须是有向非循环图(DAG)
- 稳定并分批发布项目
- 以自上而下的层次结构组织程序包依赖关系
- 一旦形成环就可能会形成无限的修改(环中的包的修改会导致大量的修改的发生)
4.4.1. Dependencies are a DAG
- 左侧这个树就形成了环状结构
4.4.2. 依赖循环打破循环:第一种方式:
- 这种情况是单向依赖产生的闭环
- 一种解决方案,将Common error依赖的一个GUI中的一个包单独放在外面,就可以避免循环依赖的出现了。
4.4.3. 依赖循环打破循环:第二种方式:
- 另一情况,这种情况是互相依赖产生的闭环,A依赖于X,Y依赖于B
- 解决方案:从Y提供一个接口(这个接口被B实现,被Y持有)
- B实现BY:B依赖于BY
4.4.4. 无环依赖原则的应用场景
- 层次式风格和主程序/子程序风格通常不会产生
- ⾯向对象式风格尤其要注意
- C/S的MVC风格可能会发⽣
- 基于数据流、事件/消息、数据共享进行交互的体系结构风格通常不会发⽣
4.5. Stable Dependencies Principle (SDP) 稳定依赖原则
- 依赖应当指向一个稳定的方向(更改他所需要付出的努力)
- 稳定性:对应于更换包装所需的时间
- 稳定的软件包:项目内难以更改
- 稳定性可以量化
4.5.1. 稳定依赖原则
- 为什么X是稳定的,而Y是不稳定的(别人变化不会影响到我)
包的稳定性度量
- 一种方法是计算进、出该包的依赖关系的数目。可以使用这些数值来计算该包的位置稳定性(positional stability)。
- (Ca)输入耦合度(Afferent Coupling):指处于该包的外部并依赖于该包内的类的类的数⽬。
- (Ce)输出耦合度(Efferent Coupling):指处于该包的内部并依赖于该包外的类的类的数⽬。
- (不稳定性I)I = Ce / (Ca + Ce)
- 该度量的取值范围是[0,1]。I=0表示该包具有大的稳定性。I=1表示该包具有大的不稳定性。通过计算和⼀个包内的类有依赖关系的包外的类的数⽬,就可以计算出实体 Ca 和 Ce 。
- 当⼀个包的I度量值为1时,就意味着没有任何其他的包依赖于该包(Ca = 0);⽽该包却依赖于其他的包(Ce>0)。这是⼀个包不稳定的状态:它是不承担责任且有依赖性的。因为没有包依赖于它,所以它就没有不改变理由,⽽它所依赖的包会给它提供丰富的更改理由。
- 另⼀方面,当⼀个包的I度量值为0时,就意味着其他包会依赖于该包(Ca > 0),但是该包却不依赖于任何其他的包(Ce = 0)。它是负有责任且无依赖性的。这种包达到了⼤程度的稳定性。它的依赖者使其难以更改,⽽且没有任何依赖关系会迫使它去改变。
4.6.Stable Abstractions Principle (SAP) 稳定抽象原则
- 稳定的包应该是抽象的包(接口)
- 不稳定的包应该是具体的包
- 稳定的包装包含高层的设计。
- 使它们成为抽象可以打开它们进行扩展,但可以关闭它们进行修改(OCP)。
- 稳定的难以更改的包装中保留了一些灵活性。
5.8.1. 抽象性度量
- 包的抽象性用抽象类的数⽬和包中所有类的数目进行计算。
- 假如说包中类的总数是Nc, 抽象类的数⽬是Na,那么抽象度A = Na / Nc
- String类(Instability、Abstraction原点)
- String类就是尴尬的处于原点的一个包,导致任何对String的修改都有可能导致之前开发的java程序无法运行。
5.8.2. 例子
5.体系结构构件之间接口的定义
- 体系结构构件之间接口的定义:
- 提供的服务(供接口):语法、前置条件、后置条件
- 需要的服务(需接口):服务名、服务
根据分配的需求确定模块对外接口,如逻辑层接口根据界面的需求得到,数据层接口根据逻辑层调用得到。
根据刺激与响应确定接口,依据详细规格明确接口内容(数据、返回值)
(1)通常情况下,VIEW的required接口可以直接作为相应Logic的Provided
(2)通常情况下,LOGIC的required接口需要分解为同层模块和不同Data的Provided
(3)Data一般没有层间依赖,接口通常来自于上一层的相应模块
【示例】saleLogic的其他接口来源 :
(1)其他被归类为saleLogic模块承担的需求,如退货用例
(2)同层模块间的依赖
解除双向依赖:需要被抽象为一个独立接口和独立包,saleLogic再导入上面的包
独立接口和独立包也属于saleLogic模块最终承担的Provide接口,也需要转化为对Data的Required接口
在分析其他模块时逐步完善,例如进行礼品赠送时查看特定客户的销售记录、库存分析时查看一定时间段内的销售记录或特定商品的销售记录
在独立接口和独立包中定义有限制的领域类供外界使用,例如对象限制为查询接口,限制数据种类等等
6.体系结构开发集成测试用例:Stub和Driver
(1)依据模块接口建立桩程序Stub——为了完成程序的编译和连接而使用的暂时代码,对外模拟和代替承担模块接口的关键类,比真实程序简单的多,使用最为简单的逻辑
【示例】P177 SalesBLService_Stub
(2)编写驱动程序,在桩程序帮助下进行集成测试
View的测试比较特殊,其他层都需要增加Driver进行测试
可以基于Junit编写Driver;基于接口规格设计测试用例
开发View层时:需要logic的stub
开发logic层时:需要模仿view的driver,需要data的stub,需要模拟同层调用的driver和stub
开发data层时:需要模拟logic的driver
(3)持续集成:逐步编写各个模块内部程序,替换相应的桩程序
真实程序不仅实现业务逻辑,而且会使用其他模块的接口程序(真实程序或者桩程序)
开始:客户端: view driver, logic stub, data stub;服务器端: logic driver, data stub
进展:客户端: (逐步替换)driver,逐步替换logic stub, data stub;服务器端: logic driver, 逐步替换 data stub
最后联调:使用真实的客户端和服务器
第11章 人机交互设计
1.可用性(名词解释)
易用性是人机交互中一个既重要又复杂的概念。它不仅关注人使用系统的过程,同时还关注系统对使用它的人所产生的作用,因为比较复杂,所以易用性不是一个单一的质量维度,而是多维度的质量属性。从易于度量的角度讲,易用性的常用维度包括:易学性、易记性、有效率、低出错率和主观满意度。
2.界面设计的注意事项及解释(至少5个)
【题型】例子违反了哪条界面设计原则
(1)不要暴露内部结构
例子:该设计明显暴露了软件结构,三个独立软件过程:创建、更新、解除
(2)展示细节——所见即所得
(3)常见界面类型:软件系统通常同时使用多种界面类型,以适应差异性的用户和任务。
3.精神模型,差异性
(1)精神模型就是用户进行人机交互时头脑中的任务模型
依据精神模型可以进行隐喻(Metaphor)设计:隐喻又被称为视觉隐喻,是视觉上的图像,但会被用户映射为业务事物。用户在识别图像时,会依据隐喻将控件功能与已知的熟悉事物联系起来,形成任务模型;隐喻本质上是在用户已有知识的基础上建立一组新的知识,实现界面视觉提示和系统功能之间的知觉联系。
(2)用户希望看到的+希望用户看到的:识别并添加哪些能够帮助用户完成任务的功能,任务的频率也要纳入考虑
常见错误:加入一些容易加入(例如正好是一个独立的软件过程)的功能,这会扰乱用户的精神模型,影响使用过程的顺利性
(3)新手用户:关注易学性,进行业务导航,尽量避免出错
专家用户:关注效率
熟练用户:在易学性和效率之间进行折中
好的人机交互应该为不同的用户群体提供差异化的交互机制。
既为新手用户提供易学性高的人机交互机制(图形界面)
又为专家用户提供效率高的人机交互机制(命令行、快捷方式、热键)
4.导航、反馈、协作式设计
(1)导航
主动将自己的产品和服务简明扼要地告诉用户,目的是为用户提供一个很好的完成任务的入口,好的导航会让这个入口非常符合人的精神模型。
全局结构按照任务模型将软件产品的功能组织起来,并区分不同的重要性和主题提供给不同的用户。全局结构常用的导航控件包括窗口、菜单、列表、快捷方式、热键等等。全局结构的设计主要以功能分层和任务交互过程为主要依据。
局部结构通过安排界面布局细节,制造视觉上的线索来给用户提供导航。局部结构常用的导航控件包括可视化控件布局与组合、按钮设置、文本颜色或字体大小等等。局部结构的设计主要以用户关注的任务细节为主要依据。
(2)反馈
让用户能够意识到行为的结果,但不能打断用户工作时的意识流
用户喜欢较短的响应时间;较长的响应时间(>15秒)具有破坏性;
用户会根据响应时间的变化调整自己的工作方式;
较短的响应时间导致了较短的用户思考时间;较快的节奏可能会提高效率,但也会增加出错率;
根据任务选择适当的响应时间:打字、光标移动、鼠标定位:50~150毫秒;简单频繁的任务:1秒;普通的任务:2~4秒;复杂的任务:8~12秒
响应时间适度的变化是可接受的;意外延迟可能具有破坏性;经验测试有助于设置适当的响应时间。
(3)协作式设计
A.简洁设计(摘要图片优于文字描述)
列举、隐藏、赋予(标签、图标等线索暗示)
B.一致性设计(确认与删除键相对位置不一致)
C.低出错率设计(用具体的指导来提示用户出错)
限制输入:列表、可选框等选择性组件代替输入框;按钮代替命令行;限定输入:类型,范围,格式…
限制范围:简单化单步操作
辅助:事前提示;事后检查;随时可以撤销
D.易记性设计
减少短期记忆负担;使用逐层递进的方式展示信息;使用直观的快捷方式。重新认知比记忆更容易;设置有意义的缺省值,可以帮助用户减少记忆负担。
第12章 详细设计概述
中层设计+低层设计:实现所有功能性+非功能性需求
1.详细设计的出发点
需求开发的结果(需求规格说明和需求分析模型)和软件体系结构的结果(软件体系结构设计方案与原型)
明确职责建立静态模型(设计类图),明确协作建立动态模型(详细顺序图)
GRASP(General Responsibility Assignment Software Patterns)(1)信息专家模式
基本的职责分配原则之一,把职责分配给具有完成该职责所需信息的那个类
例如:总价(委托)——数目——单价——促销策略:耦合没有增加
总价(得到)——数目、单价、促销策略:增加了耦合(总价知道太多)
优点:促进低耦合、高内聚、维护封装(2)控制器
处理外部事件(用户和系统时钟发生的外部交互)
核心思想:解耦
不要界面直接调用代码,也不要代码直接调用界面,把系统/人/用例作为controller(3)创建者模式
根据潜在创建者类和被示例话类之间的关系决定哪个类创建实例
当满足以下的条件时,B创建A:B“包含”A,或者B组合A;B直接使用A;B拥有A的初始化数据;B记录A(4)纯虚构
软件特有的ui、DAO、RMI、文件读写、复杂行为、设计模式、复杂数据结构、界面与逻辑层的model,不属于现实世界
作用:保持代码可复用性,高内聚、低耦合
2.职责分配
通过职责建立静态模型:面向对象分解中,系统是由很多对象组成的。对象各自完成相应的职责,从而协作完成一个大的职责。类的职责主要有两部分构成:属性职责和方法职责。类与类之间也不是孤立存在的,它们之间存在一定的关系。关系表达了相应职责的划分和组合。它们的强弱顺序为:依赖<关联<聚合<组合<继承。
3.协作
根据协作建立动态模型:(1)从小到大,将对象的小职责聚合形成大职责;
(2)大到小,将大职责分配给各个小对象。通过这两种方法,共同完成对协作的抽象。
4.控制风格
为了完成某一个大的职责,需要对职责的分配做很多决策。控制风格决定了决策由谁来做和怎么做决策
分散式:所有系统行为在对象网络中广泛传播
集中式:少数控制器记录所有系统行为的逻辑
委托式(授权式):决策分布在对象网络中,一些控制器作主要决策
5.建立设计类图或详细顺序图
抽象类的职责->抽象类之间的关系->添加辅助类
辅助类:接口类、记录类(数据类)、 启动类、控制器类、实现数据类型的类、容器类
6.协作的测试
MockObject