Ivar Jacobson 在其著作"Aspect-Oriented Software Development with Use Cases"中将AOP(Aspect-Oriented Programming)上升为一种贯穿软件开发全生命周期的设计技术,由建筑学的隐喻引出了decoration overlay(existion + extensions)的策略。在建筑学中,首先基础设计图规划了房间的空间划分,电气工程师基于这张基础图决定电气设施排布图,而装修设计师可能依据基 础设计图决定装修方案。所有这些扩展(extension)设计图叠加到基础设计图之上构成的最终设计方案决定了房间每个部分的最终功能。这也有些象是中 国传统彩画的印制技术:每次印刷一种颜色,多重叠加之后构成成品。
Jacobson指出传统的面向对象设计将会产生两个问题: tangling和scattering。tangling指的是一个组件要参与多个业务, 因而组件具有的功能是所有业务所需功能的超集, 虽然很多功能只在某一项特殊业务中使用,并无重用需求, 但组件开发者在设计和开发时却无法局域化, 必须同时理解所有需求。 scattering指的是某一特定需求可能需要多个组件协同操作来完成, 这要求实现代码分散在多个组件之中,而没有一个整体概念。这两个问题的产生是因为缺乏一种全局的separation of concern。AOSD(Aspect-Oriented Software Development)正是Jacobson指出的解决方案。
AOP 在基本的技术含义上可以认为是实现了基本动作与修饰语之间的正交分解,Jacobson将其扩大化为基础架构(architecture)与扩展 (extension)之间的正交分解,将architecture centric设计与use case, AOP等概念联系起来。 Jacobson指出程序中在两个不同的层面上存在着独立的正交分解结构,一个是组件结构(具体化为Class),一个是业务逻辑结构(具体化为use case)。The element structure identifies where elements of the system are located in the element namespace. It structures the elements hierarchically in terms of layers, packages, classes, and the features within these classes. The use case structure defines how you overlay functionality onto the element structure. 因为use case需要多个组件的参与,当我们将use case结构叠加到组件结构之上时就会产生tangling和scattering。Jacobson指出每一个use case是一个功能切片(slice), 并为use case中的概念找到了相应的程序对应物,因而可以在后期通过AOP技术将use case slice织入到系统基础架构中,从而构成一种全程建模技术。use case中的generalization可以对应于对象继承, 包含(include)可以对应于对象组合和调用, 而扩展(entension)就对应于AOP。某些use case具有共性,如常见的CURD(Create,Update,Read, Delete)操作,可以被抽象为参数化的use case, 这可以对应于模板(template)概念。
我在数年前也注意到了tangling现象,一些类的功能不断膨胀而完备性却始终无法保证。在概念 上我们可以将功能集正交分解为不同的接口,但实现时却需要集中实现,因为它们对应于同一个对象实体。当时我主要通过interface的 aggregation来解决这个问题,例如采用IDynamicInterfaceSupport接口,动态注入接口实现.
interface IDynamicInterfaceSupport{
Object queryInterface(Class interfaceClass, Object user);
void addInterface(Class inf, Object impl);
}
使用接口来处理这些问题很多时候效果并不好。首先接口要求预先有一个明确的定义,要求明确标注出功能的独特性,这样在合成的时候具有一定的困难。例如在C ++中我们将对象如果分解为大量的接口,则使用的时候需要不断的queryInterface,十分麻烦。只有弱化种类型区别,才能消除这种合成障碍。例 如在脚本语言中,我们可以自由的使用对象的所有函数,而不需要知道它是哪个接口具体提供的。我在C++中主要通过代码生成技术构造一个合成代理对象来解决 这个问题,在java中主要使用interface的extends。接口意味着某个明确的定义,而模板(template)在这方面的要求则弱得多,模 板只要求对象具有需要的函数,有时可以做到更好的可重用性。接口的另一个困难在于无法捕获cross-cutting concern。在没有AOP的代码注入技术之前我们主要是通过继承来构造扩展点,这些都是所谓侵入性(intrusive)的设计,要求不必要的附加层 次,而且具有很强的事前假定。
在jsplet中WebObject的注册格式可以认为是实现了action slice。WebObject具有的全部功能可以通过多个action集合组装出来,而这多个action slice共享一个对象名和成员变量空间。
<type name="Demo">
<!-- 如果未指定object, 则缺省为WebObject类型 -->
<object class="demo.MyWebObject" />
<listener>
<url-listener url="/demo/ActionSliceA.jsp" />
<url-listener url="/demo/ActionSliceB.jsp" />
</listener>
</type>
在tpl模板中,因为xml格式定义了明确的结构信息,所以我们比较容易的定义扩展点,并注入新的功能,例如<c:decorator>标签。这也类似于AOP的操作。
Jacobson指出传统的面向对象设计将会产生两个问题: tangling和scattering。tangling指的是一个组件要参与多个业务, 因而组件具有的功能是所有业务所需功能的超集, 虽然很多功能只在某一项特殊业务中使用,并无重用需求, 但组件开发者在设计和开发时却无法局域化, 必须同时理解所有需求。 scattering指的是某一特定需求可能需要多个组件协同操作来完成, 这要求实现代码分散在多个组件之中,而没有一个整体概念。这两个问题的产生是因为缺乏一种全局的separation of concern。AOSD(Aspect-Oriented Software Development)正是Jacobson指出的解决方案。
AOP 在基本的技术含义上可以认为是实现了基本动作与修饰语之间的正交分解,Jacobson将其扩大化为基础架构(architecture)与扩展 (extension)之间的正交分解,将architecture centric设计与use case, AOP等概念联系起来。 Jacobson指出程序中在两个不同的层面上存在着独立的正交分解结构,一个是组件结构(具体化为Class),一个是业务逻辑结构(具体化为use case)。The element structure identifies where elements of the system are located in the element namespace. It structures the elements hierarchically in terms of layers, packages, classes, and the features within these classes. The use case structure defines how you overlay functionality onto the element structure. 因为use case需要多个组件的参与,当我们将use case结构叠加到组件结构之上时就会产生tangling和scattering。Jacobson指出每一个use case是一个功能切片(slice), 并为use case中的概念找到了相应的程序对应物,因而可以在后期通过AOP技术将use case slice织入到系统基础架构中,从而构成一种全程建模技术。use case中的generalization可以对应于对象继承, 包含(include)可以对应于对象组合和调用, 而扩展(entension)就对应于AOP。某些use case具有共性,如常见的CURD(Create,Update,Read, Delete)操作,可以被抽象为参数化的use case, 这可以对应于模板(template)概念。
我在数年前也注意到了tangling现象,一些类的功能不断膨胀而完备性却始终无法保证。在概念 上我们可以将功能集正交分解为不同的接口,但实现时却需要集中实现,因为它们对应于同一个对象实体。当时我主要通过interface的 aggregation来解决这个问题,例如采用IDynamicInterfaceSupport接口,动态注入接口实现.
interface IDynamicInterfaceSupport{
Object queryInterface(Class interfaceClass, Object user);
void addInterface(Class inf, Object impl);
}
使用接口来处理这些问题很多时候效果并不好。首先接口要求预先有一个明确的定义,要求明确标注出功能的独特性,这样在合成的时候具有一定的困难。例如在C ++中我们将对象如果分解为大量的接口,则使用的时候需要不断的queryInterface,十分麻烦。只有弱化种类型区别,才能消除这种合成障碍。例 如在脚本语言中,我们可以自由的使用对象的所有函数,而不需要知道它是哪个接口具体提供的。我在C++中主要通过代码生成技术构造一个合成代理对象来解决 这个问题,在java中主要使用interface的extends。接口意味着某个明确的定义,而模板(template)在这方面的要求则弱得多,模 板只要求对象具有需要的函数,有时可以做到更好的可重用性。接口的另一个困难在于无法捕获cross-cutting concern。在没有AOP的代码注入技术之前我们主要是通过继承来构造扩展点,这些都是所谓侵入性(intrusive)的设计,要求不必要的附加层 次,而且具有很强的事前假定。
在jsplet中WebObject的注册格式可以认为是实现了action slice。WebObject具有的全部功能可以通过多个action集合组装出来,而这多个action slice共享一个对象名和成员变量空间。
<type name="Demo">
<!-- 如果未指定object, 则缺省为WebObject类型 -->
<object class="demo.MyWebObject" />
<listener>
<url-listener url="/demo/ActionSliceA.jsp" />
<url-listener url="/demo/ActionSliceB.jsp" />
</listener>
</type>
在tpl模板中,因为xml格式定义了明确的结构信息,所以我们比较容易的定义扩展点,并注入新的功能,例如<c:decorator>标签。这也类似于AOP的操作。