我是靠谱客的博主 粗心裙子,最近开发中收集的这篇文章主要介绍[课业] 19 | 软工 | 软件体系结构设计与构建体系结构设计体系结构构建体系结构集成与测试体系结构文档化体系结构验证与评审,觉得挺不错的,现在分享给大家,希望可以做个参考。
概述
文章目录
- 体系结构设计
- 体系结构设计过程
- 分析关键需求和项目约束
- 选择体系结构风格
- 进行软件体系结构逻辑设计(抽象)
- 依据概要功能需求与体系结构风格建立初始设计
- 概述
- 实践案例
- 使用非功能性需求与项目约束评价和改进初始设计
- 对上述案例的初步设计的分析
- 连锁超市管理系统最终的软件体系结构逻辑设计方案
- 物理包设计原则
- 概述
- 共同封闭原则
- 共同重用原则
- 重用发布原则
- 无环依赖原则
- 稳定依赖原则
- 稳定抽象原则
- 包设计的过程
- 依赖逻辑设计进行软件体系结构设计(实现)
- 开发包(构件)设计
- 运行时的进程
- 物理部署
- 完善体系结构设计
- 完善软件体系结构设计
- 细化软件体系结构设计
- 添加构建接口
- 体系结构构建
- 包的创建
- 重要文档的创建
- 定义构件之间的接口
- 关键需求的实现
- 体系结构集成与测试
- 概述
- 集成策略
- 概述
- 自顶向下
- 自底向上
- 三明治式
- 持续集成
- 依据模块接口建立桩程序Stub
- 集成与构建
- 项目实例
- 体系结构文档化
- 体系结构验证与评审
体系结构设计
体系结构设计过程
过程为:
- 分析关键需求和项目约束
搞清楚体系结构的输入是什么 - 选择体系结构风格
- 进行软件体系结构逻辑设计
- 依赖逻辑设计进行软件体系结构设计
- 完善体系结构设计
- 添加构建接口
- 迭代过程3-7
说明:
- 整个过程可以分为两步骤:逻辑设计(1-3)和物理设计(4-6),其中3-4是从逻辑设计到物理设计的转化
- 迭代过程是为了不断完善软件体系结构设计
分析关键需求和项目约束
- 图示
- 体系结构的输入:功能需求,非功能需求(质量、性能、约束、接口,项目约束等
- 不同程度的系统考虑输入(需求)范围不同:
简单系统可能只考虑功能需求——功能齐全就好
商业产品——加入项目约束的考虑(钱、人、物、时间等)
复杂产品——加入非功能性需求的考虑 - 实践案例
需求:
概要功能需求:10个功能
非功能性需求:安全需求(Security1~3);约束IC2
项目约束:
开发技术:Java
时间较为紧张
开发人员:不熟悉Web技术
选择体系结构风格
- 实践案例
本例中,协议较为清晰,采用分层风格
分层风格:协议不变情况下易于修改;能够促进并行开发
实际的并行开发(以逻辑、数据、展示分层为例):分三拨人员分别开发
进行软件体系结构逻辑设计(抽象)
依据概要功能需求与体系结构风格建立初始设计
概述
- 此步骤目标:将需求分配到子系统和模块
- 什么样的需求分到同一模块或子系统?
考虑功能的相同性:不同任务但是相同功能
考虑可复用性:结构、数据、行为的可复用性(如对同一数据进行操作的行为可能被放在一起)
实践案例
- 精简示例
销售与退货:互为逆向过程,属于同一功能
产品调整、入库、出库与库存分析:都是对库存数据的增删改查——行为复用
会员发展与礼品赠送:会员数据的操作
销售策略 - 精简结果
基本功能:销售、库存、会员、销售策略、用户 - 确定功能对应的逻辑包
注意起名字最好统一格式
每个功能都有三层,各有一包
- 确定好功能对应的逻辑包后,考虑每个功能依赖的数据(需要依赖这些包中的哪些)
- 根据每个功能设计的包的信息产生初步设计方案(包图)
这个方案中
存在很多复杂的依赖关系
如SaleUI要用到逻辑层4个包的数据——这在开发时,就意味着做展示层的某人要与做逻辑层的好多人都要打交道,代价过大
又如数据层就集中在一个数据库包上,数据库压力大 - 改进包的结构后的初步设计方案
这个方案中
每一个展示层包只和一个业务逻辑包相连;每一个业务逻辑包只与他对应的数据包进行交互;数据分不同包——实现层间包交互是一对一的
数据层分包的好处:如果某个数据层包发生改变,那么对上层的影响也只是一个包
此时的SaleUI包还是需要获得好多逻辑层包的数据,他们不直接相连,而是SalesUI对应的逻辑层包负责与逻辑层其他的有数据需求的包相连进行联系,这就实现了开发中的跨足沟通(代价大)转化为组内沟通(代价相对小)
使用非功能性需求与项目约束评价和改进初始设计
对上述案例的初步设计的分析
- 能够满足项目约束(项目约束)
分层风格能够促进并行开发,从而缩短开发时间
分层风格可以使用Java技术,而不使用Web技术 - 无法满足安全需求和网络分布约束,所以需要改进(非功能需求)
为使其满足安全需求,可以增加用户登录与验证功能,可以建立专门的三个模块(Presentation, Logic, Data),也可以将该功能并入用户管理功能,即为userUI,user,userData三个模块增加新的职责
连锁超市管理系统最终的软件体系结构逻辑设计方案
增加了用户登录包(逻辑层里)
做出决策:展示层与逻辑层放在客户端,数据层放在服务器端
逻辑层与数据层(客户短语服务器端)通过RMI相交流
这就体现了项目约束、非功能需求对架构的更改
物理包设计原则
概述
两类原则
- 内聚性原则:关注点在于包的内聚性,如何重用、变更;如何让包的内部具有更强大的联系,变得高内聚
共同封闭原则(CCP)
共同重用原则(CRP)
重用发布等价原则(REP) - 耦合性原则:关注点在于包的耦合性,如何使包与包之间关联变得弱,实现低耦合
无循环依赖原则(ADP)
稳定依赖原则(SDP)
稳定抽象原则(SAP)
共同封闭原则
- 一起被修改的类应该放在一个包里
- 如果一起改的东西都放在一个包里,那么要修改时就修改一个包就好了,这样利于维护,最小化修改的影响
- 一次修改的影响尽量波及到少的模块,这样编译、连接、重验证的时间和成本都减小
- 项目中
两个接口处的数据对像会一起被修改,放在一起
持久化数据连接们放在一起 - 总结
有相同修改需求的放在一起,注意这里说的一起修改需求都是可预测到的修改
不可预测的东西:要么出现了系统可以忍受,要么就是不可忍受的重大改动(代价大)
没有预测到的改变破坏力很大
再好的设计也是基于能预测到的影响和修改,不可能有高超的设计师能够包括不可预测的修改的影响
共同封闭原则趋势为包越大越好,包数越少越好
每次变更都在少数量的包进行,减少发布频率
共同重用原则
- 一起重用的类放在同一个包里
- 要用它,就要用到它们所有——他们就被放在一起
- 根据重用的角度来分包
- 总结
共同重用原则趋势为形成更多更小的、更加聚焦的包
包越多越好,每个包越小越好 - CCP与CRP的折衷——二者互斥
CCP希望包越大越好,CRP希望包越小越好
CCP有利于维护者和修改者,CRP有利于重用者
应用:在项目早期建包时(根据需求建包)即逻辑上的包时,由CCP主导,考虑开发过程,先让同时改动的东西同包,包越大越好;等到架构稳定的时候,考虑细节时,重构这些包时,考虑CRP更多,务必要分小一点
重用发布原则
- 重用的单元就是发布的单元
- 一个包中的类要么是都可以重用的,要么是都不能重用的
- 重用:要由外部软件来重用,”你用,别人来维护“
- 对于重用来说,不管这个东西是自己开发的还是从外面买来的,都只是一个重用单元,希望重用单元就是发布单元
- 假如此模块是一个重用单元,就一起来发布
- 项目中
Utility和Tools一起被重用,作为发布包时,一起被发布出来 - 总结
单个类没法重用,要将相关联的类(一起重用的类)打个包一起发布,这个包可以单独被重用,也可以单独发布,减少了重用者的工作(每次重用,多个包不合适,一次重用都在一个包里)
无环依赖原则
- 依赖结构应该是有向而无环的
- 如果有了闭环,一旦修改就是无穷的修改,故无环更加稳定,形成自顶向下的结构
- 有环依赖改成无环依赖方法一:抽象出公共依赖部分来改变方向
Comm Error依赖GUI的依赖可以被改进,他造成连环
改进
Comm Error依赖GUI,只依赖于GUI中的Message Manager包,可以讲他从GUI中移出来,使他同时被GUI与Comm Error两个包依赖,消除了环 - 有环依赖改成无环依赖方法二:抽象出接口
A, B, X, Y都是模块,AB的包与XY的包互相依赖
改进图示
从B中抽象出接口BY(即Y所依赖的部分)放入Y的包,B去实现接口
注意:类实现接口,从类的角度来说,要遵从接口的规则,类依赖于接口(而接口与实现什么样子无关);接口的改变一定会影响到实现 - ADP的项目应用场景
层次式风格(严格上层依赖下层)和主程序/子程序(树结构)风格通常不会发生
面向对象式风格尤其要注意(对象之间互相依赖)
客户端/服务器的MVC风格可能会发生
基于数据流、事件/消息、数据共享进行交互的体系结构风格通常不会发生
稳定依赖原则
- 依赖关系指向稳定的方向
- 稳定的指:改变时付出代价大的,别人依赖自己的是稳定的(外面对自己影响小)
- 稳定的不容易变化,但是改变要付出的代价大——应该依赖一个稳定的
- 稳定与不稳定的例子
稳定的:别人依赖于我,我不依赖于别人
X 是稳定的
Y是不稳定的
- 包的稳定性度量
计算进出该包的依赖关系的数目,可以使用这些数值来计算该包的位置稳定性(Positional Stability)
(Ca)输入耦合度(Afferent Coupling):指处于该包的外部并依赖于该包内部的类的类的数目
(Ce)输出耦合度(Efferent Coupling):指处于该包的内部并依赖于该包外部的类的类的数目
不稳定性I = Ce / (Ca + Ce)
该度量值的取值范围为[0, 1],0表示该包具有最大的稳定性,1表示该包具有最大的不稳定性
I = 1时,就意味着没有任何其他的包依赖于该包,而该包却依赖于其他的包,这是一个包最不稳定的状态:他是不承担责任且有依赖性的,因为没有包依赖于他,所以他就没有不改变的理由,而他所依赖的包会给他提供丰富的更改理由;I = 0时,就意味着其他包会依赖于该包,但是该包却不依赖于任何其他的包,他是负有责任且无依赖性的,这种包达到最大程度的稳定性,他的依赖者使其难以更改,而且没有任何依赖关系会迫使他去改变 - 项目中SDP的体现
大家都依赖database包,他相对稳定
稳定抽象原则
- 稳定的包应该是抽象的包(如接口),不稳定的包应该是具体的包
- 抽象之后有利于扩展,修改时好修理
- 抽象性度量
包抽象性用抽象类的数目和包中所有类的数目进行计算
包中类的总数Nc,抽象类的数目Na,抽象度A = Na / Nc - 到主序列的距离
说明:
点(0, 0):稳定而不够抽象,不好的点——因为难以改动(如Java的String类,多年类从未改过String接口)
点(0, 1):类是稳定而抽象的,好
点(1, 0):类不稳定且不抽象,好
连线上的点,类的抽象性与不稳定性成较好的比例
度量抽象性/稳定性合适与否,就看点到直线距离,近一些的问题就不大
线上(图右上角)是不稳定而抽象的包们的位置,“依赖别人多的类”,这种类我们不太在乎是具体还是抽象(因为他们对别人的影响小) - SAP在项目中
直接这样不好,根据SAP,应该在逻辑层与数据层抽象出接口来被依赖(接口就是高抽象的)
包设计的过程
先CCP原则把可能一同变化的类组织成包进行发布(包大趋势)
随着系统的不断增长,我们开始关注创建可重用的元素,于是开始使用CRP和REP来指导包的组合
最后使用ADP、SDP、SAP对包图进行度量,去掉不好的依赖
依赖逻辑设计进行软件体系结构设计(实现)
开发包(构件)设计
- 初始物理包
- 细节考虑和包的添加
考虑到要跨机器(用到RMI技术,依赖于RMI包)和数据持久化(依赖于JDBC包、databaseutility包)
考虑到Presentation层的所有开发包要使用图形类型建立界面,都要依赖图形界面类库包;Presentation层实现时,由mainUI包负责整个页面之间的跳转逻辑,其他包负责各自页面的跳转逻辑
考虑到展示层应该依赖逻辑层的接口、逻辑层应该依赖数据层的接口(会有接口包);传输数据中要有po和vo两个包
考虑到应该去处循环依赖和逻辑层一些关于初始化和业务逻辑层上下文的工作被分配到utilitybl包中去
存在的循环依赖
根据这些物理包,画出超市管理系统开发包图(客户端、服务器端)
客户端
服务器端
运行时的进程
服务器进程、客户端进程中间靠RMI通信
物理部署
代表部署时要做的一些准备
完善体系结构设计
完善软件体系结构设计
- 常见完善的方面:启动、现场初始化、监控、环境建立与维护、清理现场、安全等
- 示例:完善启动和网络链接
细化软件体系结构设计
- 细化逻辑层模块:以salesbl为例
细化出逻辑接口与数据接口 - 数据定义
接口的数据对象
关键类的重要数据结构
Value Object:传递一个值,用于逻辑层与展示层之间传递数据;既可以是某个实体数据的一部分,也可以是很多实体数据的组合
Persistent Object:将数据持久化到数据库里,传递的是值
逻辑层与逻辑层交互,只代表值,写的话只是改变这个值,并没有改变真是的逻辑层对象 - 数据持久化的细化
序列化持久化:补全属性
数据持久化:建好表
添加构建接口
- 构件初步设计
根据分配的需求确定模块对外接口
初步设计关键类
编写接口规范 - 确定模块对外接口
分层方式下,两种接口:逻辑层提供给展示层的;数据层提供给逻辑层的
这两种接口都是按照需求确定的:逻辑层给展示层的接口看展示层对逻辑层有什么需求;数据层给逻辑层的接口看逻辑层对数据层又什么需求
此例中:
两类接口:供接口(对上层展示层)、需接口(对下层数据层) - 编写接口规范
体系结构构建
包的创建
分层:每层一个包
层内模块:每个模块一个包
重要文档的创建
源文件、数据文件、系统文件、配置文件等
文件列表
定义构件之间的接口
以定义SalesBLService为例
public interface SalesBLService{
//与其他模块进行交互,都是销售过程中需要获得数据
public CommodityVO getCommodityByID(int id);
public ArrayList<CommodityPromotionVO> getCommodityPromotionListByID(int commodiyID);
public MemberVO getMember();
//销售步骤(略)
...
//这些销售过程都是会被Sales展示层调用的,故提供这些接口(按需求)
}
关键需求的实现
- 实现一些关键功能需求
目的:“让心里更有底”,消除风险,先完成一些有代表性的功能(一般都是有完整端到端的功能,需要有足够的覆盖面),证明现阶段的成果使我们有能力完成软件(结果是做出一个demo之类)
比如连锁超市中,销售用例就是一个最关键的用例;他所覆盖的面最广,如果销售用例可以实现,其他用例实现就比较有信心了;而这个需求需要做到端到端的实现
比如,连锁超市系统中存在客户端和服务器端,那么我们就需要在客户端通过GUI界面发出指令,通过客户端的业务逻辑处理,访问服务器端的数据,对数据进行修改;只有这样才能表明当前体系结构可以胜任功能性需求 - 对原型的非功能性需求进行估算和验证;如果出现不符合非功能性需求和项目约束的情况,我们还需要对体系结构设计重新调整
比如,连锁超市系统中,我们用文件来保存数据,而当数据越来越多,特别是销售记录要保存一年;这时候,客户端跨网络对服务器上文件查找就比较慢,可能会使我们不满足实时性需求;这时候,可能我就要调整体系结构中数据存储方案,比如换成数据库;或者在客户端的逻辑层和数据层之间假设数据映射层,在客户端运行初始化时,将数据从服务器端载入到客户端的内存中去
注意:估算、验证不一定必须在使用后全都保证正确,只不过测出来不对就要先做调整
体系结构集成与测试
概述
- 体系结构设计完成之后(包、文件、接口定义好之后,验证过非功能需求等之后)进行体系结构集成与测试
- 需求阶段结束时,进行验收测试——验收时按照需求,做的是验收测试用例结果
体系结构设计阶段结束时,进行集成测试——将设计的components拼在一起,可能会出现各种各样问题,如果有就解决
集成策略
概述
- 当体系结构中原型各个模块代码都编写完成并经过单元测试之后,需要将所有模块组合起来形成整个软件原型系统,这就是集成
- 集成的目的是为了逐步让各个模块合成为一个系统来工作,从而验证整个系统的功能、性能、可靠性等需求
- 对于被集成起来的系统,一般主要是通过其暴漏出来的接口,伪装一定的参数和输入,进行黑盒测试
- 根据从模块之间集成的先后顺序,一般有下列几种常见的集成策略
大爆炸式:所有东西放在一起,不利于问题定位
增量式:每次多集成一个小部分(一个模块),一出问题就可以定位在新加入的东西上
自顶向下式,自底向上式,三明治式,持续集成
自顶向下
- 图示
- 过程
如M1写好之后,与已经写好的M1相邻的模块M2、M3、M4的桩程序(作为这几个模块的代替)来测试,如果测试通过,那么M1就OK - 写好的模块测试时,直接相连的模块用桩程序代替参与测试
- 优点:
按深度优先可以首先实现和验证一个完整的功能需求
只需最顶端一个驱动(Driver)
利于故障定位 - 缺点:
桩的开发量大
底层验证被推迟,且低层组件测试不充分 - 软件测试什么时候停:一般不会测试到100%正确再停,而是“钱用尽了就停”,“时间到了就停”
- 测试费时间、费钱(三分之一的经费有时)
自底向上
- 图示
- 过程
底下模块先测试好再搞上面的,被测试模块上面全都改成驱动,测试好了该测下一个模块时就往上加模块 - 优点:
对底层组件较早验证
底层组件开发可以并行
桩的工作量少
利于故障定位 - 缺点:
驱动的开发工作量大
对高层的验证被推迟,设计上的高层错误不能被及时发现
三明治式
既写桩又写驱动,两头同时开测
持续集成
- 集成:将代码集合在一起形成一个可以发布的版本
- 一种增量集成的方法,但他提倡尽早集成和频繁集成
不要等全部开发完再做集成,而是体系结构设计完就把桩程序写完,之后,写完一个模块就可以测试这个写完的模块 - 尽早集成是指不需要总是等待一个模块开发完才把它集成起来,而是在开发之初就利用Stub集成起来
- 结合尽早集成和频繁集成的方法,持续集成可以做到:
防止软件开发中出现无法集成与发布的情况,因为软件项目在任何时刻都是可以集成和发布的
有利于检查和发现集成缺陷,因为最早的版本主要集成了简单的Stub,比较容易做到没有错误;后续代码逐渐开发完成后,频繁集成又使得即使出现集成问题也能够尽快发现、尽快解决 - 持续集成的频率很高,所以手动的集成对软件工程师是无法接受的,必须利用版本控制工具和持续集成工具;入股程序员可以仅仅通过一条命令就完成一次完整的集成,开发团队才有动力并且能够坚持进行频繁的集成
- 持续发布:集成好后将它发布出去,必须有控制工具
依据模块接口建立桩程序Stub
- 桩:
为了完成程序的编译和连接而使用的暂时代码
对外模拟和代替承担模块接口的关键类
比真实程序简单的多,使用最为简单的逻辑 - 桩的定义与使用
模块的类图和一部分交互
该模块桩的定义和使用:使用时,把想要的内容的数据直接填进来,要调用哪个方法就这样调
驱动的定义和使用:看期望结果与通过调用方法得到的结果是否一致
- 编写驱动程序,在桩程序帮助下进行集成测试
驱动程序调用被测部件,被测部件联系到其他模块,这部分联系用其他模块的桩完成
集成与构建
- 集成
使用桩程序辅助集成
编译与链接 - 持续集成
逐步编写各个模块的内部程序,替换相应的桩程序(原来的桩程序是为了测试该体系结构是否合格)
真实程序不仅实现业务逻辑,而且会使用其他模块的接口程序(真实程序或桩程序)
每次替换之后都进行集成测试
项目实例
体系结构文档化
体系结构设计文档模版
体系结构验证与评审
- 评审角度
设计方案正确性、先进性、可行性
系统组成、系统要求以及接口协调的合理性
对于功能模块的输入参数、输出参数的定义是否明确
系统性能、可靠性、安全性要求是否合理
文档的描述是否清晰、明确 - 评审方法
对结果的评审:checklist(评审的是体系结构设计的结果——文档,要求评审人员对细节很熟悉,要能判断是否合理;而不同软件系统结构差别很大,这种对细节的判断代价很大)
对设计决策的评审(不评审结果,评审的是开发过程的决策质量;思想是“决策不差、结果就不会差”;进行评审的人不必懂项目,请人就行) - CheckList
内容 | 解释 |
---|---|
体系结构设计是否为后续开发提供了一个足够的视角 | 体系结构设计是后面的蓝图,能否提供视角很重要(如果用户根据此蓝图产生了新的想象,就说明视角不够) |
是否所有的功能都被分配给了具体模块 | |
是否所有的非功能属性都得到满足 | |
是否所有的项目约束都得到了满足 | |
体系结构设计是否为后继设计提供了简洁的概述、背景信息、限制条件和清晰的组织结构 | |
体系结构设计是否能对应可能发生的变更 | 预测变更 |
不同的体系结构设计视角的依赖是否一致 | 有否矛盾 |
是否关注点在详细设计和用户接口的层次之上 | 是否够高层 |
系统环境是否定义,包括硬件、软件和外部系统 |
最后
以上就是粗心裙子为你收集整理的[课业] 19 | 软工 | 软件体系结构设计与构建体系结构设计体系结构构建体系结构集成与测试体系结构文档化体系结构验证与评审的全部内容,希望文章能够帮你解决[课业] 19 | 软工 | 软件体系结构设计与构建体系结构设计体系结构构建体系结构集成与测试体系结构文档化体系结构验证与评审所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复