面向方面编程 / 面向方面软件设计






4.89/5 (47投票s)
关于 AOP/AOSD,您想知道的一切,却又不敢问
目录
第一部分
第二部分
面向对象技术在本地化涉及全局约束和普遍行为的关注点、恰当分离关注点以及应用领域特定知识方面存在困难。1
什么是面向方面编程?
面向方面编程 (AOP) 提供了拦截代码执行的能力,目的是在正常执行的代码之前、之中或之后插入一个过程。当您考虑到一切,包括调用方法、从方法返回、检索或赋值、处理异常、切换上下文等,都由代码执行组成时,您就能开始思考这种能力将如何被使用。如果这满足了您的好奇心,那么请勿继续阅读。然而,这个定义过于简单,以至于使用 AOP 的原因完全丢失了。请注意,这个定义仅针对一种特定的实现,而据作者看来,这种实现实际上与面向方面软件设计(或开发,尽管缩写相同,术语似乎可以互换)- AOSD 没有关系。此外,AOSD 本身是后面向对象编程 (POP) 技术的一个子集。不幸的是,深入研究 AOP 和 AOSD 会让我们迅速陷入晦涩的术语和自定义语言及扩展的泥潭。本文旨在提供对 AOP 以及更广泛的 AOSD 问题的简洁理解。像许多旨在解决复杂软件开发中出现的某些问题的最新范例一样,AOSD 在实际实现的喧嚣中包含着智慧的闪光点。
开发更简单的系统所取得的成功会引发对更大复杂性的追求1
为什么是面向方面软件设计?
AOSD 的首要原因是帮助解决混乱的面向对象架构问题。AOSD 的支持者声称,面向对象编程在处理全局信息方面存在困难。此外,需要涉及多个不同对象(可能被集合到组件中)的功能会导致这些对象/组件之间的相互依赖。这使得应用程序容易受到依赖对象/组件实现更改的影响。维护和增强也是问题,因为这些对象/组件之间的交互(无论交互规则是正式编码还是非正式编码)通常硬编码在包含对象中。如果这本身就令人困惑,请考虑在这些陈述中,AOSD 的术语被特意避免用来解释 AOSD 的原因。这是有意的,目的是避免本末倒置。在没有词汇表的情况下,立即沉浸在 AOSD 的术语中会使理解 AOSD 变得非常困难。
当应用程序需要执行某项操作而涉及许多不同对象时,就会出现混乱的面向对象架构。给定一个需求规格,面向对象设计 (OOD) 通常通过确定支持需求所必需的对象来进行。目标是根据需求分析对功能进行模块化,直到发生足够的抽象(以及 OOD 的其他目标)。在此过程中,设计者经常忽略一个事实,即最终这些对象将需要相互交互才能通过实际功能满足需求。这种情况很容易且不知不觉地发生,因为需求交互的方式通常与对象交互的方式根本不同。“设计模式”中描述的概念是尝试正式理解对象交互问题,并为设计者提供一些良好理解和支持的手段来解决这种对象间依赖问题。事实上,AOP 语言的主要实现,即 AspectJ,严重依赖于观察者设计模式。然而,AOSD 支持者提出的一项批评是,除了设计模式太多(并且还在不断增长)之外,这些模式通常在设计过程的早期就“设计进去”了。结果是,在需要不同的设计模式时,必须重新设计,甚至更糟,重构实现。另一项批评是设计模式与系统结构之间的交互。设计模式不仅影响系统架构,而且本身也受到系统架构的影响。因此,设计模式随着在实现中的消失而失去其模块化。这通常会影响可重用性,因为设计模式(可重用组件)和对象模型(另一个可重用组件)会集成到一个更僵化、可重用性更低的组件中。
就个人而言,虽然我同意这种观察的真实性,但我不同意这表明设计模式的概念存在缺陷。相反,我认为这表明需求和分析不完整,设计者/开发者的教育和培训不足,以及糟糕的项目领导(应该有人比所有人都聪明!)。如果您接受这种反驳,那么它也会削弱 AOSD 是糟糕的面向对象架构的“解药”的前提。另一个考虑因素是,这些范例都未能指示何时应该使用它们。许多有用的应用程序,包括基于 Web 的应用程序(其中页面刷新之间的复杂对象依赖关系很难维护),并未遇到 AOSD 试图解决的问题。AOSD(以及类似的范例)只有当应用程序跨越自动化简单任务(例如具有剪切、粘贴和打印功能的简单文本编辑器)和自动化由多个任务组成的复杂过程(例如提供嵌入式图形、绘图、拼写检查、布局和格式设置工具、协作、安全以及与其他应用程序集成的文字处理器)的边界时才开始变得有用。这个边界在哪里,何时切换设计范例,仍然是一门艺术。我的经验告诉我,模糊的需求、定义不清的用例以及无限的增强功能是表明我应该在项目开始时就考虑这些问题并进行相应设计的指标。
方面需求是导致实现横切的关注点1
术语
到目前为止,我一直避免使用 AOSD/AOP 随附的任何术语,而是试图坚持使用众所周知的面向对象术语。至少拥有这些术语的基本知识是必要的,即使只是为了在下次面试时能说出一些时髦词。这些术语的良好定义相当难以找到,部分原因是这些术语使用得不够精确(因此没有人愿意定义它们),AOSD 的概念不断发展(因此术语具有新的含义),并且学术界继续倾向于使用那些在没有周围环境的情况下意义微乎其微的术语。
关注点
在 AOSD 世界中,关注点有几个定义,但我最喜欢的是 IEEE 的定义:“……与系统开发、运行或对一个或多个利益相关者至关重要或其他重要方面相关的那些利益。”然而,这对于将定义带入现实世界背景并没有太大帮助,但它确实概括了应用程序开发阶段,粗略地枚举为
- 需求收集
- 设计
- 原型制作
- 实现
- 测试
- 交付
- 维护
这些阶段中的每一个都对一个或多个相关方或利益相关者(举几个例子)感兴趣
- 客户
- 管理
- 开发团队
- 数据库组
- 库/组件开发人员
- QA 团队
- 市场营销
- 生产
- 文档/帮助编写
- 技术支持
- 应用程序本身
请注意最后一个条目,“应用程序本身”。认识到应用程序本身是一个利益相关者很重要,它关注诸如
- 安全
- 性能
- 优化
- 准确性
- 数据表示
- 数据流
- 可移植性
- 可追溯性
- 分析
尽管最后一组关注点由不同的人“持有”,但应用程序本身在这些问题上有利益,因为它们直接影响实现和重构问题。无论前端设计工作如何,最后一种情况中的许多问题直到大部分组件被粘合在一起并且产品接近完成时才会被揭示。因此,有“90%的工作在最后 10% 的开发中完成”的说法。如果 AOSD 对软件开发有任何价值,那就是它将这些关注点带入了意识,并让参与软件生产的整个实体从不同的角度考虑这些关注点。
视角
这个术语在 AOSD/AOP 圈子中不常见,但值得提及。视角是从不同位置看待整个系统并根据这些位置确定关注点的方式。软件供应商和客户对软件构建过程有不同的视角。在这两类(但这并不意味着只有两类)中,供应商有不同的视角(管理、工程师、技术支持等),客户也是如此(例如,管理、会计、数据录入员)。我经常从“办公室女士”的视角处理的一个关注点是双重的——“这个学习起来会很难吗?”和“谁来提供培训?”。讽刺的是,经理们似乎自己忘记了问这个问题,同时又认为自己不受培训需求的困扰,并且实际上在使用最终产品。
关注点分离
这是对应用程序开发中存在的关注点的分析。每个利益相关者对他们/它/它在应用程序中的参与都有不同的关注点。关注点分离是一项重要的分析,因为它确定了物理实现(参见构件)最终涉及多个利益相关者的领域,无论是部门/组织利益还是对象。从不同的视角看待关注点有助于关注点分离过程。需求通常不明确说明何时关注点涉及多个利益相关者。相反,需求文档通常根据特定部门的需求来陈述关注点。对象设计也是如此(因此上面的“它”),其中对象设计从预期的输入、结果输出和操作方法的角度规定了它的关注点,而不管整个系统。因此,分离不同的关注点(这意味着正确地识别它们)是以后确定应用程序中实际信息流(参见横切)的关键步骤。
关注点分离的目的是识别可以以分散方式设计和实现的领域。这使得不同的团队能够并行工作,并拥有独立自主的决策过程。即使是简单的应用程序也利用了这一概念。例如,考虑一个简单的网络消息应用程序。该应用程序依赖于网络协议完全独立且可靠地开发的事实,因此程序员无需“关心”这个问题。从不同的角度来看,大型应用程序(考虑运行航天飞机所需的软件)必须将其关注点分离成可管理的“块”,才能在合理的时间内完成工作。然而,这导致了 AOSD 试图解决的问题,即重新集成这些分离的关注点。归根结底(比喻意义上)所有不同的部分都必须以有意义和一致的方式重新组合。AOSD 是检测此过程中问题区域的工具。
构件
从 AOSD 的角度来看,关注点通常是概念性的,而“构件”是受关注点驱动的具体构造。这会引起关于何时某事物是关注点何时是构件的困惑。经验法则法则是,如果它是概念(例如,需求属性),那么它就是一个关注点。如果它是具体的构造(例如,对象的实现),那么它就是一个构件。术语“构件”在 AOSD 世界中不常用。无论如何,将关注点和构件视为独立的实体以便两者都可以独立考虑是有用的。这有助于设计者独立于其对实现(构件)的影响来思考关注点。作为一名顾问,我发现这项技术非常有价值,因为它可以帮助我识别部门之间的沟通问题、现有手动流程中的问题、部门/人员目标的冲突等。因此,我经常花费一半以上的时间记录和修复与编程完全无关的流程,而无需编写一行代码。或者,如果我太快地沉浸在对象(构件)的设计中,直到我过多地参与项目设计/实现,我永远不会注意到不同关注点之间的冲突和脱节。然后,我必须要么修复设计/实现,要么强制更改现有流程,或者两者兼而有之。最终结果是,我被视为软件延迟交付或工蜂之间很多痛苦的原因。通过分别看待关注点和构件,我可以在编写任何代码之前解决设计/实现问题。这种方法的附带好处是,通过解决其流程中的问题,公司可以立即开始省钱。他们以为他们需要一个程序来做这件事!
将关注点与构件分离的目的之一是帮助创建编程语言(参见下面的“形式化”),这些语言可以在不处理实现(构件)细节的情况下表示关注点。到目前为止,这非常有限。感兴趣的读者应参考 AspectJ(Java 扩展)、Cosmos(用于建模关注点的工具)和 CoCompose(一种设计方法)。
横切
横切是一个术语,用于描述应用程序与多个构件交互的实现。(看,我们现在可以开始使用术语了)。由于构件是不同关注点的物理实现,因此横切通常涉及一个试图解决多个关注点的操作(或工作流)。由于代表这些关注点的构件通常实现为独立的(因为我们都试图推广重用),我们最终会得到混乱的代码。通常将对象传递给其他对象(void ObjectD::DoSomething(ObjectA* a, ObjectB* b, ObjectC* c)
)或通过全局机制获取(AfxGetMainWnd()
)。这是 AOSD 的关键——对复杂面向对象实现包含相互依赖性,导致调试、测试、维护和扩展功能困难的批评。通过显式识别横切何时何地发生,AOSD 提供了一种识别全局相互依赖性的机制,从而实现了 OOD 的原则,即模块化和重用。(哎呀,我开始听起来像我在读的文章了)。
方面
最后,我们可以解释 AOSD 缩写中的第一个词的含义。方面不过是强制在实现中横切的事物。虽然横切是使用多个构件来执行其任务的过程的实现,但方面是这种情况发生的情况。AOSD 是识别导致构件横切的方面或情况的技术。这实际上意味着需求文档很少整体指定事物。相反,需求文档是不同组的需求集合,每个组都互不顾及。这种疏忽通常直到对象设计完成,并且出现将对象耦合在一起以执行特定过程(或工作流)的问题时才会被发现。我见过许多省略此步骤的设计。如果不省略,那么每个人都会拿出他们的“设计模式”书,识别适用的设计模式来帮助连接分散的对象,在对象图上绘制几个额外的对象,然后每个人都愉快地开始编码。如果我是 Aosd 先生坐在这些设计会议上,我会喊“停止!”,并耐心解释仍然需要一个元设计级别来防止设计模式和对象纠缠在一起。这还包括指出可能只有 10%(如果不是)迫使横切的方面被正确识别。而且,由于大多数语言不直接实现 AOSD,因此有必要实现一个能够妥善处理这些方面的框架,以便我们的设计模式和对象模块化/重用得以保持。(多么美妙地过渡到应用程序自动化层!)
不幸的是,在至少一篇关于关注点分离的文章中,作者将“方面”定义为组件的属性。这与 AOSD/AOP 更常遇到的定义相冲突。
织入
织入是用于描述用于解决方面问题的任何解决方案的奇怪术语。(哈哈,你以为我用不了这个词)。因为这涉及到物理实现(构件),织入解决方案几乎是任意的,只要最终的努力能够创建一个连贯的系统。“织入”这个术语可能是由于使用 AOP 语言扩展(如 AspectJ)所形成的心理图像产生的。回想一下我对 AOP 的简单定义——拦截代码执行的能力,目的是在正常执行的代码之前、之中或之后插入一个过程。AOSD 实现通过注入前、后和“环绕”过程来“织入”方面问题的解决方案!(换句话说,作者认为“织入”这个术语是荒谬的,并且本身就证明了他对学术界和时髦词的所有其他抱怨)。
组件 (Component)
之所以在此包含此术语的定义,不是因为它在 AOSD 中经常使用(实际上并不常用),而是因为本文使用了它。在 Microsoft 开发领域,“组件”这个术语变得模糊不清,因为它现在与非平凡的 GUI 控件相关。就本文而言,“组件”的定义为“……一个封装的、自主的服务提供者,它通过明确定义的接口向其所处的更广泛的上下文提供有用的服务,并与其他组件协作以实现共同目标。”5 考虑此定义的……“与其他组件协作”……暗示了其实现中的横切。嗯。
形式化
一种编程语言。(不,我没开玩笑)。
如果不对系统设计中的关注点给予适当的关注,将很难管理复杂性、可读性、系统的演变和组合2
一个案例研究
让我们以船舶修理厂运营管理为例(我的一个客户),看看这是如何工作的。
关注点
首先,关于零件处理的一般性陈述。请记住,当我开始着手自动化他们的运营时,这些都没有以任何连贯的方式书面记录。这是 20 年口头“诀窍”的结果,并在几周内通过口头传达给我。正式流程、图表等完全缺乏。结果是工作培训昂贵且犯了很多错误。我发现讽刺的是,一个人可以要求计算机系统完美,而它所替代的系统却如此不完美。
作为一家全方位服务的船舶修理厂,客户基本上不允许自己修理船只,除了内部的小型维修。这部分是由于环境法规,部分是因为它增加了利润。客户需要的任何零件都通过前台处理,通常以零售价出售,除非船厂经理另有规定。客户通过现金、信用卡、支票或通过船舶商店账户支付零件费用。然而,通常,零件用于客户船只上进行的所有维修和保养。当船厂经理编写一张由客户(通常是口头)授权的工单时,机械师就会得知需要对船只进行维修。拿到工单后,机械师开始工作,这可能需要零件。零件有库存、缺货或特殊订购。假设零件有库存,机械师就会去零件柜台,索要必需的零件。在结账时,用于各种工单的所有零件都会被统计、征税并开具给客户。偶尔,船厂经理会协商一个要给予客户的特殊价格。这通常发生在客户知道竞争对手销售的零件价格更低时。这几乎总是发生在特殊订购的零件上,船厂经理在签发工单时就知道这一点。船厂经理还会填写一份材料申请表,说明零件和价格。采购人员负责订购零件,并确保在机械师需要零件时零件能及时到货。最后,船厂经理通常会规定该客户的所有工作都将以折扣价开具账单,或者只有某些工单才有折扣。否则,零件将以零售价开具账单。采购部门有兴趣知道哪些零件用在了哪些工单上,以便更好地估算将来的库存采购,船厂经理也想知道,以防有什么可以实施的成本削减措施。船舶修理厂还设有一位会计,他非常想了解零件的购买和销售情况,因为船舶修理厂目前每月有 15,000 美元的库存报废——零件被购买但似乎消失了。当然,老板认为这是员工盗窃。由于这是一个完全手动化的过程,任何试图自动化这些流程的软件都必须确保库存被准确地借记,并且零件被正确地开具给客户。哦,还有一件事。请记住,船舶修理厂还拥有并运营几艘自己的船——用于接送人们往返船只的渡轮、包租船等。这些船也需要维修和保养,并且零件需要正确地开具给船舶修理厂自己的账户以用于税务目的。与船舶修理厂工作相关的所有零件都“按成本”出售给船舶修理厂。
在此案例研究的范围内,让我们看看如何根据“关注点”重写此陈述。
船厂经理
船厂经理关心
- 与工单相关的折扣
- 与客户相关的折扣
- 特殊订单的特殊定价
- 工单中使用的零件
- 效率
采购部门
采购部门关心
- 零件出售时(通过船舶商店或零件柜台)借记库存
- 采购特殊订购零件
- 补充库存
会计
会计关心
- 库存水平和价值
- 每月 15,000 美元的报废
- 零件正确开具给客户或船舶修理厂
- 税费
账单部门
账单部门(或应收账款)关心
- 按指定时间段内使用的所有零件向客户开具账单
老板
老板关心
- 员工盗窃
船舶修理厂的关注点
在用构件(组件、对象和其他结构化实现)考虑问题使问题复杂化之前,让我们只关注关注点本身,看看能学到什么。会计的关注点(每月 15,000 美元的报废)和老板的关注点(谁在偷我的东西?)实际上是关于一个可以应用于任何流程或关注点的共同主题的问题:验证、错误更正和异常处理。这揭示了一些系统性问题
- 船厂经理如何验证客户是否按照材料申请正确计费?
- 谁来验证库存是否从库存中正确借记?
- 谁来验证零件(特殊订购的或非特殊订购的)是否正确添加到工单中?
- 谁负责确定零售价并给予折扣?
- 当船厂经理忘记通知这位“魔法人物”应给予客户零件折扣时,会发生什么?
- 当零件成本高于船厂经理向客户报价的价格时,会发生什么?
- 谁来验证零件是否被正确地开给船舶修理厂自有的船只?
- 工单是否总是在开始修理船只之前创建,还是有例外?
这些只是从各种关注点中浮现出来的一些问题。这一点怎么强调都不为过,因此我将再次强调:所有关注点都需要从验证、错误更正和异常处理的视角来审视。这些不是编程问题。这些是流程问题:现有的业务流程如何处理这些情况?然而,它们将始终揭示现有流程中的重大问题,影响实现决策,并导致项目原始估算的时间和成本超支。通过使用 AOSD 识别关注点,然后应用这个基本规则,您可以在不设计任何组件或编写一行代码的情况下,发现关于系统的很多信息。
为了好玩,我将根据我刚到现场时船舶修理厂的运营方式来回答这些问题。
- 船厂经理如何验证客户是否按照材料申请正确计费?
通过查看每张账单。如果船厂经理记得特殊订单,那么就有可能发现错误。
- 谁来验证库存是否从库存中正确借记?
没有人。未执行诸如周期盘点之类的标准库存准确性机制。
- 谁来验证零件(特殊订购的或非特殊订购的)是否正确添加到工单中?
船厂经理对工作需要哪些零件有一个“感觉”。
- 谁负责确定零售价并给予折扣?
基本上没有人。基本上,采购部门根据制造商建议零售价 (MSRP) 设定价格,但零件通常没有 MSRP,所以这相当随意。
- 当船厂经理忘记通知这位“魔法人物”应给予客户零件折扣时,会发生什么?
会计部门必须在账单上进行手动更改。如果账单没有及时被发现,那么就会签发一张贷项通知单,下次显示在客户的账单上。
- 当零件成本高于船厂经理向客户报价的价格时,会发生什么?
什么都没有。公司承担损失。此外,损失没有传达给会计,因此从未作为损失显示。
- 谁来验证零件是否被正确地开给船舶修理厂自有的船只?
没有人。没有对开给船舶修理厂自有船只的零件进行核算。会计每月估算账单。
- 工单是否总是在开始修理船只之前创建,还是有例外。
否。在高峰时期,通常会口头发出工单。
外观可能具有欺骗性,但区别很明显4
学到的教训
在我开始考察船厂流程和自动化时,没有应用 AOSD 技术,我最终完全走错了方向,花了三个到四个月的时间,因为我不了解问题的深度。讽刺的是,我已经做过程自动化很多年了,我应该知道得更多。然而,外表可能会欺骗人。这家船舶修理厂从外面看起来运作得非常好(这正是他们希望客户看到的),但内部却一团糟。一位客户打电话抱怨说,他花了六个月前买的价值 6,000 美元的丁基船从未被收费(这种情况确实发生过),也许他对内部问题有了更深的理解。
构件(对象和组件)
现在,我将采取大胆的一步,确定一套基本的组件,而不是基于任何正式的需求文档,而是从关注点本身来确定。这让我们从抽象的关注点问题转移到更具体的、设计和实现的问题。通过一般陈述的语义和关注点可以识别出以下组件:
- 工单
- 材料申请
- 客户账户
- 库存
- 发票(账单)
- 零件
- 折扣
无论实现细节和本地化(例如,内存对象、XML 数据、数据库表等)如何,这些似乎都是合理的组件。根据需求,这些组件可以以所有这些形式存在,以及其他形式。
样板关注点
此时进行的一项有趣的步骤是考虑这些构件的关注点。构件的样板关注点包括诸如
- 对象/信息创建日期/时间
- 关闭日期/时间
- 活动、非活动或已删除
- 更新/更改历史
- 错误检查
- 异常处理
- 信息完整性
- 日志记录
请注意,这些关注点可以并且通常在组件内部处理——在大多数情况下,它们不需要与其他组件交互。例外情况是最后三项:异常处理、信息完整性(生成问题警报)和日志记录,它们可能涉及其他“辅助”组件。这些只是组件可能拥有的常见关注点中的一些,而与需求无关。在应用程序的复杂性增加和添加新功能时,将这些关注点考虑在内来设计组件会非常有帮助。除了这些样板关注点之外,还可以根据需求文档和产品开发过程确定特定于应用程序的关注点。
在实际提供给客户的系统中,只解决了一些这些关注点。我在此感到遗憾的是,诸如更改历史之类的关注点并未成为最初实现的一部分。然而,由于实现很好地处理了横切问题(使用了应用程序自动化层框架!),实现其他关注点非常简单,并且不会破坏现有系统。
横切
以一个过程为例,确定零件的价格,这说明了实现(在这种情况下是上述组件)的横切。即使在早期设计阶段,在不深入研究每个组件的实现细节的情况下,也可以证明,确定零件价格(如客户账单所示)涉及系统中的许多不同组件。下图使用“黑箱”的概念来计算价格。
不幸的是,AOSD 并没有真正提供如何处理这种横切的指导,除了帮助识别它的存在。 “织入”的概念(将在下面的 AspectJ 部分讨论)是提供形式化(语言实现)来解决横切引入的问题的尝试。
方面
在上面的例子中,价格计算是一个方面,因为它强制组件实现横切。我想正确的用法应该是“价格计算方面”。
像许多旨在解决复杂软件开发中出现的某些问题的最新范例一样,AOSD 在实际实现的喧嚣中包含着智慧的闪光点3
AspectJ
请读者注意,我不是 Java 程序员,如果我能决定,我希望永远都不是。因此,本节将相当简短。如果您想深入了解此语言扩展的完整语法,可以参考下面的参考文献。即便如此,这个语言扩展中有一些有趣(也许更合适的词是“奇怪”)的功能值得一读。为了演示实现,需要定义一些额外的术语,在这些定义中,您会找到示例用法和进一步的讨论。
连接点
连接点是程序执行中任何“定义良好”的点。从高级语言的角度来看,这将是调用方法、访问数据、存储值、对象创建和销毁(垃圾回收语言除外)、异常处理程序等位置。这基本上排除了算术和布尔运算(除非重载或处理 setter/getter 字段),并且从低级汇编语言的角度来看,是为了执行高级指令而必需的中间机器指令。
讨论
您可能会认为 C# 的“set”和“get”语言结构抽象了属性的连接点概念,或者 C# 和 C++ 的继承和重载特性抽象了方法和运算符的连接点概念。事实并非如此。连接点所涉及的问题实际上要求语言直接支持该概念作为“一流公民”,而不是使用现有的面向对象技术。这主要是由于连接点可以拦截的丰富方式以及在现有语言结构中模拟此概念所需的体力劳动(参见下面的“切入点”)。
切入点
切入点(或切入点指示符)是在程序中选择连接点的规范。给定一个连接点(您感兴趣的程序执行中的一个位置),切入点是您用于使用形式化语法向编译器或解释器传达您对该连接点的兴趣的语法和机制。不幸的是,您会看到类似“代码在达到切入点时运行”这样的句子,这至少可以说是令人困惑的。切入点在程序执行到达连接点时将程序执行向量化到“通知”(见下文)。这种术语的混淆使得理解 AspectJ 中提出的语言扩展,甚至接受它们变得非常困难。
用法
给定一个对象 Point,一个指定 Point 对象更改的拦截的切入点如下所示 [6]
pointcut fieldChanged(Point changed) : call( * Point.set*(..)) && target(changed);
讨论
此语言扩展的一个有趣(奇怪)的功能是通配符的使用。通过指定“call (*.*(..))”作为切入点,您可以创建对所有函数调用的切入点。通过指定“call (Point.*(..))”作为切入点,您可以为类中的所有方法指定切入点。另一个控制领域是指定调用源的能力。如果您有两个类 Line 和 Rectangle,这项功能允许您在 Line 操作 Point 时与 Rectangle 操作 Point 时区分并指定不同的切入点。这些可以嵌套到任何级别。还有额外的构造提供了进一步的复杂性,例如“within”、“withincode”、“cflow”和“cflowbelow”,我在这里不进行描述。
通知
通知是当切入点指定的连接点被命中时执行的代码。在通知中,您可以指定在拦截代码(或切入)之前、之后和/或替代(“环绕”)运行的代码。通知,或增强,通过此机制“织入”到现有实现中。
用法 [6]
after(Point changed ) : fieldChanged(changed) { changed.makeDirty(); }
讨论
源自汇编语言编程时代,这听起来不过是一个华而不实的“修补”。此外,切入点规范与架构实现、程序流和类实现紧密相关。当实现更改时,这会导致更多副作用,而不是更少。此外,这种实现使测试复杂化,因为它扩展了代码可以达到的路径数量以及决定代码执行的决策。虽然此语言扩展提供了一些有趣的思考点,但我实在无法相信它可以被认为是 AOSD 确定的横切问题的解决方案。AspectJ 的支持者会争辩说,它允许轻松添加日志记录和安全检查等功能。这些是简单的示例,可以通过重构等现有机制以及更出色的前期设计来更好地处理。使用此语言扩展,应用程序将迅速演变成切入点规范的拼凑,因为代码的连贯结构被拼凑的拦截织入其中,代码的可读性将降至零。此外,对于像上面案例研究中那样复杂的(敢说“真实世界”)示例,此语言扩展似乎不合适。
其本质上,AOP 是一种编程技术1
结论
显然,AOSD 并不是软件生产中问题的完美解决方案,但如果您还没有自己的解决方案,它是一个很好的起点。通过经验,您应该能够微调 AOSD 的概念,以便更好地适应您自己的工作环境、客户群、团队成员等。主流语言集对 AOSD 解决方案的语言支持很少。我个人认为语言扩展完全是错误的方向。如果有的话,它们会破坏 AOSD 的目的,因为至少在使用 AspectJ 时,组件之间的耦合似乎会变得更紧密。此外,对程序流的单独规范只会增加程序员的困惑,并需要跟踪相关的但独立的实现细节。相反,AOSD 提出的问题可以通过现有工具(如设计模式)和语言(如 C#、C++)通过设计适当的框架来管理横切问题,从而更好地解决。本文作者提出的一个此类解决方案(再次推介 AAL)通过消除不必要的对象构造、将数据视为独立的事件驱动实体,并提供一种通过脚本语言实现的工作流机制来粘合自治组件以实现所需过程。
参考文献
由于这不是一篇正式论文(而且因为我太懒),我在文章本身没有加入任何脚注来注明当我陈述某些观点时我引用了哪些其他作者。我仿佛能听到大学警察正在敲门。不管怎样,以下参考文献,其中大部分可以在互联网上找到(有些可以通过 ACM,一个优秀的在线编程技术来源),对于 AOP、AOSD 和 AspectJ 的信息来说,大多数都是优秀的来源。如果您亲自阅读它们,您就能弄清楚我正在转述谁以及从哪里引述。对于可能冒犯的作者,请注意,除非另有说明,我从未在本文中直接引用过您。我更喜欢转述和改写,以便普通程序员社区有机会理解您们实际上在谈论什么。
- 1Elrad, Filman, and Bader, October 2001/Vol. 44, No. 10 COMMUNICATIONS OF THE ACM
-
2AOSD, AOSD 2002--1st International Conference Aspect-Oriented Software Development, http://trese.cs.utwente.nl/aosd2002/
-
3Marc Clifton
-
4Peter Gabriel
- 5Stojanovic and Dahanayake. Components and Viewpoints as Integrated Separations of Concerns in System Designing
- 6SDSU, Aspect Oriented Programming, CS683: http://dev.eclipse.org/viewcvs/indextech.cgi/~checkout~/aspectj-home/doc/progguide/index.html
- Batenin and O'Neill. Towards Unanticipated Composition of Concerns in Hyperspaces
-
Chitchyan, Sommerville, and Rashid. An Analysis of Design Approaches for Crosscutting Concerns
-
Graversen and Beyer. Conceptual programming using roles
-
Sutton and Tarr. Aspect-Oriented Design Needs Concern Modeling
-
Wagelaar and Bergmans. Using A Concept-Based Approach To Aspect-Oriented Software Design
-
Akkawi, Bader, and Elrad. Dynamic Weaving for Building Reconfigurable Software Systems
- Tucker and Krishamurthi. Pointcuts and Advice in HigherOrder Languages
- Hannemann and Kiczales. Design Pattern Implementation in Java and AspectJ
- Clarke and Walker. Towards a Standard Design Language for AOSD