AspectJ重构:替换类型间字段与方法的实用指南
立即解锁
发布时间: 2025-08-20 00:01:06 阅读量: 1 订阅数: 5 


面向方面软件开发交易I
### AspectJ 重构:替换类型间字段与方法的实用指南
在软件开发中,代码的灵活性和可维护性至关重要。AspectJ 作为一种强大的面向切面编程(AOP)扩展,为我们提供了丰富的工具来处理横切关注点。本文将深入探讨 AspectJ 中的两种重要重构方法:替换类型间字段为切面映射(Replace Intertype Field with Aspect Map)和替换类型间方法为切面方法(Replace Intertype Method with Aspect Method)。
#### 1. “declare warning” 与 “declare error” 的转换
当所有警告都消除后,应将 “declare warning” 更改为 “declare error”。例如,在保护类型间方法不被继承类和切面继承链之外访问时,若要允许宿主类成员除了在切面及其后代中访问,只需在 “declare error” 中添加一个 “within” 子句:
```java
declare error:
call(void processSensitiveData())
&& !within(Participant+)
&& !within(GeneralPolicy+):
"Call to processSensitiveData() outside Participant and General Policy chains.";
```
#### 2. 替换类型间字段为切面映射
##### 2.1 典型场景
当一个切面静态地为一组类引入额外状态,而我们期望状态与目标之间有更动态、灵活的关联时,就需要进行此重构。
##### 2.2 推荐操作
将类型间声明替换为切面拥有的结构,该结构用于实现目标对象与额外状态之间的映射。
##### 2.3 动机
类型间声明是一种静态机制,它会影响目标类的所有实例的整个生命周期。对于某些问题,这种方式是合适的,但对于其他情况,更灵活的方式可能更可取。例如,一个类的部分实例可能只在特定生命周期阶段需要额外状态和行为,或者同一个实例可能同时需要多个额外状态和行为的实例。此外,应用程序可能在运行时才知道哪些实例需要额外状态和行为。类型间声明在这些情况下无法提供必要的灵活性。
另外,当多个切面在相似数据上执行相似操作,且包含类型间声明时,会出现代码重复。将公共部分提取到超切面可以消除重复,但会带来新问题:目标对象为每个子切面都有额外状态的独立实例,而将代码提取到超切面后,所有子切面将共享一个引入状态的实例。这种提取通常需要先将类型间状态替换为切面状态。
##### 2.4 前提条件
确保各个切面中的字段确实提供了等效的接口和功能。
##### 2.5 操作步骤
1. 对引入的字段使用封装字段(encapsulate field)方法。与传统的访问器方法不同,创建接收目标对象作为参数的切面方法。
2. 向切面添加一个能够支持等效映射功能的映射结构。添加与上一步创建的访问器类似的访问器,从映射结构中检索引入的字段。理想情况下,这些基于映射的访问器应具有与上一步创建的访问器相同的签名和名称。添加可能需要的任何额外管理方法(如插入、删除等)。
3. 如果切面有使用类型间字段的类型间方法,使用替换类型间方法为切面方法(replace intertype method with aspect method)来创建基于新映射结构的切面版本的这些方法。
4. 编译并测试。
5. 将第一步创建的访问器的每个调用替换为基于映射的访问器。完成所有替换后编译并测试。
6. 删除第一步创建的访问器方法。编译并测试。
7. 删除类型间字段和相关代码。编译并测试。
##### 2.6 示例:使用切面映射替换类型间字段
以下示例展示了一个实现中介者模式的切面片段。在这个示例中,有一个中介者对象(类型为 Mediator)作为各个同事之间通信的中心。同事包括 ClearButton、MoveButton 和 KidList。该切面通过标记接口间接为每个同事引入对中介者的引用。
```java
public aspect Mediating ...
private interface Colleague {}
private Mediator Colleague.mediator;
declare parents:
(ClearButton || MoveButton || KidList) implements Colleague;
pointcut clearButtonExecute(ClearButton clearButton): ...
after(ClearButton clearButton):clearButtonExecute(clearButton){
clearButton.mediator.clear();
}
pointcut moveButtonExecute(MoveButton moveButton): ...
after(MoveButton moveButton): moveButtonExecute(moveButton) {
moveButton.mediator.move();
}
pointcut kidListChanged(KidList kidList): ...
after(KidList kidList) returning: kidListChanged(kidList) {
kidList.mediator.select();
}
```
这个实现不灵活,因为它为所有参与者类的实例引入了额外状态和行为,而不管它们是否需要。通过将其替换为基于映射的实现,我们可以消除这种不灵活性。
首先,执行类似封装字段的重构,为类型间字段创建一个临时访问器方法:
```java
public aspect Mediating ...
private Mediator getMediator0(Colleague colleague) {
return colleague.mediator;
}
pointcut ...
after(ClearButton clearButton):clearButtonExecute(clearButton) {
getMediator0(clearButton).clear();
}
pointcut ...
after(MoveButton moveButton): moveButtonExecute(moveButton) {
getMediator0(moveButton).move();
}
pointcut ...
after(KidList kidList) returning: kidListChanged(kidList) {
getMediator0(kidList).select();
}
```
现在,所有对类型间字段的访问都通过这个临时访问器进行,中介者字段的类型间性质得到了有效封装。接下来,添加一个合适的数据结构(如哈希表)来映射目标对象到中介者字段:
```java
import java.util.WeakHashMap;
public aspect Mediating ...
WeakHashMap colleague2mediatorMap = new WeakHashMap();
private Mediator getMediator(Colleague colleague) {
return (Mediator)colleague2mediatorMap.get(colleague);
}
public void setMediator(Colleague colleague, Mediator mediator) {
colleague2mediatorMap.put(colleague, mediator);
}
private Mediator getMediator0(Colleague colleague) {
return colleague.mediator;
}
```
我们需要决定在何处调用基于映射的设置器。对象创建的位置可以作为参考,但在某些情况下,可能更适合在其他地方调用。在切面外部,调用最终设置器的方式如下:
```java
Mediating.aspectOf().setMediator(clearButton, mediator);
```
在切面内部的通知中,调用可以更简单:
```java
setMediator(clearButton, mediator);
```
插入对基于映射的设置器的调用,并使对临时访问器的调用引用基于映射的访问器。再次编译和测试后,我们可以删除原始声明和临时访问器。最终的切面代码如下:
```java
public aspect Mediating ...
private Mediator Colleague.mediator;
declare parents: (ClearButton || MoveButton || KidList)
implements Colleague;
WeakHashMap colleague2mediatorMap = new WeakHashMap();
private Mediator getMediator(Colleague colleague) {
return (Mediator)colleague2mediatorMap.get(colleague);
}
```
0
0
复制全文
相关推荐










