面向切面的程序设计

特性导向程式设计（Aspect-oriented programming，AOP，又译作面向方面程序设计、面向切面程序设计、剖面导向程序设计），是计算机科学中的一种编程范型，旨在将交叉切入关注与作为业务主体的核心关注进行分离，以提高程序代码的模块化程度。“方面”（aspect）通过规定叫做“定点切入”（pointcut）的一种量化或查询，在各种接合点（join point）上应用建议（advice），从而改变现有基础代码的行为（behavior）。比如定点切入与建议的一个实例：“对所有方法名以`set*`开头的方法添加后台日志”。该思想使得开发人员能够将与代码核心业务逻辑关系不那么密切的功能（如日志功能）添加至程序中，同时又不降低业务代码的可读性。

## 简介

面向方面编程将代码逻辑切分为不同的模块（即关注，一段特定的逻辑功能）。几乎所有的编程思想都涉及代码功能的分类，将各项关注（concern）封装成独立的抽象模块（如函数、过程、模块、类以及方法等），后者又可供进一步实现、封装和重写。部分关注“交叉切入”程序代码中的数个模块，即在多个模块中都有出现，它们即被称作交叉切入关注。

日志功能即是交叉切入关注的一个典型案例，因为日志功能往往跨越系统中的每个业务模块，即交叉切入（crosscut）所有有日志需求的类及方法体。而对于一个信用卡应用程序来说，存款、取款、帐单管理是它的核心关注，日志和持久化将成为交叉切入整个对象结构的交叉切入关注。

方面的概念源于对面向对象编程和计算反射的融合。面向方面编程语言拥有很多类似于元对象协议的功能，但有更多的限制。方面相关的编程概念包括面向主题编程、混入和委托 (计算)。使用面向方面范型的其他方式有复合过滤器和Hyper/J的hyperslices方式。

## 历史

“面向方面编程”这一术语由施乐帕洛阿尔托研究中心的Chris Maeda首先提出，但其具体时间已经不可考证了。术语“交叉切入”（crosscut）是由Gregor Kiczales提出的。同许多重大的技术创新一样，面向方面编程，也是在不同的地方被独立发展出来。面向方面编程的早期工作，主要是由下面几个机构和人员作出的：

- 施乐帕洛阿尔托研究中心：Gregor Kiczales、John Lamping、Cristina Videira Lopes等人，进行的早期工作有关于反射机制和元对象协议，在1997年Gregor Kiczales发表了论文《面向方面编程》；代表系统是基于元对象协议的面向方面编程系统和AspectJ。
- 国际商用机器公司托马斯·J·沃森研究中心：William Harrison、Harold Ossher、Peri Tarr等人，在1980年代末进行的早期工作，有关于软件开发环境与工具集成；后来提出了多维关注分离（MDSOC），代表系统是Hyper/J。
- 美国东北大学：Karl Lieberherr等人，进行的早期工作是研究软件演化，提出了得墨忒耳定律、传播模式、适应性编程；代表系统是Demeter/C++和Demeter/Java。
- 荷兰特文特大学：Mehmet Aksit等人，其代表系统是复合过滤器。

## 基本概念

关注是对软件工程有意义的小的、可管理的、可描述的软件组成部分，一项关注通常只同一个特定概念或目标相关联。传统的编程语言，以一种线性的文本来描述软件，只采用一种方式比如类，将软件分解成模块；这导致某些关注比较好的被捕捉，容易进一步组合、扩展；但还有一些关注没有被捕捉，弥散在整个软件内部。

关注分离（SOC）是标识、封装和操纵只与特定概念、目标相关联的软件组成部分的能力，即标识、封装和操纵关注的能力。分离关注使得解决特定领域问题的代码从业务逻辑中独立出来，业务逻辑的代码中不再含有针对特定领域问题代码的调用，业务逻辑同特定领域问题的关系通方面来封装、维护，这样原本分散在在整个应用程序中的变动就可以很好的管理起来。

核心关注是一个软件最主要的关注。在传统的编程语言中，将软件分解成模块的主要方式，是支配性分解，即按主关注进行模块分解。用来描述、设计、实现一项给定关注的软件构造单位是方法。如果两个关注的实现的方法存在交集，则称谓这两个关注相互交叉切入（crosscut）。

面向方面编程的核心概念，是从核心关注中分离出交叉切入关注。面向方面编程，在支配性分解的基础上，提供叫做方面（aspect）的一种辅助的模块化机制，这种新的模块化机制可以捕捉交叉切入关注。

## 接合点模型

面向方面编程语言的建议相关构件，定义了一个接合点模型（JPM）。一个JPM定义了三种东西：

- 接合点 (编程)（join point），它指定在什么时候可以运行额外行为。在一个运行的程序中，接合点是可派上用场的能接合上额外行为的时间点。一个接合点想要有用，它必需是可寻址的，并且对普通程序员是可理解的。接合点还应该经历无关紧要的程序变更而保持稳定，使得一个方面经历这种变更而保持稳定。很多AOP实现支持方法执行和字段引用作为接合点。
- 定点切入（pointcut），它是规定（或量化）接合点的交叉切入模式的方式。定点切入确定运行的程序在特定时间点是否匹配一个给定接合点，定点切入所专用的语言一般使用类似基础语言的语法，例如AspectJ使用Java签名，定点切入允许通过命名和组合来重新使用。
- 建议 (编程)（advice），这是AspectJ中的称谓，它是指定在接合点要运行额外行为的手段。建议通常是可以在接合点之前、之后和周围运行特定代码。

对接合点模型进行比较可以基于：所暴露的接合点，如何规定接合点的交叉切入模式，在接合点上允许的操作，能够表达的结构性增强机制。一些实现还支持在一个方面中定义另一个类上的一个方法。

### AspectJ的接合点模型

所有有效的Java程序也是有效的AspectJ程序，但是AspectJ容许编程者定义叫方面 (编程)的特殊构造。方面包含一些对于标准类不能获得到的实体，它们是扩展方法、定点切入和建议。

在AspectJ中接合点 (编程)包括：方法或构造子调用或执行，一个类或对象的初始化，字段读或写访问，异常处理等。接合点不包括：循环、`super`调用，`throw`子句，多个语句等。

#### 扩展方法

扩展方法允许编程者在这个方面之内向现存的类增加方法、字段或接口。下面的访问者模式例子中，方面`VisitAspect`向具体元素类比如`Point`增加一个`accept`方法：

```aspectj
aspect VisitAspect {
  ⋮
  void Point.accept(Visitor v) {
    v.visit(this);
  }
  ⋮
}
```

#### 定点切入

定点切入是通过组合“原始定点切入指示符”（PCD - primitive pointcut designator）来规定接合点的交叉切入模式。定点切入可以复合和命名来重新使用。例如：

```aspectj
aspect …… {
  ⋮
  pointcut set() : execution(* set*(*)) && this(Point) && within(com.company.*);
  ⋮
}
```

这个使用名字`set()`来提及的定点切入，匹配一个方法执行接合点，如果这个方法名字开始于`set`，并且此对象（`this`）是在`com.company`包中类型`Point`的实例。

“种类”PCD匹配特定种类的接合点（比如方法执行），并且倾向于接受类似Java模样的签名作为输入：

```aspectj
execution(* set*(*))
```

这个定点切入匹配一个方法执行接合点，如果这个方法名字开始于`set`，并且精确的只有一个任何类型的实际参数。

“动态”PCD检查运行时间类型和绑定变量：

```aspectj
this(Point)
```

这个定点切入在当前执行对象是类`Point`的实例之时匹配。注意一个类的未限定名字可以通过Java的正常类型查找来使用。

“范围”PCD限制接合点的词法作用域：

```aspectj
within(com.company.*)
```

这个定点切入匹配在`com.company`包中任何类型的任何接合点。`*`是一种形式的通配符，它用来匹配具有一个签名的任何东西。

#### 建议

建议 (编程)规定了在匹配了定点切入的而交叉切入一个接合点之后，除了运行接合点所指定的代码比如一个方法之外，可以在它之前、之后或周围运行的特定代码。面向方面编程的运行时间系统，在某个定点切入匹配一个接合点的时候，自动调用建议。例如：

```aspectj
aspect …… {
  ⋮
  after() : set() {
    Display.update();
  }
  ⋮
}
```

这在效果上指定了：“如果匹配了`set()`定点切入而交叉切入这个接合点，在它完成之后，运行`Display.update()`。”

## 实现

下列编程语言已经实现了AOP，于语言之内或外部库：

- .NET Framework语言（C# / VB.NET）
- PostSharp，是一个商业AOP实现，具有免费但有限制的版本。
- 微软企业库，提供到在核心编程领域包括数据访问、安全性、日志、异常处理和其他之中经过实践检验的设施的API。
- ActionScript
- Ada
- AutoHotkey
- C / C++
- COBOL
- Cocoa Objective-C框架
- ColdFusion
- Common Lisp
- Delphi
- Oxygene (编程语言)
- e (验证语言)（IEEE 1647）
- Emacs Lisp
- Groovy
- Haskell
- Java
- AspectJ
- Spring Framework
- JavaScript
- Logtalk
- Lua
- make
- Matlab
- ML
- Perl
- PHP
- Prolog
- Python
- Racket
- Ruby
- Squeak Smalltalk
- UML 2.0
- XML

## 站外链接

- Aspect-Oriented Software Development
- Aspect-Oriented Software Development（AOP年会）
- AOSD Wiki （英文维基给AOP的专栏）
- AspectJ（Java的实现）
- 更多有关于型态间成员宣告的资料
- 关于面向侧面的编程和AspectJ的系列文章
- The AspectBench Compiler for AspectJ（另一个Java实现）
- 一篇深度讨论AOP与AspectJ的系列文章
- 利用RemObject Taco实现AOP的文章
- Constraint-Specification Aspect Weaver
- 面向侧面对物件导向：该用哪个？何时使用？
- 给Python用的AOP轻量实现
- LOOM.NET
- 剖面导向程式设计(AOP/AOSD)简介