文章目录
什么是软件设计
概述
- 设计的流程为:体系结构设计——>详细设计——>人机交互设计
- 在瀑布模型里,软件设计处于需求阶段之后,构造(编程)阶段之前,所以就要考虑到
软件设计与编程是什么关系
软件设计与需求分析是什么关系
软件设计思想的发展
如图所示
- 横轴表示时间,纵轴表示抽象性
- “程序设计”以下,是最初阶段;这一阶段主要精力在于编程,设计的概念不是很重(有设计也是针对程序如何编写)
- “自顶向下、逐步求精”以上:70年代开始
软件与人的关系、与过程的关系:此时开始讨论软件设计
设计开始独立于软件实现
出现结构化设计,抽象数据类型,信息隐藏,面向对象设计
直到90年代初,出现重构、设计模式、为复用而设计的概念 - 90年代到2010年代初:大规模软件设计
体系结构的概念逐渐重要
产品线,框架,构件 - 2010年后:
容器,云,基于容器的云(可以使部署与开发更紧密的联系起来),微服务
软件设计的核心思想
为什么要设计
对设计的需求源于软件开发过程存在的矛盾:事物的复杂性 VS. 思维的有限性
- 7 ± 2规则:人同时能记住的东西的数量在7 ± 2范围内
- 关注点分离与层次性有助于降低复杂
既然同一时刻能处理的信息有限,试着将它们分成几个部分,每次处理一个部分
关注点集中在复杂度小的一个时刻
为什么软件设计很复杂
- 复杂性是软件的基本特性/内在特性,不是一个偶然情况
- 软件复杂的原因
问题域本身就复杂
管理开发过程这件事本身困难:因为软件开发过程不可见(其他工程如盖房子是可见的)
软件更加强调灵活性
计算机是一个离散的世界,用离散的世界去刻画连续的世界也是困难的 - 设计的复杂度
设计的复杂度有两个方面:事物的复杂度和载体的复杂度;各自要体现在对外表现和内部结构上
设计复杂度 = 事物复杂度 + 载体与事物的适配复杂度
设计、过程要克服事物的复杂度;将事物转化为软件的解决方案,要做适配工作,这体现了载体的适配复杂度
问题域是复杂的,软件本身是复杂的;设计就更加复杂:因为它既要解决对外的表现复杂度,又要解决对内的结构复杂度
如何控制软件设计复杂度
- 软件设计的核心思想:分解与抽象
分解是纵切,将问题分解成小块一个一个解决
抽象是横切,为每个实现抽象出接口,使用时只了解接口就可以不用管实现
再复杂的系统也都可以用这个核心思想,通过不断的迭代(分解)变得不再那么复杂
迭代指不断进行分解和抽象(如下) - 分解与抽象的并用和层次性
理解软件设计
什么是设计
- Design is the planning that lays the basis for the king of every object or system
- 精髓在于计划、思维和后续执行
- Ralph将设计定义为
设计(名词):一个对象的规格说明。它由人创造,有明确的目标,适用于特殊的环境,由一些基础类型构件组成,满续一个需求集合,受一定的限制条件约束
设计(动词):在一个环境中创建对象的规格说明 - 如图
软件设计是一份由软件设计工程师创造的,含有软件设计描述和软件原型的规格说明,该规格说明是关于软件部署的物理环境、软件受制于的限制条件、软件要达成的业务目标、软件需要满足的需求规格等内容
注:
设计规格说明包含内容包括:原型、描述文档、设计的原型图甚至代码demo(类似需求阶段的产物:需求原型,描述文档等)
设计规格说明有关的软件所满足的需求规格内容就与前阶段的需求开发联系上了 - 设计是真正做本系统之前的plan/blueprint
- 设计经常需要一个设计师考虑一个对象或过程的审美、功能以及其他方面,这通常需要进行相当的研究、思考、建模、交互调整和重新设计
工程设计与艺术设计
- 软件需要功能与艺术性并重
- 软件设计要:时刻保持以用户为中心,为其建造有用的软件产品;将设计知识科学化、系统化,并能够通过职业教育产生合格的软件设计师;能够进行设计决策与折衷,解决设计过程中出现的不确定性、信息不充分、要求冲突等复杂情况
- 加入的功能都应该是用户需要的,而不是自己能做到的都加入
- 不能单独地割裂工程与艺术;Design = Engeineering + Art
理性主义与经验主义
- 理性主义与经验主义互相促进,使软件工程更好地发展,并非是势不两立的
- 理性主义的代表
理性主义更看重设计的工程性,希望以科学化知识为基础,利用模型语言、建模方法、工具支持,将软件设计过程组织成系统、规律的模型建立过程
在考虑到人的因素时,理想主义者认为人是优秀的,虽然会犯错,但是可以通过教育不断完善自己
设计方法学的目标就是不断克服人的弱点,持续完善软件设计过程中的不足,直到达到完美
形式化软件工程(通过数学证明来证明软件的正确性)的支持者是典型的理想主义 - 经验主义代表
经验主义者在重视工程性的同时,也强调艺术性,要求给软件设计过程框架添加一些灵活性以应对设计中的人的因素
没有过程指导与完全依赖个人的软件设计活动是不能接受的,因为不能保持质量和工程性
一些人的因素决定了完全理性的设计过程是不存在的(不存在完美设计/完全理想的设计):用户并不知道他们到底想要什么样的需求;即使用户知道需要什么,仍然有些事情需要反复迭代才能发现或理解;人类的认知能力有限;需求的变更无法避免;人类总会犯错;人们会固守一些旧的设计理念(而实际上这次的设计活动的不太能复用);不合适复用
**软件设计需要使用一些方法弥补人的缺陷,以建立一个尽可能好(或者不坏)的软件设计过程
文档化、原型、尽早验证、迭代式开发等都被实践证明能够有效弥补人类缺陷
软件设计的演化性
- 需求是外部表现;最终的solution是内部结构
- 外部表现与内部结构之间是有鸿沟的;鸿沟就是设计任务,设计就是要产生内部结构
- 考虑两点
外部需求如何让内部结构实现(需求分配给内部)(分配的是功能性需求),构建一些内部结构来分担责任
对质量的反思:还有非功能性需求,它们大都是质量上的需求,这就需要重新反思与加工,于是形成迭代 - 体系结构就是非功能性需求的体现
- 需求分配与质量反思是迭代的过程
分配了需求,再看这样分配后质量上有何考虑,之后再对系统有什么改进,再不停地进行此过程——这就是设计的演化性
决策
设计的决策
- 软件设计是一种问题求解和决策的过程;问题空间是用户需求和项目约束,解空间是软件解决方案;从问题空间到解空间的转换是一个跳跃性的过程,需要发挥设计师的创造性,设计师跳跃性地建立解决方案的过程叫做决策(提出方案)
- 软件设计的问题求解与决策比普通数学问题复杂的多,因为软件设计面对的问题通常不规则,包含有很多的不确定性和信息不充分情景,所以进行决策时并不能保证决策的正确性,往往需要在很长时间之后才能通过验证发现之前的决策正确与否
决策的约束性
决策具有约束性,也是决策很困难的原因之一
约束有:需求,环境,资源,技术等(人、钱、物、时间)
设计的决策依据
难以找到最好方案,不容易决策也是很困难的原因之一(要考虑很多东西)
这些是进行决策是可以依据的一些方面
决策的多样性
- 决策的选择性
- 多个同样好的方案,选择一个(方案们有不同的适合场景)
- 找不到好的方案,在不坏的方案里进行选择
决策的演化性
- 做决策是演化过程
- 遵循路线:问题决策验证下一个问题
- 决策的影响顺序:前一个决策会影响后一个决策,而且不可预见(不同决策面对的下一个决策可能不同)
- 决策的不可逆性:无法完全消除前期的错误决定
- 决策要一致:前一个决策与后面的决策不能有自相矛盾
决策的概念完整性
概念完郑性与一致性放在一起理解
概念完整性:从需求,到软件设计(架构、交互)等一切都要遵循同一个理念,塑造共同的形象
设计的演化性
- 考虑约束——>做决策,这是一个线性流程:不回头,前影响后,不能撤回
- 为保证的到好效果,保证每个决策的一致性
软件设计分层
概述
- 如图所示
- 一个分层角度:软件体系结构设计与软件详细设计
- 另一个分层角度:高层设计、中层设计与低层设计
- 两个角度的联系
根据系统复杂度等指标,两种不同角度的分层方法结果会有交叉
体系结构包含了高层设计(最抽象部分),但有时候根据系统复杂度不同会包含一部分中层设计(模块、导入/导出)
如:大系统会分为子系统,体系结构设计就是系统级的(即大系统直接谈体系结构就对应到他的子系统上)而如果系统较为简单,体系结构设计就会详细一点(如深入到模块内部)
产生交叉的其他因素还有项目要做到什么程度(即靠近现实多近)或分工(不同人做不同阶段,越靠近实现的归为详细设计,越靠近抽象高层的归为体系结构设计)
低层设计
低层设计的发展
- 1950年代:第一代、第二代语言;语句是最小单位
- 1960年代:第三代语言(COBOL,FORTRAN);出现类型与函数:这是第一次的复杂系统分割
- 1970年代:类型与函数的成熟;理念为:程序=数据结构+算法
低层设计的工作
- 将基本的语言单位(类型与语句)组织起来,建立高质量的数据结构+算法
- 常见设计场景:数组使用、链表使用、内存使用、遍历算法、递归算法
- 典型场景:堆栈、队列、树、排序算法、查找算法……
- 数据结构与算法审美:简洁、结构清晰坚固(可靠、高效、易读)
低层设计的本质
屏蔽程序中复杂的数据结构与算法的实现细节
低层设计被分为抽象层(接口层)与实现层
- 使用者需要知道的接口,即数据结构与算法的语义与使用方法被放在接口层
- 使用者不需要知道的实现细节,即数据结构与算法的具体实现,包括结构的类型定义、存储空间的更改与使用、分支操控、数据操控等都被放在实现层
低层设计:代码设计
- 对一个方法/函数的内部代码进行设计
- 又称软件构造,通常有程序员独立完成
- 依赖于语言提供的机制(面向对象与结构化、引用、地址、指针等方面)
中层设计
中层设计的发展
- 1970年代:函数的成熟与模块的出现
中层设计的工作
- 模块划分:将系统分成简单片段(片段有名字,可以被反复使用——这就是模块概念)
- 名字和使用方法称为模块的抽象与接口
- 模块内部的程序片段称为模块的精化与实现(模块内部的具体实现就是低层设计)
中层设计的本质
模块划分隐藏一些程序片段(数据结构+算法)地细节,暴露接口与外界
- 使用者需要知道是接口,一个现成的可用的模块
- 使用者不需要的是实现细节,现成的数据结构与算法们如何拼接成模块的细节,整个低层设计都是中层设计的实现层
模块化
模块化的目标
- 模块化的目标:完全独立性
- 理想:构造出完全独立的模块
完全独立:本模块与其他模块没关系 - 完全独立的好处
不论模块关系网有多复杂,完全独立有助于理解(每次只考虑一个模块即可),使用和复用,开发(可并行开发),修改(修改时只考虑模块内部)
模块化的问题和困难
- 程序片段之间不可能是完全独立的,总会有跟其他模块有关联
- 方法:尽可能实现独立
模块化,信息隐藏,抽象数据类型,封装等方法
注:模块化与信息隐藏是比编程范式更加高层的设计思想 - 图示
中层设计总结
- 最终审美目标:简洁性、结构清晰、一致性、质量(可修改、易开发(易理解、易测试、易调试)、易复用)
- 直接评价标准:模块化、信息隐藏、面向对象原则
- 中低层设计的问题
过于依赖细节:连接与依赖、接口与实现
大规模变成不能过于依赖细节,如果依赖细节,就会无法有效抽象部件的整体个性,即不能以更高的抽象来思考问题,不利于考虑总体结构和质量属性(质量属性需要在高层抽象上来解决)
大型软件开发的一个根本不同是她更关注如何将大批独立模块组织形成一个系统,也就是说更重视系统的总体组织
高层设计
- 高层设计:体系结构
- 部件承载了系统主要的计算与状态
部件,在谈的时候是逻辑概念,抽象实体;会有环节将它对应到软件代码上 - 连接件承载部件之间交互
在高程设计中,连接件是一个与部件平等的单位(连接件如方法调用) - 部件与连接件都是抽象的类型定义(就像类定义),他们的实例(就像类的对象实例)组织构成了软件系统的整体结构,配置将它们的实例连接起来
- 部件、连接件、配置:他们本身代表了抽象的含义(尽管我们会尽量将它们落实到实体上)
- 高层抽象表征软件的整体状况,再在抽象表征之下考虑质量要求
- 敏捷视点
只有高层设计良好,低层设计才可能良好(低层设计很难再弥补高层设计的缺陷,特别是非功能属性)
只有写完并测试代码后,才能是完成了设计
源代码可能是主要的设计文档,但他通常不是唯一一个必须的(实际应用时,用什么方法来体现不重要,只是要把该体现的东西体现出来)
代码即设计
体系结构设计负责完成非功能性需求
软件设计过程、方法和模型、描述
软件设计过程的主要活动
- 设计的输入:需求
- 设计的需求(不是指软件的功能需求):对设计来说,输入为何输出为何,做什么事(对设计要做的事作出界定)
软件设计的方法和模型
设计的方法
- 结构化设计方法
- 面向对象设计方法
- 数据为中心设计
- 基于构件设计
- 形式化方法设计
动态模型与静态模型
- 用模型来描述设计
- 两类:动态模型、静态模型
- 动态模型
动态模型通常描述的是系统行为和状态转移、怎么发生的行为
比如:排序的过程中如何进行排序 - 静态模型
静态模型是通过快照的方式对系统中时间不变的属性进行描述,通常描述的是状态而不是行为(一个时间点上各自的职责是什么,关系是什么)
比如:一个数字的列表是按大小排序好的
结构化设计模型与面向对象设计模型
- 在结构化设计中
静态模型:实体关系图
动态模型:数据流图、结构图 - 在面向对象设计中
静态模型:类图、对象图、构建图、部署图
动态模型:交互图(顺序图、通信图)、状态图、活动图
软件设计描述
软件设计描述规范
IEEE有两个标准表述
并没有限定设计描述文档的具体结构,而是通过解释一系列概念说明了软件设计描述文档应该遵循的思路和基本内容(遵从思路、包含内容、每个表征的关系和意义)
软件设计描述模型
- 这是从零开始组织起具备各要素和各要素关系的软看设计描述的流程
设计关注点:产生需求;存在设计视角(不同人关注视角不同)
需求是软件设计的输入
软件设计描述包含很多视图(视图必须隶属于某一视角,即某视角下的某视图)
设计视图要由图来组成,画图需要设计语言(最后能推倒设计视角上) - 这是成品的结构示意图
- 软件的抽象描述结构示意图
- 常用设计视角
- 利益相关者
设计视角必须符合在需求中利益相关者的设计关注点
设计对不同利益相关者来讲有不同意义
因为每个利益相关者的目的不一样,对设计就会产生不同设计关注点;而设计得全面考量各个利益相关者的设计关注点,给出相应的设计视角下的设计视图
不同人角度不同;不同角度不同图、不同模型 - 设计理由:做决策要有理由
设计描述文档
文档模版
其中设计理由可以单独写,也可以每个都说明理由
书写要点
- 充分利用标准的文档模版,根据项目特点进行适当的剪裁(因为不同项目可能有不同侧重)
- 可以利用体系结构风格的图,让读者更容易把握高层抽象
- 利用完整的接口规格说明定义模块之间的交互(写体系结构文档时模块接口很重要)
模块之间接口定义好,就可以写模块之间的集成测试用例了;测试用例写好之后,代码一旦写好,就可以马上开始集成 - 从多视角出发,让读者体会到一个立体的软件系统
- 设计文档中应当体现对于变更的灵活性