面向切面的程序设计

**剖面導向程式設計**（Aspect-oriented programming，AOP，又譯作**面向方面程式設計**、**面向切面的程式設計**），是電腦科學中的一種程式設計范型，旨在將交叉切入關注點與作為業務主體的核心關注點進行分離，以提高程式碼的模組化程度。通過在現有代碼基礎上增加額外的通告（Advice）機制，能夠對被聲明為「點切入」（Pointcut）的代碼塊進行統一管理與裝飾，比如說：「對所有方法名以 `set*` 開頭的方法添加後台紀錄檔」。該范型使得開發人員能夠將與代碼核心業務邏輯關係不那麼密切的功能（如紀錄檔功能）添加至程式中，同時又不降低業務代碼的可讀性。

## 簡介

剖面導向程式設計將代碼邏輯切分為不同的模組（即關注點，一段特定的邏輯功能)。幾乎所有的編程范型都涉及代碼功能的分類，將各個關注點（Concern）封裝成獨立的抽象模組（如函式、過程、模組、類以及方法等），後者又可供進一步實現、封裝和重寫。部分關注點「交叉切入」程式碼中的數個模組，即在多個模組中都有出現，它們即被稱作交叉切入關注點。

紀錄檔功能即是交叉切入關注點的一個典型案例，因為紀錄檔功能往往跨越系統中的每個業務模組，即「交叉切入」所有有紀錄檔需求的類及方法體。而對於一個信用卡應用程式來說，存款、取款、帳單管理是它的核心關注點，紀錄檔和持久化將成為交叉切入整個物件結構的交叉切入關注點。

方面的概念源於對物件導向程式設計和計算反射的融合。面向切面程式語言擁有很多類似於元物件協定的功能，但有更多的限制。方面相關的編程概念包括主題、混入和委託。使用面向切面范型的其他方式有複合過濾器和 Hyper/J 的 hyperslices 方式。

## 歷史

「剖面導向程式設計」這一術語由全錄帕洛阿爾托研究中心的 Chris Maeda 首先提出，但其具體時間已經不可考證了。術語「交叉切入」是由 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 定義了三種東西：

1. 何時通告可以執行。這些叫做接合點，因為在一個執行的程式中，它們是可以有用的接合上額外行為的點。一個接合點想要有用，它必需是可定址的，並且對普通程式設計師是可理解的。它還應該經歷無關緊要的程式變更而保持穩定，使得一個方面經歷這種變更而保持穩定。很多 AOP 實現支援方法執行和欄位參照作為接合點。
2. 規定（或量化）接合點的方式，這叫做點切入。點切入確定是否匹配一個給定接合點。最有用的點切入語言使用像基礎語言的語法（例如 AspectJ 使用 Java 簽章），並允許通過命名和組合來重新使用。
3. 指定在接合點要執行的代碼的手段。AspectJ 稱之為通告，並且可以在接合點之前、之後和周圍執行。一些實現還支援在一個方面中定義另一個類上的一個方法。

對接合點模型進行比較可以基於：所暴露的接合點，如何規定接合點，在接合點上允許的操作，能夠表達的結構性增強機制。

### AspectJ 的接合點模型

所有有效的 Java 程式也是有效的 AspectJ 程式，但是 AspectJ 容許編程者定義叫方面的特殊構造。方面包含一些對於標準類不能獲得到的實體。它們是擴充方法、點切入和通告。

擴充方法允許編程者在這個方面之內向現存的類增加方法、欄位或介面。下面例子向類 `Point` 增加一個 `acceptVisitor` 方法（參見存取者模式）：

在 AspectJ 中接合點包括：方法或構造子呼叫或執行，一個類或物件的初始化，欄位讀或寫存取，例外處理等。接合點不包括：迴圈、`super` 呼叫，`throw` 子句，多個語句等。

點切入是通過組合「原始點切入指示符」（PCD）來規定的。「種類」PCD 匹配特定種類的接合點（比如方法執行），並且傾向於接受類似 Java 簽章作為輸入。一個這種點切入如下所示：

這個點切入匹配一個方法執行接合點，如果這個方法名字開始於 `set`，並且精確的只有一個任何類型的實際參數。

「動態」PCD 檢查執行時間類型和繫結變數。例如：

這個點切入在當前執行物件是類 `Point` 的實例之時匹配。注意一個類的未限定名字可以通過 Java 的正常類型尋找來使用。

「範圍」PCD 限制接合點的詞法作用域。例如：

這個點切入匹配在 `com.company` 包中任何類型的任何接合點。`*` 是一種形式的萬用字元，它用來匹配具有一個簽章的任何東西。

點切入可以複合和命名來重新使用。例如：

這個點切入匹配一個方法執行接合點，如果這個方法名字開始於 `set`，並且 `this` 是在 `com.company` 包中類型 `Point` 的實例。這個點切入可以使用名字 `set()` 來提及。

通告規定在（通過點切入指定的）一個接合點（之前、之後或周圍）執行特定代碼（指定如若一個方法中的代碼）。AOP 執行時間系統，在這個點切入匹配一個接合點的時候，自動呼叫通告。例如：

這在效果上指定了：「如果 `set()` 點切入匹配這個接合點，在接合點完成之後，執行代碼 `Display.update()`。」

## 實現

下列程式語言已經實現了 AOP，於語言之內或外部庫：

## 參考文獻

## 站外部連結接

* Aspect-Oriented Software Development
* Aspect-Oriented Software Development（AOP 年會）
* AOSD Wiki （頁面存檔備份，存於網際網路檔案館）（英文維基給 AOP 的專欄）
* AspectJ（頁面存檔備份，存於網際網路檔案館）（Java 的實現）
  * [1] （頁面存檔備份，存於網際網路檔案館） 更多有關於型態間成員宣告的資料
* 關於面向側面的程式設計和 AspectJ 的系列文章
* The AspectBench Compiler for AspectJ（另一個 Java 實現）
* 一篇深度討論 AOP 與 AspectJ 的系列文章
* 利用 RemObject Taco 實現 AOP 的文章
* Constraint-Specification Aspect Weaver （頁面存檔備份，存於網際網路檔案館）
* 面向側面對物件導向：該用哪個？何時使用？ （頁面存檔備份，存於網際網路檔案館）
* 給 Python 用的 AOP 輕量實現
* LOOM.NET （頁面存檔備份，存於網際網路檔案館）
* 剖面導向程式設計 (AOP/AOSD) 簡介 （頁面存檔備份，存於網際網路檔案館）