软件体系结构 (Software Architecture) 讲义12:面向方面的软件开发 (Aspect-Oriented Software Development, AOSP)
内容 背景 面向方面的编程(AOP) 基于用例捕获和建模关注点 基于用例和方面建立体系结构 Theme-面向方面的分析和设计方法
背景 软件开发范型的变迁 Code Fix模型 --最原始的开发方式 结构化开发:SA、SD、SP --自顶向下,逐层分解 程序越来越大,难以掌握—软件危机 结构化开发:SA、SD、SP --自顶向下,逐层分解 数据与方法分离,内聚性不够—与现实世界模型不一致 面向对象开发:OOA、OOD、OOP –现实世界真实反映 其它:数据流理论 1970 1980 1990 结构化方法 数据流方法 面向对象
背景 面向对象开发技术的核心特点 结构化理论:将应用分解为功能模块、子功能模块、功能接口;与现实问题域的东西没有直接联系 面向对象理论:系统模型是对问题域的直接映射,即从现实世界中直接抽象出一个模型,然后在计算机中实现出来
面向对象开发技术的历史变迁 面向对象编程开启了面向对象发展之门 而面向对象分析、设计方法才是OO思想的真正标志 面向对象程序语言 1970年 1980 1960 1970 1990 Lisp Simula67 SmallTalk72 SmallTalk80 C Objective-C C++ Java 面向对象程序设计 面向对象设计 面向对象分析 面向对象程序语言 1970年 1990年 面向对象编程开启了面向对象发展之门 而面向对象分析、设计方法才是OO思想的真正标志
面向对象开发技术的今天—核心基础 核心基础:构件技术、UML建模技术 构件技术:大型项目与系统的必由之路 独立与厂商的组件描述语言--UML 需要支持多平台:SOA、ESB—连接构件 拥有大量构件:复用、MDA—快速、廉价集成构件 响应日益复杂的业务操作:EA(连通软件与业务的鸿沟)、PLE(处理产品线可变性)、逆向工程(复用遗留系统) 框架:J2EE、.NET等 独立与厂商的组件描述语言--UML
显然仅有构件和UML是不够的,因为我们还需要知道如何捕获构件!--答案就是最佳实践(RUP): 面向对象开发技术的今天—最佳实践 显然仅有构件和UML是不够的,因为我们还需要知道如何捕获构件!--答案就是最佳实践(RUP): 迭代开发 体系结构为中心 用例驱动
最佳实践1—迭代开发
最佳实践2—用例驱动
最佳实践3—体系结构为中心
内容 背景 面向方面的编程(AOP) 基于用例捕获和建模关注点 基于用例实现关注点分离 基于用例和方面建立体系结构 应用
基于构件的酒店管理系统
关注点分离 把问题分解到更小的部分,在计算机科学中称之为关注点分离 理想情况下,希望将不同的关注点清晰地分离到不同的模块中,并能够相互独立地、一次一个地进行探究和开发。然后,只需将这些软件模块组合在一起就能得到完整的系统 因此,关注点分离的概念和模块的概念是事情的两面,将关注点分解到模块中,而各个模块则解决或实现一些特定的关注点集合。
横切关注点 横切关注点: 横切关注点的种类 因而,可以说用例都是横切关注点 任何一个组件都无法完全满足的关注点 基础结构关注点:满足诸如实例化、日志、分布式及事务管理等非功能需求的横切关注点 还有一些横切关注点是与功能性需求相关的,经常会发现功能需求(在同一个用例中描述)的实现贯穿于多个组件中 因而,可以说用例都是横切关注点
横切关注点1—对等关注点 对等关注点 构件在保持对等关注点相互分离方面的局限性 相互独立的,没有任何一个对等关注点比另一个更重要 相互没有依赖关系,可以为每一个对等关注点构建一个独立的系统 但是当在相同的系统中实现多个对等关注点时,会发现它们之间有明显的交叠 构件在保持对等关注点相互分离方面的局限性 分散:某个特定对等关注点的实现分散在多个构件中 缠绕:各个构件包含满足不同关注点的实现
横切关注点1—对等关注点 各个构件包含满足不同关注点的实现—缠绕( tangling )状态 某个特定的关注点的实现分散在多个构件中—分散(scattering)状态
横切关注点2—扩展 扩展 问题 在基础构件(base)之上定义的构件,它用来表示附加的服务、功能或非功能属性 在添加新的功能之前,这些新添加的代码其实还并不需要 不可能预先标识出所有的扩展点
横切关注点现行实行的不足 横切关注点1—扩展 横切关注点2—对等功能点 代码的大量重复,涉及到多个功能性类 当横切关注点的需求发生变化时,会造成大量修改 横切关注点的更新难以实现一致性 横切关注点2—对等功能点 捕获对等功能点的用例本身就涉及了多个类,容易使分析与设计的结果不能够有效地对应起来,影响了跟踪 功能的修改,可能涉及多 个类,从而影响系统的弹性 原有的用例建模在管理 横切关注点时存在问题
需要的技术 关注点分离技术(Concern Separation Technique) 为了使关注点保持分离,必须对关注点进行建模和结构化 用例技术对于在需求和分析期间进行对关注点进行建模是十分有益的 要分离对等的用例是十分容易的(用例本身也就是这样定义的),但分离扩展用例就需要新的语言构造。在此基础上,还需要一种在设计和实现阶段保持分离的技术。 关注点合成机制(Concern Composition Mechanism) 关注点合成发生在编译时、编译后,甚至是运行时 合成普通的扩展相对简单些,因为所需要的只是一些监控基础组件(base)执行的自动化方法,当需要时转而执行扩展 合成对等用例而则要难得多,必须处理相互交叠的行为、冲突以及其它问题
面向方面技术 面向方面是使横切关注点更好地分离的一种技术,有时也称为高层关注点分离 面向方面技术的研究 AspectJ 荷兰Twente大学计算机系的TRESE组织提出的Composition filters(CF,合成过滤器) IBM研究院提出的Multidimensional separation of concerns (MDSOC,多维关注点分离) 马萨诸塞州波士顿市Northeastern大学(NEU)软件科学中心提出的Adaptive programming(AP,自适应编程) OOPSLA’1997大会上,来自Xerox Parc的Gregor Kiczales发表了面向方面编程 AspectJ
面向方面技术 本质:分离和合成横切关注点的方法 核心概念 可以实现将类之外的附加行为合并到类本身 可以在编译时或运行时进行 可以将不同的关注点的实现分离到不同模块中 核心概念 Intertype声明允许将新的特性(属性、操作以及关系)合成到已有的类中 Advices提供了一种在AOP中通过pointcut指定的扩展点上扩展现有操作的手段 Aspects(方面)只是一种构造块,用于组织intertype声明和advice。方面aspect可以是其它方面aspect的泛化(generalize)
基于方面技术的对等关注点分离 由不同关注点组成的类 简化的客户入住登记的简化版源程序 1. public aspect CheckInCustomer { 2. ... 3. public void Room.assignCustomer () 4. { 5. // 实现客户入住登记功能的代码 6. } 7. public void Reservation.consume() 8. { 9. // 实现去除删除预订记录的代码 10. } 11. public void Payment.createBill() 12. { 13. // 实现生成初始空白账单的代码 14. } 15. ... 16. }
基于方面技术的扩展分离 扩展--确保只有已认证的用户才允许预订房间;以及如果没有房间是空闲的,则将该用户放入等候列表。可以通过两个步聚来完成这个目标: 在行为需要扩展的原有操作中标识出扩展点 定义附加的行为,它们将在这些扩展点的位置上对行为进行扩展
Advice描述当执行到这一点时要运行的附加行为 简化的处理等候列表的简化版源代码 简化的处理用户验证的简化版源代码 1. aspect HandleWaitingList { 2. ... 3. pointcut makingReservation() : 4. execution(void ReserveRoomHandler.makeReservation()) ; 5. ... after throwing (NoRoomException e) : makingReservation() { 7. // 用于实现将客户放入等候列表的代码; 8. } 9. } 1. aspect HandleAuthorization { 2. pointcut performingTransaction (): 3. call(void ReserveRoomHandler.*(..)) 4. || call(void CheckInHandler.*(..)); 5. void around () : performingTransaction() { 6. if(isAuthorized()) { 7. proceed() ; 8. } 9. } 10. } advice 受限的模式匹配,一个pointcut定义多个执行点 Point标识出扩展点 Advice描述当执行到这一点时要运行的附加行为
内容 背景 面向方面的编程(AOP) 基于用例捕获和建模关注点 基于用例和方面建立体系结构
基于用例对横切关注点建模 用例是对横切关注点建模的最好工具 用例描述的关键是事件流 提供了在项目中及早确认涉众关注点的手段 对于关注点的建模,应该以有利于系统的早期确认和增量开发的方式进行,用例技术适用于此 用例就是系统执行的动作序列,为特定的用户产生了可观测到的有价值的值的序列 用例描述的关键是事件流
基于用例对横切关注点建模 用例扩展 在父用例中添加新的行为 与AOP中的aspect类似 有效地在需求阶段使用方面的概念 从用例建模到基于方面的设计和实现之间提供了转换 pointcut
基于用例对横切关注点建模 用例包含 不同的用例中有某些相似的步骤,将这些事件的公共事件流抽取出来,放在被包含用例中,其它用例可以引用被包含用例中的事件流
基于用例对横切关注点建模 用例泛化 Is-a-kind-of 当一组用例含有相同的序列或拥有相似的约束时,就可以将用例泛化
基于用例捕获横切关注点 应用(application)用例和基础结构(infrastructure)用例 理解涉众关注点 应用用例描述如何与系统交互以实现预期的功能 基础结构用例描述应用用例的每一步如何添加可靠性、可用性、性能、容错性等质量属性 理解涉众关注点 理解问题域 领域类:通过对核心业务场景的分析,标识出系统必须保存和维护的信息,将这些信息建模为领域类 领域模型:识别领域类的核心属性以及类之间的关系,为领域建模
基于用例捕获横切关注点 抽取系统特性(Feature List) 处理功能和非功能需求(应用用例、基础结构用例) 特性是系统预期目标的高层描述,功能性特性或者质量属性,得出系统关键特性列表 处理功能和非功能需求(应用用例、基础结构用例) 功能需求:用户如何使用系统,应用用例 非功能需求:系统的质量属性,基础结构用例
基于用例捕获横切关注点 捕获应用用例 捕获基础结构用例 从特性中识别出参与者,从参与者如何使用系统识别出用例 识别用例变量,并处理用例变量 客户类型、预定渠道等 基本事件流:不依赖于用例变量的取值 或者 使用默认值 处理扩展用例 扩展用例本身可以独立于被扩展用例进行建模,通过pointcut进行标识 捕获基础结构用例 抽象为<执行事务>用例对基本事务进行建模 基础结构用例的结构化、描述基础结构用例 处理系统范围的关注点
基于用例切片分离对等用例 使用用例对涉众点进行建模之后,在设计和实现过程中继续使得关注点保持分离 对等用例 协作 从用例建模的角度,对等用例之间没有任何关系 从用例实现的角度,对等用例可能影响同样的类 例如,Reserve Room和Check in Custom 协作 用例的实现通常建模为协作 用例从系统的外部视角来描述参与者如何与系统进行交互 协作对系统的内部机理进行建模
用例切片 为了使模型中的用例在实现时仍然保持模块化,需要引入新的模块化单元,包含了特定于某个用例实现的元素,称为用例切片 用例切片包括 描述用例实现的协作 (交互图、类图) 特定于该用例实现的类 特定于该用例实现的扩展
基于用例切片实现分离 合并特定于某用例的类 : 合并用例特定的类扩展:
基于用例切片组成系统 通过用例切片(use case slice)和非特定用例切片(non-uc-specific slice)来叠加出系统:
用例模块 对于某个用例而言,都包括不同的模型(用例描述、分析、设计、实现、测试设计、测试实现),每个模型都可以组成一个切片,而将这些切片放在一个单独的包中,就称为用例模块
内容 背景 面向方面的编程(AOP) 基于用例捕获和建模关注点 基于用例和方面建立体系结构
体系结构(Architecture) 什么是体系结构 什么是好的体系结构 系统元素如何组织? 系统如何实现所需功能? 系统如何满足预期性能、可靠性和其它质量特性 系统需要什么技术? 系统内部组织的结构是否能够弹性响应功能、技术、平台变化? 标准是否能够确保系统开发始终保持一致?采用什么设计模式? 什么是好的体系结构 分离功能需求 从功能需求中分离出非功能需求 分离平台特性 将测试从已测单元中分离出来
平台无关的结构(PIM) 类保证系统中的单元相互独立,用例保证单元中的任务相互独立,形成正交的结构 元素结构:基于包和类的分层结构 组成部分主要是领域相关的类 通常包括“应用层”和“领域层” 应用层的元素主要实现支持系统的主要参与者的工作流 领域层则要包括了描述重要领域概念的元素,在用例实现中被共享或者复用 用例结构:在元素结构的基础上添加了实现功能的方法
叠加平台相关特性(PSM) 选择平台 最小化用例设计(minimal use-case design) 系统的平台特性基于架构师选定的部署结构和进程结构 我们需要将平台特性与平台无关的部分分离—最小化用例设计 最小化用例设计(minimal use-case design) 可执行,并采用默认编程语言实现 是通过程序接口来激活的 分布性、内部进程的通信和平台相关消息通信都与其分离 所需的信息都在内存中
使功能需求保持分离 1 分离功能需求的最好办法是用例(应用用例) 应用用例的分析 使功能需求在分析模型中保持独立 什么是特定于某用例的、什么是独立于用例的,什么是特定于某应用的、什么是应用通用的 ,什么是领域通用的、什么是领域特定的 这些问题的答案用于指导:如何将类归入到层、包,如何将类扩展归入到相应的切片、模型中 应用用例的分析 考虑用例规约,必要时进行更新 分析用例:识别出参与用例实现的分析类,并将行为分配给这些类 将类组织成为层和包,在用例结构中将类和类扩展组织成切片 将分析元素映射为设计元素、识别出接口及附加的设计元素 使功能需求在分析模型中保持独立 根据类参与实现的用例,形成合适的元素结构 将用例结构分为:针对某个特定用例行为的“用例切片”,不属于特定的某个用例的“非用例特定切片”
使功能需求保持分离 2 应用用例设计 使功能需求在设计模型中保持独立 识别设计元素:从分析模型中标识设计元素,根据实现需要引入新的设计元素 识别构件和接口:将分析类中的一部分演化为设计构件 完善设计元素 使功能需求在设计模型中保持独立 使类扩展保持分离:ReserveRoom类需要updateAvailability()和retrieveDetails()两个方法。前者是“预订房间”特用,后者是公用。其处理方法有四种:
使功能需求保持分离 3 使功能需求在设计模型中保持独立 使操作扩展保持独立 1)总和:两个用例切片需要从相同操作得到不同的输出,并且操作的职责是提供两种输出的合并 2)选择:当用例切片需要从同一个操作得到不同的输出,并且要求组合结果只有一种输出 操作扩展声明包括 1)结构上下文:说明哪个操作是要增加的 2)行为上下文:说明扩展何时执行 3)说明附加行为
使扩展的功能需求保持分离 1 面向方面技术能够使我们更好地在原有系统上进行扩展 识别类: 该扩展流程应该在pointcut UpdatingRoomAvailability返回没有空闲房时 系统建立一个针对选定房间类型的带唯一标识符的待处理订单,然后将其放到等候列表中,再将此唯一标识符返回给客户,用例结束。 识别类:
使扩展的功能需求保持分离 2 识别Pointcut,并将用例行为分配给类 标识结构上下文:确定在何处运行这些操作扩展来调用扩展用例 标识行为上下文:定义职责中的何处需要调用
使扩展的功能需求保持分离 3 因此,得到了一个独立的用例切片完成该扩展用例
使非功能需求保持分离 1 分离非功能需求最有效的工具是基础结构应用 识别类 标识Pointcut 将用例行为分配给类
使非功能需求保持分离 2 使基础结构用例保持分离 细化元素结构 在用例切片中使基础结构保持分离
使平台特性保持分离 最小设计视角 合并了平台特性
Theme-面向方面的分析与设计方法 什么是Theme Theme之间的关系 开发者所能想到的每项功能、aspect或者关注点,都被视为系统中需要单独考虑的一个theme Theme之间的关系 概念共享 横切
Theme-面向方面的分析与设计方法 用Theme/Doc分析需求 用Theme/UML设计theme 确定某个特定Theme的功能是否应该是用aspect建模 用Theme/UML设计theme Theme/UML允许分别设计theme的UML模型
Theme-面向方面的分析与设计方法 Theme方法可以分别设计系统的每个特性,并提供了一种方法,将这些特性组合起来 需求层次:比较抽象的参与者和活动,以一组需求的形式来描述 设计层次:由类、方法及其相互关系构成
保持关注点分离带来的生产率提高 假设某系统有N个独立的需求,假设每个的工作量需X,则总工作量就是NX 若N个需求未良好分离,就意味着每个需求的实现与其它需求的实现相互交迭,最坏时可能是每个需求的实现与N-1个需求相关,如果交迭部分的开发量是Y,则任务量就成了NX+N(N-1)Y 假设N=20,X=20,Y=1,良好分离时工作量为400,未良好分离时显然就是780,接近一倍。 随着Y值的增加,未良好分离的 体系结构就会带来越大的成本。
从何处入手 用例建模与分析:对涉众关注点捕获的正确性和精确性是十分重要的。用例建模与分析适用于任何软件开发,不管是面向方面、面向对象还是其它的。建议针对基础结构服务编写用例。建议多练习,编写出客户和开发团队都易于理解的有效用例。 设计和实现:用例切片、用例结构、AOP都比较新,需要学习如何对aspect和用例切片进行建模,如何描述Pointcut。建议选择一个适用该方法解决的特定关注点。通过用例来描述它,并从此驱动到编码、测试的全过程。 测试:基于用例切片,可以很轻松地 将测试引入的控制和工具代码轻松地 去除。
在项目的不同阶段采用 计划阶段:很容易着手,从需求开始,从头贯穿AOSD; 细化阶段前期:已经完成了一部分迭代,有一些可以工作的用例和几个基础结构服务,则可以在没有开发的部分上应用用例切片,对已开发的基础结构服务可以考虑重新设计用例切片是否有价值; 细化阶段后期:如果已有一个可靠的架构,基本上不建议做修订和改变。但可以考虑用参数化pointcut来帮助你把基础结构服务和平台特性融合到系统中。还可以使用AspectJ来判断层和包之间是否存在依赖注入。 构建阶段:如果项目接近尾声,不必有太大改变,可以考虑会该方法进行测试。 完善阶段:可以用来对原系统更好的进行扩展。
内容 背景 面向方面的编程(AOP) 基于用例捕获和建模关注点 基于用例和方面建立体系结构 Theme-面向方面的分析和设计方法
分析、设计及合成时的Theme
Theme过程概述 分析 分析需求以识别Theme 将需求映射到系统中的关注点 Theme/Doc可以让读者了解行为之间的关系,暴露缠结的关注点 设计 使用Theme/UML设计theme 用Theme/Doc发现的theme来识别潜在的类和方法 填充设计细节并改进 可能有其它aspect theme出现 合成 指定如何重新组合Theme/UML模型 Theme/Doc有利于发现关系:是否共享或者横切
Theme过程
识别theme 表达式求值系统的需求 R1 计算功能,确定表达式的计算结果 R2 显示功能,以文本形式显示出来 R5 Expression定义为variableexpression/ numberexpression/ plusoperator/minuoperator/unaryplusop/unaryminusop之一 R6 Plusoperator定义为一个expression和一个加号和一个expression R7 minuoperator定义为一个expression和一个减号和一个expression R8 unaryplusop定义为一个加号和一个expression R9 unaryminusop定义为一个减号和一个expression R10 variableexpression定义为一个字母和一个expression R11 numberexpression定义为一个数字和一个expression
识别Theme 主要活动 6个可能的theme 9个实体 选择一个初始集合,包括潜在的theme 从需求中筛选关键的实体 找出关键的关注点 例如,特性的名称、服务或系统中的用例 6个可能的theme Evaluation/display/determine/check-syntax/log/define 9个实体 Expression/variableexpression/numberexpression/plusoperator/minuoperator/unaryplusop/unaryplusop/unaryminusop/plus/minus
细化theme集合 Theme/Doc中对theme和需求的操作
识别横切的theme 识别aspect的规则 分裂无效(通过重写无法隔离theme并消除共享) Base触发aspect(aspect行为是否base行为内部触发的)
设计theme Theme/UML的主要意图 Theme/UML规定的活动 定义每个关注点模型之间的关系,并验证所做的决策 Theme/UML规定的活动 分别设计用Theme/Doc识别的Theme 指定各个Theme之间的关系 合成Theme,进行验证
设计过程
Define theme设计
Check-syntax theme设计
Evaluate theme设计
log theme设计
合成theme 指定theme之间的关系
合成共享概念后的theme
合成横切行为后的theme
演化 需要增加的需求 R12:check-def-use功能,确保所有使用的变量都定义过,并且都被使用 R13:check-style功能,确保表达式符合本地命名惯例 R14:mix-and-match选中功能,确保用户在使用检查工具时,可以从语法检查、 check-def-use和check-style中选择一种来运行 R15: check-def-use和check-style操作应该记入日志
选择theme
审查共享需求 演化后的横切关系图
设计并合成theme 新的theme的合成
设计并合成theme 合成后的mix-and-match
谢谢大家! Questions or comments?