信息系统分析与设计 第十章 系统总体设计

信息系统分析与设计 专栏收录该内容
21 篇文章 7 订阅

10.1 软件架构的设计

10.1.1 什么是软件架构

1、架构的概念
建筑、文学、音乐、机械、电子、计算机软硬件等领域都会使用“架构(architecture)”这一概念。架构都提供了系统最高层的设计方案,以确保建筑、小说、乐曲、设备、计算机等系统满足期望的特性。
-好的建筑应该美观、坚固、实用
-好的计算机应用系统应该实用、好维护、可靠、性价比高
架构师(architect)需要发现特定系统的最重要的关注点,设计某种折衷的总体方案以满足关注点。
架构包含系统的一组基本结构(structure),每种结构都有各种类型的部件(component)及其关系构成,架构描述了这些部件的组合、相互调用参照、通信以及其他动态交互。

架构和结构的关系
架构是抽象无形的,体现高层全局的决策,就像文章的中心思想和提纲。
结构是具体有形的,体现决策的贯彻,如同文章的每个段落及细节描述。

架构包含了结构的初步描述和决策。
相同架构的系统,具体结构允许有差异。
使用桥梁来比喻:
桥梁的架构设计可以使用草图描述,架构决定了桥梁的基本结构部件。
-桥梁有梁式桥、拱桥、斜拉桥、悬索桥等架构
斜拉桥的基本结构:索塔 主梁 斜拉索
桥梁的结构设计则需要考虑各种部件的数量、材料、重量、形态等方面,是可以施工的严谨的结构图。
-架构是抽象的,对结构进行了设计和限定,每座桥的结构是具体有形的、元素组合千变万化

2、软件架构
软件架构(software architecture)的定义没有统一的版本,一般认为:一个应用程序或计算系统的软件架构是一个或一组结构,它包含组成系统的软件元素、这些元素对外可见的性质以及它们之间的关系。对外可见的性质指软件元素能够提供的服务、性能特征、错误处理、共享资源的用法等。
-软件的一个结构元素可能是一个子系统、构件、进程、库、数据库、计算结点、遗留系统等等。
软件架构是最高层次的系统分解,它不会囊括所有的结构和行为的定义,它只关注那些被认为是重要的元素。
-架构难以更改,一旦修改,意味着整个系统重建,而结构修改只影响局部。
软件架构(software architecture)包括逻辑设计和物理部署两方面。
逻辑设计:通过对系统的层、包、类、接口和子系统的组织方式来描述;
物理部署: 描述了进程分配和网络配置。

3、软件架构模式
大部分的架构来源于有相似关注点的系统的总结和抽象,这些相似性被描述成某种特殊模式的架构风格,也就是架构模式(architectural pattern)。
一种架构模式就是一个经验秘籍,架构师在设计不同系统时可以重复使用这些先进经验。
-中国建筑有一种攒尖模式,被广泛应用在古典园林中,如三角、四角、五角、八角等亭子,宫殿、坛庙大量应用

软件架构模式就是可重复使用的软件结构风格。

10.1.2 多层应用架构设计

1、分层的含义
基于组件的软件开发,组件根据横向位置划分为多层(N-Layer):
下层组件负责对上层组件提供服务
上层组件可以使用下层组件定义的服务,但下层组件对上层组件一无所知。
层与层之间通常是不透明的,每一层都具有独立的职责
不同层的软件构件可以分布在多台机器上,也可以部署在同一台机器上,形成物理上的多层(N-Tier)

层次模型的理念就是将整个任务横向划分为不同级别,而不是纵向
-比如学校管理纵向划分有教学、人事、财务、后勤等任务
-横向按管理层次划分有主管校长(高层)、部门领导(中层)、普通员工(基层),或处、科、室,按对外接待层次从公司前台、到部门秘书、再到办事人员……
计算机程序的组织结构也可以有纵向划分和横向划分
-纵向:教师管理功能、学生管理功能、课程管理功能……
-横向:界面窗体、业务逻辑类、数据访问类……

自从C/S出现之后,软件就被分层了:
Client端的软件完成前台任务,Server端的软件完成后台任务(一般是DB Server);
Client使用Server端的服务,依赖于Server端。
自从Internet出现之后,软件进一步分层:
Client端的软件(IE浏览器)完成输入输出任务,Web Server上的程序提供业务逻辑处理,后台DB Server完成数据的存取。
C/S常被称为传统的两层,B/S称为三层。
本书的分层将不包含有关系统软件(屏蔽如IE、DBMS等内容),仅讨论应用系统本身的设计,即应用架构设计。

2、三个基本层次
应用软件内部也可以进行多层的划分。
比如一个用户注册程序可以划分为两层:
-Register.aspx/Register.aspx.cs窗体:负责界面数据的输入和格式检验,结果的输出等。
-UserDal类:负责数据库访问和注册规则检查等。
-从应用层面上看,如果整个应用软件都是采用这种方式编程(如订单处理由PlaceOrder.aspx窗体负责界面交互,由Order、Product等类负责数据库访问和订单金额计算等业务逻辑处理),那么称之为两层的应用架构。
可以有两层、三层、四层等不同分层模式。
-Register.aspx/Register.aspx.cs窗体
-UserBll类:负责注册规则检查等业务逻辑。
-UserDal类:负责数据库访问。

传统的C/S应用程序
界面窗口程序中包含所有的内容,如输入输出、界面逻辑控制、业务逻辑运算等。
系统架构是两层,应用架构没有分层。
在这里插入图片描述
经典的三层架构
在这里插入图片描述
1 表现层:处理用户和信息系统之间的交互。
-可以是简单的命令行窗口,也可以功能完善的图形用户界面(胖客户端程序),如基于HTML的浏览器界面(瘦客户端程序),也可以是手机界面。
2 业务逻辑层:也称为领域层或应用层,是信息系统所有和领域相关的工作。
-如根据输入数据或已有数据进行计算,可以是类库或Web服务。依赖于数据访问层获取数据或保存数据。
3 数据访问层:一般指与数据库的交互,主要责任是数据库记录的存取。
-如组件中包含专门的数据访问类,或每个表对应一个数据访问类

3、扩展的五层架构
1 表现层:等同于三层中的表现层。
2 控制层/中介层:是表现层和领域层的中介层,也称应用控制器。
主要表示业务逻辑中的工作流,一般针对于用例的事件流控制。此外还负责会话状态、数据的合成或分解等事务。
3 领域层:业务逻辑中的领域类的集合,不包含复杂工作流。
4 数据映射层:负责将基于对象的领域层数据映射到数据库关系表中的记录。也称为数据持久层,可自行开发或采用持久化框架。
5 数据访问层:负责数据库表的增删改查等操作。持久化框架中包含该层组件。

4、MVC架构模式
模型(Model)
-代表数据,使用对象及其属性实现。
控制器(Controller)
-是模型与视图的联系纽带,客户的请求由控制器处理,它根据客户的请求调用模型的方法,完成数据更新,然后调用视图的方法将响应结果展示给客户。相应的,模型的更新与修改将通过控制器通知视图,保持视图与模型的一致性
视图(View)
-是模型的外在表现形式,视图可以直接访问模型;查询数据信息,当模型中数据发生变化时,它会通知视图刷新界面,显示更新后的数据。

MVC架构示意图
在这里插入图片描述
MVC模式和三层模式有共同特点(业务逻辑、数据和表示的分离),但不完全遵守分层约定。

5. 多层的物理配置
由于应用软件封装成不同层次的独立组件,这给软件部署带来了灵活性:
物理一层:所有应用软件的组件都安装配置在一台机器上。比如全部Web程序、DBMS都在Web服务器上。
物理两层:应用软件的组件配置在两台机器上,比如一部分安装在客户端,另一部分配置在应用服务器上。
物理多层:客户端、一台或多台应用服务器、数据库服务器,即多台服务器的方式,这是分布式结构的一种形式。

多层体系结构的优势
客户对数据的访问通过中间层进行了隔离,数据库的安全性提高了。
应用程序分布部署在多个物理节点上成为可能,从而增强了处理大量的用户负载或计算任务的能力,系统可靠性和响应速度得到了提高。
业务逻辑处于不同的中间服务器,当业务规则变化后,客户端程序基本不做改动,当组件接口不变时,某一层的改动不会影响其它层,这也意味着更好的重用和可维护性(如从窗口界面变更到Web界面)。
将不同层的开发任务在开发者之间适当地分配(如一些人专注页面表现,另一些人专注于业务逻辑),有效地利用开发人员的专长和开发技巧,并且能够提高并行开发能力。

10.1.3 软件框架

“不要重复发明轮子”
软件复用:
-从代码角度(开发态)看有子过程、函数、类等
-从部署角度(运行态)看有类库、Web服务等二进制可执行组件、中间件和平台
架构模式同样可以复用,当架构模式的复用形式不仅仅停留在逻辑层面(如蓝图、方案),而以物理的二进制组件的方式提供重用时,就产生了框架。

软件框架(software framework)是对整个或部分系统可重用的设计和实现。
框架可以选择对某种架构模式的基本结构和接口机制进行编程实现,不仅封装该架构模式的基本元素对外提供类库,还封装底层公用的流程控制逻辑,从而直接为应用软件提供了最初的骨架。
框架就是一个半成品软件平台,软件框架和应用软件:
八股文 => 具体文章 (按照规范填空即可,“听课容易,解题很难,且听且珍惜”)
架构模式/框架/应用程序 => 菜谱菜式/半成品/成品菜

引入软件框架之后,整个开发过程变成了“分三步走”:
决定应用架构
选择实现应用架构的现成框架
基于框架之下编写程序(简单、统一)
优点:
代码具有相同规范和结构,易于理解和维护;提高效率;更稳定更可靠。
局限:
囿于框架所限定的“框框”之内构建应用程序,比较死板,缺乏灵活性

典型框架产品
支持MVC架构模式的框架:
Java开源MVC框架Struts
微软平台的MVC4框架
PHP的Zend框架
多种框架产品的组合使用:
如SSH(Struts、Spring、Hibernate)

Struts2简介
MVC:
View:由JSP页面实现
Model:由自行编写的Action对象完成,Action类就是一个普通的Java类,里面封装了领域对象的属性和方法,可以用于存取数据和执行有关业务逻辑。属性和方法也可以分开到不同类中。
Controller:由Struts2的内置过滤器(dispatcher filter)、拦截器(interceptor)或自行编写的拦截器来实现,过滤器和拦截器可以截获JSP页面请求,解析http请求中的参数,赋值给Action对象中对应的属性,还可以在调用Action之前或之后进行预处理或后处理。
在这里插入图片描述

10.2 高层结构设计

高层结构讨论系统比较大的组成部件(如包、构件、子系统等)及其接口设计。

10.2.1 包

包(Package)是一种逻辑分组手段,可以取UML模型中的任何一种事物,将相关成分聚在一起,以构成更高层的组织单元——包。
最常用的方法是将类以包为单位进行分组,比如上一节提到的层,每一层中的所有类组成一个包。
一个包可以包含其它的包,高层包被分成若干子包,子包又可以在分成更小的包。
-但在Java中,包还指代了物理的组织手段

分包(软件类的分组)有两种原则:
-共同封闭原则(Common Closure Principle)。一个包中的各个类应该是由于相似的原则而改变,即将一组职责相似、但以不同方式实现的类归为一个包中。比如按照层来进行分包就是这种类型。
-共同复用原则(Common Reuse Principle)。一个包中的各个类应该一起被复用,复用其中一个可能需要同时考虑同一个包中的其它协作类。通常和业务功能相关。

包图用来描述包及其依赖关系。
当表现层包中的类要使用领域包中的领域类提供的服务时,表示包就依赖于领域包。
在这里插入图片描述

10.2.2 子系统及接口

当按照相对完整和独立的业务功能或管理职能组织包,并对这样的包进行封装后,一个高层的具有特定功能的可以运行的独立构件就产生了,称为子系统(Subsystem)。
子系统对外可以提供有限的接口,只要接口不改变,不管子系统内部发生什么变化,也不会影响到依赖于该子系统接口的其它子系统。
子系统及其关系使用UML构件图(component diagram)描述。

理解接口概念
词典释义
-两个不同系统(或子程序)交接并通过它彼此作用的部分。
人类与计算机之间的接口称为用户接口。
计算机硬件元件间的接口叫硬件接口。
计算机软件元件间的接口叫软件接口。
-内部接口:系统内部各元件间的接口
-外部接口:系统对外提供给其他系统使用的接口
-API:应用编程接口(Application Programming Interface),一种应用程序提供的外部接口的说法

区分子系统和包
子系统与包在语义上具有差异:
子系统是一种通过一个或多个它所实现的接口来提供行为的软件单位,可运行,具有物理意义。
包并不提供行为,包只不过是用来容纳各种其他模型元素的容器,一般是逻辑意义上的。

子系统的关系
财务子系统将内部操作进行了封装,但对外提供必要的接口(比如一组函数)
销售子系统在执行销售业务过程中可以使用该接口对销售数据执行某些财务操作。对于销售子系统而言,依赖的是财务子系统的接口,并不需要关心财务子系统的具体实现。
采用UML2.0的构件图表示如下:
在这里插入图片描述

10.2.3 构件及接口

构件(component)是系统中实际存在的可更换部分,它实现特定的功能,符合一套接口标准并实现一组接口。
构件是可复用的软件组成成份,可被用来构造其他软件。
-在系统中采用构件软件程序不需要重新编译,也不需要构件自身的源代码并且不局限于某一种编程语言,所以构件的复用也称为二进制复用(binary reuse),因为它是建立在接口而不是源代码级别的复用。
构件及其关系使用UML构件图描述。

构件之间存在依赖关系:
DataAccess构件用于实现数据存取访问,对外提供接口名为SearchInDB(查询数据库,接口参数等细节省略)。
Book构件实现图书的管理,对外提供SearchBook(查询图书)和ExportToXml(导出图书为XML文件)两个接口。
Book构件需要使用DataAccess构件提供的接口,即构件Book依赖于DataAccess构件。
在这里插入图片描述
区分子系统和构件
子系统和构件在结构上具有差异:
子系统是一个系统,是有特定功能的整体,可以直接使用,不会被复用。
构件只是构成系统的元素,不具备单独使用的能力,用于复用。

10.3 结构化设计方法

结构化设计的基本思想
结构化:自顶向下,逐层分解求精
结构化设计:软件模块化,按层次划分

使用功能分解一定程度上能够简化系统结构,使系统容易修改和理解。
具体做法:
把整个软件划分为部分,其中每一部分的功能简单明确,即程序模块(可以是子过程或函数)
划分模块的工作按层次进行,上层模块调用下层模块
每一个模块应尽可能独立
模块间的调用接口要阐明(模块名称、输入数据、输出数据)

10.3.1 模块

模块(Module)一词使用很广泛。通常对应于用一个名字就可以调用的一段程序语句(子程序或函数)。
模块具有输入和输出、逻辑功能、运行程序、内部数据四种属性。
在这里插入图片描述

10.3.2 结构图

结构图(Structure Chart)描述系统的模块结构及模块间的联系
结构图中的主要成分有:
-模块:用长方形表示
-调用:从一个模块指向另一模块的箭头表示前一个模块调用后一个模块。有循环调用和条件调用
-数据:用带圆圈的小箭头表示从一个模块传递给另一模块的数据(有实义)
-控制信息:带涂黑圆圈的小箭头表示一个模块传送给另一模块的控制信息

结构图的画法
在这里插入图片描述
结构图无严格的模块调用顺序,但一般习惯从左至右
因为约定遵从从上向下的调用,调用关系也可以不使用箭头,而直接使用直线
模块间传递的信息如果出现在数据字典中,则视为数据,否则为控制信息

一个完整的结构图
在这里插入图片描述
简单画法的结构图
简化后,忽略信息传递的结构图如下:
在这里插入图片描述借书模块还可分解:
验证读者身份、修改图书状态、保存借阅记录等

10.3.3 模块的联系

为了衡量模块的相对独立性,提出了模块间的耦合(Coupling)与模块的内聚(Cohesion)两个标准
-耦合:模块和模块之间的联系程度
-内聚:模块内部各元素之间的联系程度

设计目标:
模块内的联系越紧越好
模块间的联系越少越好
高内聚低耦合

10.3.4 模块间的耦合

两个模块之间存在联系
在这里插入图片描述
影响耦合度的因素
如果使用模块A需要了解模块B,那么A和B是耦合的。影响模块间耦合程度有三方面的因素:
联系方式--模块间通过什么方式联系
来往信息的作用--模块间来往信息作什么用
数量--模块间来往信息的多少。

离坐标原点越远,耦合程度越高
在这里插入图片描述
耦合分类如下:
数据耦合:采用子程序调用,调用模块将需要进行处理的数据传递给被调模块。数据耦合是不可避免的。
标记耦合:如果调用模块将整个数据记录传递给被调模块,而被调模块只使用了部分数据项,则称为标记耦合或特征耦合。
控制耦合:一个模块将控制信息传递给另一个模块,以控制被调模块的内部处理逻辑。(可以分解)
公共环境耦合:如果两个模块共享同一全局数据,称为公共耦合。
内容耦合:两个模块之间的内部属性有直接关联,也称病态耦合。(某些GOTO语句)

10.3.5 模块的内聚

模块内部各元素(变量、语句)之间存在联系
在这里插入图片描述
内聚的好处
模块的内聚反映模块内部联系的紧密程度。
一个模块只需要做好一件事情,不要过分关心其它任务。
高内聚性的好处是可以提高程序的可靠性。

模块的内聚可以分以下七类:
1、偶然内聚(coincidental cohesion)
2、逻辑内聚(Logical cohesion)
3、时间内聚(temporal cohesion)
4、步骤内聚(procedural cohesion)
5、通信内聚(communicational cohesion)
6、顺序内聚(Sequential cohesion)
7、功能内聚(functional_cohesion)

1、偶然内聚(coincidental cohesion)
当同一个子程序中的操作之间无任何联系时,为偶然内聚性,也叫作“无内聚性”。
比如只是为了将程序中某几处凑巧相同的一些语句组合起来形成的一个模块:
在这里插入图片描述

2、逻辑内聚(Logical cohesion)
将几个逻辑上相似的功能放在一个模块中
在这里插入图片描述
温度转换函数(摄氏和华氏温度的互相转换,if-else语句)
常见的出错处理模块,工作模块发现错误后,调用错误处理模块,将错误号作为控制参数传入,然后出错处理模块根据不同的错误号执行相应的操作(switch…case分支语句)

3、时间内聚(temporal cohesion)
将在有限时间单元内处理的成分组合为同一模块
比如在窗口load事件过程:

        private void AddBook_Load(object sender, EventArgs e)
        {
            //初始化图书类别选择框
            cboType.Items.Add("哲学");
            cboType.Items.Add("法律");
		……

            //初始化出版社选择框
            Publisher p1 = new Publisher();
            DataTable dt = p1.GetAllPublishers();
            cboPublisher.DataSource =dt;
            cboPublisher.DisplayMember = "name";
            cboPublisher.ValueMember = "publisherID";
        }

可视化程序设计中初始化窗口中的缺省选项
还比如:C++的构造函数、析构函数

4、步骤内聚(procedural cohesion)
当子程序中的操作是按某一特定过程结构进行的,就是步骤内聚。
例如:用户想按一定的顺序打印告,子程序设计成是用于按顺序打印销售收入、开支、雇员电话表的。
步骤内聚在时间内聚的基础上增加了次序的约束。

5、通信内聚(communicational cohesion)
当模块内的成分引用共同的数据,而不存在其他联系时,称为通信内聚
在这里插入图片描述

6、顺序内聚(Sequential cohesion)
模块中某个成分的输出是另一成分的输入,顺序内聚有较强的内聚性,是步骤内聚和通信内聚的结合。
但仍然不是最高的内聚类型,包含功能不单一。
比如显示期末成绩通知:
在这里插入图片描述

7、功能内聚(functional_cohesion)
一个模块包括并且仅仅包括为完成一个具体任务所需要的所有成分,称为功能内聚。

功能内聚性是最强也是最好的一种内聚
-例如:打印职工名单,PrintStaffList()
-例如:计算平均分,CalculateAvg()

仅用一个动宾词组能明确指出这个模块的所有功能

耦合和内聚的概念是Stevens等人提出的, 是测量一个模块化系统好坏的标志。
按他们的观点, 给上述七种内聚评分如下:
功能内聚10分
顺序内聚9分
通信内聚7分
步骤内聚5分
时间内聚3分
逻辑内聚1分
偶然内聚0分
可以给一个软件的所有模块打分,最后计算平均分,作为软件结构质量评价的参考

10.4 面向对象设计方法

10.4.1 根据架构设计软件类

一种3层的分层模式:
从分析模型的领域类导出设计阶段中的实体类
增加边界类和控制类完成程序的交互和控制。
为了分辨出类的这三种不同类型,可以采用UML提供的扩展机制——构造型(stetreotype)及其表达符号来定义模型元素构造型
在这里插入图片描述Rose中不同构造型的图符
在这里插入图片描述
1、边界类
边界类的职责是完成系统与其参与者之间的交互。
-接收来自用户和外部系统的信息与请求
-将信息与请求提交给用户和外部系统
通过用例图可以得知每个边界类至少应该与一个参与者有关,参与者类型不同,边界类的设计也不同
边界类包括屏幕窗口、通信接口、打印机接口、传感器、终端以及专用API(应用程序编程接口)等软件对象。
-对于图书馆系统来说,参与者都是系统用户,因此边界类只有窗口界面这一种形式。
-假如考虑提供馆际互借业务,那么系统就会产生与其它外部合作的图书馆系统的交互,这时与外部系统间的通信接口也是一种边界类

识别边界类
根据用例图,每个参与者与一个用例交互,必定导出一个边界类
在这里插入图片描述
2、实体类
实体类来源于领域模型中的类。
实体类是一个软件对象,表示了领域对象的信息,以及具有与它所表示的信息有关的操作。
实体类反映的信息需要在系统中进行处理,并需要进行持久化存储。 持久化存储可以由实体类来实现,也可以设计专门的数据访问类来完成。

边界类和实体类的交互
边界类仅负责数据的输入和输出,不应承担和数据处理有关的业务逻辑,可负责部分不太复杂的数据校验功能(如非空检查、多个输入域之间的约束和联动)。
边界类通过与实体类的交互,获得有关数据处理的结果
在这里插入图片描述3、控制类
控制类代表协调、排序、事务处理以及对其它对象的控制,经常用于封装与某个具体用例有关的控制流。
控制类处理和协调用例事件流中的主要动作和控制流,并将部分任务委派给其它对象。
根据分层原则,控制类不封装与参与者交互有关的内容,也不封装与系统处理的长效持久的信息有关的问题,这些问题分别由边界类和实体类进行封装。

识别控制类
当用例逻辑较为复杂,并涉及到多个实体类时,可以考虑采用控制类
简化方案:控制类的职责合并给边界类
在这里插入图片描述
不同类的职责分配
向下依赖的关系:
边界类
-负责与参与者的交互(输入数据、显示数据)
-为GUI的每个弹出式屏幕创建一个边界对象。
控制类(可选)
-负责一个用例的事件流,或部分复杂数据流
实体类
-负责数据的封装
-为每个领域类创建一个实体类

图书馆系统的界面类
在这里插入图片描述图书馆系统的控制类
在这里插入图片描述图书馆系统的实体类
在这里插入图片描述

10.4.2 设计类的属性

1 属性类型和初值
-属性的类型和默认的初始值应该在设计模型中表示出来。类型和属性名之间用冒号隔开,等号之后写初值。选择的数据类型最好是目标语言中可用的。
-关联属性
2 属性的可见性
-类中的每个属性可以有可见性定义,指定该属性可以被其它类利用的程度,
-UML定义了4种属性可见性:
公有(public) “+”
受保护(protected) “#”
私有(private) “-”
包(package) “~”

10.4.3 设计类的方法

交互图中的消息映射为接受消息的对象类的方法(操作)
消息表达式中的参数和返回值等映射为类方法函数的参数和返回值

同义词释疑:
-操作是关于行为的定义,方法是行为的实现,服务是行为的对外表现,消息是行为如何协作,它们用于不同的角度、场合、时机和模型中,但可以将它们认为是同义词

1、职责
类的方法是对象应该执行的操作,也称为对象的职责和义务。
职责是在设计过程中分配给每个类的,可以采取的方法有:
-使用交互图
-使用CRC技术

职责完成的两种情况
一项职责的完成有以下两种情况:
-1. 由某个对象独立承担,比如“计算超期天数”由一个Loan对象负责。
-2. 主要由一个对象负责,但该对象将职责进行了二次分配或分解。比如“负责计算订单总额”应该由一个Order对象负责,Order类为了完成该职责,需求OrderItem对象的协作,OrderItem对象负责提供每个订单项的小计金额(数量*单价)。
软件对象的职责分配可以映射到现实世界中的分工和协作,例如:
-部门经理A布置业务人员B完成一项任务x,B可能一个人独立承担该任务,B也可能将任务分解为x1、x2、x3,自己负责x1和x2,请C负责x3。

职责有两种类型:
1、行为型:即对象本身的方法。比如进行一项计算、被创建时的初始化、执行控制或协调的各项活动。
2、了解型:对象应掌握的信息。比如对象自身的数据和属性、相关联的对象以及能够派生或计算的对象(set/get方法),如Loan类需要了解借出和归还日期(属性),以及所借资源的有关情况,即ResourceItem或ResourceTitle对象(关联对象)。

CRC卡片法
CRC卡片法是一种职责分配技术,CRC是类-职责-协作(Class-Responsibility-Collaboration)的简称。具体实践过程:
首先为系统中每个软件类制作一张卡片
选取一个用例,确定该用例的参与类
取出上述确定的类卡片
通过移动卡片来讨论类如何协作完成用例功能
最后将形成的职责概念记录在类所在的卡片上。
虽然它不是UML的组成部分,但也是一种快捷有效的的OO设计技术。
直接建模(绘制顺序图)同理。

2、对象交互建模
交互模型的设计内容:
1 对象职责的识别,意味着对象协作过程中消息的分发
2 定义消息的完整格式
3 将消息映射为类的操作,并在实现时转化为类的方法

交互模型举例
消息的含义:
-对象A向对象B发送消息X(服务员发炒菜消息给厨师)
-对象B为对象A提供消息X规定的服务(厨师提供炒菜服务)
-对象B有响应消息X的行为(厨师有炒菜行为)
-B的类里有X方法
在这里插入图片描述
在这里插入图片描述

交互图
多个对象的行为采用对象交互来表达,UML2.0提供的交互图有顺序图、交互概览图、通信图和计时图。最常用的是顺序图。
交互的类型:
-参与者和系统之间有交互
-系统内部元素之间也有交互
每个用例对外展示了用户和系统的交互(人-机交互),而内部实现则需要多个对象交互以完成用例事件流规定的各种功能。

顺序图(Sequence Diagram)
顺序图的元素:
参与者
对象
生命线
激活框
消息
控制框架(分支、循环、可选)

系统顺序图
每个用例可以建模为参与者与系统之间的人-机交互:
在这里插入图片描述
顺序图(参与者与对象)
参与者(Actor)实例是一个交互过程(用例)的发起者,通常放置在最左边。
对象(Object)——对象就是类的一个实例,在顺序图中上方以和类相同的图形符号表示,并带有一条叫做“生命线”的垂直虚线。生命线上的矩形条表示对象在特定时间的生存期。
UML规定了对象实例的表示方法:
在这里插入图片描述顺序图(生命线与激活框)
生命线(lifeline)说明了对象的生命周期。
对象如果被销毁了,其生命线就会中断。
激活框(activation box)表明交互中对象何时起作用,激活框在顺序图中是可选的
在这里插入图片描述
顺序图(消息)
消息是对象之间的通信,消息传递的同时对应活动随之发生。
在顺序图中,消息被表示为从一个对象的生命线到另一个对象的生命线的水平箭头。
箭头通过消息名称及消息参数来标记,箭头自上至下的垂直位置来表示时间的先后顺序。
消息响应后可能会回送结果,发送请求的消息采用实线箭头,返回结果的消息采用反向的虚线箭头。但为了模型的清晰,对显而易见的返回通常不必绘出。

顺序图(控制流)
如果用例的备选事件流的步骤较多,可另外为备选事件流单独绘制一个顺序图。
一个顺序图中也可以描述控制流(UML2.0):
-利用交互框架来标示
-框架可以将顺序图中的某个区域框起,并划分成若干片断
-每个框架有一个操作符。对于循环操作,可以使用loop操作符,条件操作则使用alt操作符
-每个分支片断有一个监护条件,满足该条件才会执行该片断内的事件流

复杂控制逻辑由活动图描述更清晰,但活动图不能标识消息。

顺序图举例
包含循环和分支的顺序图
在这里插入图片描述
协作图/通信图
协作图一般可由建模工具自动从顺序图生成,与顺序图是等价的。

在这里插入图片描述
另一种交互图:
-UML1.X称为协作图(Collaboration Diagram)
-UML2.0称为通信图(Communication Diagram)
元素和表示方法与顺序图基本相似,但不表达生命线和消息物理位置,而增加对象消息连接。
消息连接显示为两个对象之间的一条实线,其上附带消息、消息流向和消息的顺序号。
更直观地显示了对象的关系,它更有利于理解对给定对象的所有影响。

图书馆顺序图1
借书(初步设计,消息使用中文)
在这里插入图片描述图书馆顺序图2
还书(备选事件流使用opt框架,表示可选分支):
在这里插入图片描述3、消息的设计
消息规范化设计,需要使用以下表达式语法:
-return := message (parameter : parameterType):returnType
-如果类型信息非常明显或不重要的话,可以省略书写,如:
-reader := getReader(cardID : String)
消息的序号
-消息的次序用自小到大的顺序号来表示。按照消息的完成情况,消息可以有嵌套消息(代表任务分解)。
-嵌套消息的序号按照层次编号,所有嵌套的子消息都是服务于其上层消息。

嵌套消息
嵌套消息含义与结构化方法中的模块调用相同
结构化方法中模块都是单个的,没有分组。面向对象方法也强调模块的概念,但模块都有一个主人——对象
OOD中的模块划分要解决两个问题:
-任务如何分解为子任务?
-各个任务应由谁承担?

嵌套消息示例
计算超期罚金,最高不超过书价
Loan对象在计算超期罚金时还需要获取资源的价格和罚金标准,因此向ResourceItem发送请求价格的消息,向FineRule发送超期罚金标准,而ResourceItem对象并不记录价格,因此请求价格的消息还需要发送给ResourceTitle对象。

在这里插入图片描述返回消息
很多消息发送之后,消息的接收对象会在响应后产生一些结果回传给发送者,这就是返回消息。
在UML交互图中,返回消息以虚线箭头线表示。为了简洁,一般省略返回消息。

自身消息
有些消息是对象发给自己的。
-比如借书界面中要增加一个借阅项目,首先发送消息给控制类请求业务逻辑的处理(makeNewLoan消息),然后在窗口的列表中将新的借阅记录添加进来(addListItem)以备浏览和打印,添加列表的功能是界面类的职责,消息应发给窗口对象自身
-通常是一个类内部的private方法
在这里插入图片描述
4、为类添加方法
对象职责体现为顺序图中类所收到的消息,也就是类的方法。
类的方法可以定义可见性,当然顺序图中的绝大多数消息是公有方法。

10.4.4 设计类的关系

设计四种关系的具体实现:
泛化(类)
关联(对象)
实现(类)
依赖(对象)

1、泛化设计
泛化在面向对象语言中使用继承来实现,继承机制实现了子类拥有父类特性的这一过程。
泛化设计还有一个更重要的目的在于如何实现多态性。

2、关联设计
实现对象关联的一个简单策略就是:
-在关联的源类中声明一个属性来保存对目标类的实例的引用,这种属性称为关联属性或引用属性。
根据关联的导航性,有单向关联和双向关联。
根据关联重数,有一对一、一对多和多对多关联,多对多关联通过建立关联类分解成1对多的关联。
限定关联。

关联可以在对象一产生就已经存在,也可以在运行期间动态建立。
对于那些比较持久并且不会发生变化的关联,或者具有很强归属关系的聚集关联,一般在主对象的构造函数中创建关联对象或记录下关联对象的引用
较为松散的对象关联,一般当某项业务逻辑发生时,两个对象的关联才被确立,可以设计特定方法建立或解除关联

3、接口与实现的设计
所有的实体类都具有这些数据库操作行为,将这些操作抽象出来封装到一个IEntityOperate接口中

4、依赖设计
在类图中依赖关系通常指明一个类的对象实例使用了另一个类的属性和方法。
界面层使用了控制层对象,控制层对象使用了数据访问层对象。

类的耦合
类之间的联系的紧密程度就是类之间的耦合度。
四种关系的耦合程度从高至低:
-泛化
-实现
-关联
-依赖(上层对象依赖于下层对象)

类的内聚
类的合理封装
-内部属性和方法的关系紧密
单一职责的类
-不要杂凑类
-不要大而全的类

10.5 面向服务设计方法

10.5.1 面向服务的基本概念

1、什么是服务
一个“服务”定义了一个与业务功能或业务数据相关的接口,以及约束这个接口的契约,如业务规则、安全性要求、质量要求等。
接口和契约采用中立的、基于标准的方式进行定义,它与实现服务的硬件平台、操作系统和编程语言无关,使得服务能以统一的方式在异构系统之间互相理解和交互。
服务是自治、独立的、无状态的,既要符合能独立的完成进化,并且进化过程不影响其他的逻辑单元。
服务可以被其他服务或程序利用。这种服务之间的应用,是基于相互理解的基础之上,即服务描述。描述文件中描述了服务的名称,服务所需要的数据,处理后返回的数据。

2、SOA的概念
SOA(Service-Oriented Architecture)
是一种架构模式,系统基于服务构件来开发,多个服务通过它们定义良好的接口和契约联系起来。
最早由Gartner公司于1996年提出SOA概念时,是这样描述的:“客户端/服务器的软件设计方法,一个应用系统由软件服务和软件服务使用者组成……SOA与大多数通用的客户端/服务器模型的不同之处在于它着重强调软件构件的松散耦合,并使用独立的标准接口。”
狭义的SOA:一种IT架构风格,是以业务驱动、面向服务为原则的分布式计算模式。
广义的SOA:包含架构风格、编程模型、运行环境和相关方法论等在内的一整套企业应用系统构造方法和企业环境,涵盖分析、设计、开发整合、部署、运行和管理等整个企业信息系统建设的生命周期。

SOA特点
可从企业外部访问
松耦合、粗粒度
面向业务
内部实现可以基于对象,但是作为一个整体,它是面向业务的,而不是面向对象的。

设计服务可以解决:
企业内的应用集成
企业间的应用集成
软件和数据重用
按需业务流程

(1) 企业内跨平台应用集成
企业里经常都要把用不同语言写成的、在不同平台上(异构)运行的各种程序集成起来,而这种集成将花费很大的开发力量。例如Windows应用程序需要从运行在IBM主机上的程序中获取数据;或者把数据发送到主机或UNIX应用程序中去。
即使在同一个平台上,不同软件厂商生产的各种软件也常常需要集成起来。

(2) 跨企业跨行业应用集成
企业所关注的流程跨越了企业的边界,衍生到组织外部,跨企业的应用集成是必然趋势。
例如生产企业与供应商签订了采购订单,供应商的订单系统(或ERP)记录和跟踪订单,所有订单数据及状态变化直接影响到生产企业的供应系统(或ERP),跨企业的两个信息系统之间需要无缝集成。
另外在互联网和电子商务背景下,不同组织间的协同工作模式也在不断创新,比如图书电子商务网站和出版、银行、运输等行业也存在应用系统集成的需求。

(3) 软件和数据复用
SOA是对构件技术的演进,在软件复用上更加优越。数据提供商的数据如何被不同的服务提供商重用?
例如在互联网上提供一个多语种翻译的服务,可以集成到很多应用系统中。
数据生产商或供应商可以利用服务将数据出卖或出租给使用者,并与使用者的应用系统进行整合,例如114黄页信息、天气预报信息、证券信息的拥有者可以基于SOA对外提供数据服务,授权的用户程序(如新浪等门户网站、数据分析软件)能获取这些信息并集成到自己的应用中。证券交易出卖行情的授权。

(4) 按需业务流程定制
传统管理软件将企业的业务功能和流程固化到软件代码中,当企业业务发展导致需要变更IT系统的业务流程时,IT部门并不能快速的满足最新的需求。
SOA关注业务流程和使用标准接口,以服务组件的方式实现流程中的任务或活动。
-SOA提供了业务流程服务化的手段,变化后的业务流程可以通过对服务的重新编排而实现快速定制。
-在 SOA 术语中,业务流程包括依据一组业务规则按照有序序列执行的一系列操作。操作的排序、选择和执行称为服务或业务流程编排。

3、SOA技术概览
在这里插入图片描述
SOA技术架构
(1) 现有应用资产层:这一层包括所有已开发、定制或打包的应用软件或数据库等资产
(2) 服务构件层:包含软件构件,提供服务的实现。
(3) 服务层:服务层由所有在 SOA 中定义的逻辑服务构成。该层的服务可被发现、调用、编排进行形成组合服务,也可以直接被业务流程层和用户表示层使用。
(4) 业务流程层:该层定义服务层中这些服务的组合和编排,将一组服务组合或编排成一个流程。
(5) 用户表示层:也称消费者层,基于服务提供软件功能和数据给最终用户,并具备建立不同应用的连接能力。通过信息门户、富客户端(Ajax、Flex)等技术,为业务流程、组合应用提供了快速创建客户前端的能力,以响应市场变化。
(6) 服务集成层:集成层支持和提供调节能力,确保服务发起者能够向正确的服务提供者传输服务请求,包括路由、协议支持和转换、消息传递/交互风格、异构环境支持、适配器、服务交互、服务实现、服务虚拟化、服务消息传递、信息处理和转换等。
企业服务总线(enterprise service bus,ESB)就是一个可灵活整合和连接不同应用和服务的基础设施,支持:转化请求者和服务之间的传输协议;处理分离资源间的业务事件;转换请求者和服务之间的消息格式;路由服务间的消息传递。
(7) 服务质量层:支持SOA各层非功能性需求,提供了监视、管理和维护诸如安全、性能和可用能力。
(8) 数据和商务智能层:这一层包括信息架构、业务分析和商务智能(business intelligengce,BI)、企业元数据(meta data)管理等等。
(9) 治理层:治理层确保一个组织中的服务和SOA解决方案遵守指定策略、指导方针和标准,SOA治理活动符合企业和IT治理准则和标准。

10.5.2 服务设计

服务分析与设计的步骤:
识别服务
描述服务
服务实现

1、识别服务
目前有三种策略帮助我们识别候选服务:
自上而下的分解
自下而上的现有系统分析
业务目标对齐

图书馆系统的候选服务
以读者的整个服务流程出发,识别出的候选服务:
1 办理读者卡
1.1 提交办卡申请
1.2 发放读者卡
2 借书
2.1 检查读者信息
2.2 处理图书借阅 (2.2.1 保存借阅记录 2.2.2 更新图书信息)
3 还书
3.1 获取借阅记录
3.2 销记借阅记录
3.3 更新图书信息
4 注销读者卡
4.1 获取借阅记录
4.2 转入历史档案

图书馆系统的服务
封装了为3个服务,服务及其操作如下:
在这里插入图片描述2、描述服务
描述服务细节,定义服务规约,包括输入/输出消息等功能性属性,以及服务各种约束定义、服务之间的关系等等。
-首先选择可以暴露的候选服务为最终服务
-然后对服务各方面属性进行描述

图书馆系统的服务
挑选可组装、松耦合、无状态的服务对外进行暴露,例如1.1、2.1、3.1、4等 ,然后进行描述,例如:
2.1 检查读者信息
输入参数:读者卡号
输出参数:读者信息(姓名、身份证号、邮箱、电话、借书限额、已借数量、状态)
其他说明:该服务用来获取读者信息和状态,可以被其他服务使用。
4 注销读者卡
输入参数:读者卡号
输出参数:读者注销是否成功
其他说明:该服务用来注销读者卡号,并将读者资料转入历史档案,返回是否成功注销的结果。该服务可以被其他服务使用。本服务先使用4.1服务获得读者借阅信息,在不存在借阅信息的情况下,再调用4.2服务完成读者资料归档。

3、设计服务的实现
服务如何包装,是全新设计还是重用已有构件和软件类?方案的设计包括:
分析现有系统。了解应用主要功能和对外接口,寻找可复用的构件,例如图书馆系统已有类库。
确定服务分配。确定服务构件和现有系统软件构件间的映射关系,如果映射后发现的数据(消息)不匹配的现象需要设计服务中介进行格式转换,映射不成功的需要设计新的构件等。
确定服务实现策略。选择服务包装的技术方案,如采用web service或其他技术。
设计服务基础设施。如流程引擎、规则引擎等。

10.6 设计原则

总的原则
-抽象与复用(封装、信息隐藏)
-松耦合
面向功能模块
-设计功能内聚的模块,避免使用全局数据
-模块传递的参数作数据用,并且尽可能少
-模块内语句数一般为50-100
面向对象
-单一职责、开放封闭、里氏替换、依赖倒置…
面向服务
-标准化服务合约、服务松散耦合、服务抽象、服务可复用、服务自治、服务无状态、服务可发现性和服务可组合性

抽象与复用
模块化:模块是广泛意义上的建模元素,可以是子过程或函数、类、构件、服务、流程等。模块化强调抽象和封装。
-好的抽象能让人们集中精力考虑问题实质,而忽略问题中与主旨无关的次要部分。
-好的封装能让一个部件暴露出其最本质的内容,让人们快速理解和使用它,不让复杂性蔓延。
模块化的另一个好处就是复用。
-不同业务流程中重复对一组数据执行同一操作,对这类数据和操作进行合理抽象和封装后,就能在多个地方进行复用,节约了成本,提高了效率。

松耦合
任何事物只要相互之间存在某种关系,就意味着事物间的耦合。
-紧耦合:是指应用系统的各部件在功能上、数据上或结构上是紧密相连的,因而每当某个部件发生变化时,相关部分的也要随之进行部分甚至整个应用程序的调整。
-松耦合:面对变化则具有很好的适应能力和应变能力。
耦合和内聚有着密切关系,通常高内聚就会带来松耦合

单一职责原则SRP
即内聚性原则。
单一职责的模块 →单一职责的类。
一个类承担的职责过多,某个职责的变化可能会削弱或者抑制该类完成其他职责的能力,并影响到构建、测试和部署等活动。
多职责会导致脆弱的和不易理解的设计。

开放封闭原则OCP
软件实体(类、模块、函数等)应该是“可扩展”的,但又是“不可修改”的。
“变化才是不变的真理”,但通过设计使得系统能够适应改变又能保持相对稳定,避免僵化的设计。
开放-封闭原则实现两个目标:
-“对于扩展是开放的”(open for extension)。这意味着模块的行为是可扩展的,从而使其具有满足那些改变的新需求。
-“对于更改是封闭的”(closed for modification)。当对模块进行扩展时,不必改动模块的源代码或二进制代码(如dll/jar文件)。

Liscov替换原则LSP
实现OCP的主要机制是抽象和多态。怎样设计最佳的继承层次,Barbara Liskov在1988年首次提出LSP:子类型(subtype)必须能够替换掉它们的基类型(base type)。
假设S是T的子类型,所有使用了T对象的程序(也称客户程序),用S对象替换T对象后,仍能成功执行。
LSP是多态顺利实现的保证,从而使OCP成为可能。因为正是子类型的可替换性才使得使用基类的模块在无需修改的情况下就可以扩展。
增加或修改任何一个子类型,基类不用修改(封闭)
基类的使用者(客户程序)通过多态得到扩展或修改过的行为(开放)。

依赖倒置原则DIP
A 高层模块不应该依赖于低层模块,二者都应该依赖于抽象(也称针对抽象编程);
B 抽象不应该依赖于细节,细节应该依赖于抽象。
结构化设计时,高层模块总是依赖于低层模块。面向对象的分层模式中也是高层的类依赖于底层的类。
按照自上而下的依赖关系,高层的策略设置模块往往是无法重用的,如果设法让高层模块独立于低层模块,则实现重用就变为可能。
依赖倒置原则的启发式建议是“依赖于抽象”,具体做法是将高层需要的服务声明为抽象接口,高层使用这些接口,低层模块实现这些接口,使得高层不再依赖于低层,而是依赖于抽象接口,同样低层也依赖于抽象接口。

10.7 设计模式

GRASP对象职责分配模式
GRASP(General Responsibility Assignment Software Pattern)是一组通用的基本原则和惯用的设计方案,用来指导对象职责的分配和交互图的创建。OOAD经典著作《UML和模式应用》进行了总结和应用。
GoF23种设计模式
由四人组的专著《设计模式》一书总结了广为应用的23种设计模式,每种模式解决了一个特定问题,包括一组合适的对象和对象接口,以及对象间协作的方式。

模式是对成功应用经验的总结与复用
好莱坞电影模式
-社会题材、动作片、言情片、历史题材片…
中国象棋开局
-当头炮、顺炮、列炮、屏风马…
围棋布局
-星小目、三连星、中国流、宇宙流…
古代行军布阵
-八阵图、天门阵、一字长蛇阵…
建筑、服装、交通、社会、文化…诸多模式

设计模式的基本思想
软件是在不断进化的
-需求在不断改变,所以软件应该适应变化
-设计模式是为了让软件更加适应变化,有更多的可复用性;就是有变化时你不用从头重写一次这个软件
如何适应变化?
-就应该封装变化,让变化的影响最小
-封装复杂性,提供简单的接口

遵守上述设计原则:
松耦合
针对接口编程,而不是针对实现编程
使用继承、组合、委托、多态、泛型

设计模式的基本要素
名称:用于助记,形象表示这个模式
问题:这个模式可以解决什么问题
解决方案:这个模式怎样解决这个问题的步骤与方法
效果:使用这个模式与不使用这个模式有什么区别,它有什么优点和缺点

一个问题可以有多种解法,好的解法都可以找到很多种,每种都有优缺点,某个模式也不一定永远是最好的。

  • 1
    点赞
  • 2
    评论
  • 2
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

©️2022 CSDN 皮肤主题:终极编程指南 设计师:CSDN官方博客 返回首页

打赏作者

Lora青蛙

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值