面向切面的程序设计
(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发表了论文《面向方面编程》[1];代表系统是基于元对象协议的面向方面编程系统和AspectJ。
- 国际商用机器公司托马斯·J·沃森研究中心:William Harrison、Harold Ossher、Peri Tarr等人,在1980年代末进行的早期工作,有关于软件开发环境与工具集成;后来提出了多维关注分离(MDSOC),代表系统是Hyper/J。
- 美国东北大学:Karl Lieberherr等人,进行的早期工作是研究软件演化,提出了得墨忒耳定律、传播模式、适应性编程[2];代表系统是Demeter/C++和Demeter/Java。
- 荷兰特文特大学:Mehmet Aksit等人,其代表系统是复合过滤器。
基本概念
关注是对软件工程有意义的小的、可管理的、可描述的软件组成部分,一项关注通常只同一个特定概念或目标相关联。传统的编程语言,以一种线性的文本来描述软件,只采用一种方式比如类,将软件分解成模块;这导致某些关注比较好的被捕捉,容易进一步组合、扩展;但还有一些关注没有被捕捉,弥散在整个软件内部。
关注分离(SOC)是标识、封装和操纵只与特定概念、目标相关联的软件组成部分的能力,即标识、封装和操纵关注的能力。分离关注使得解决特定领域问题的代码从业务逻辑中独立出来,业务逻辑的代码中不再含有针对特定领域问题代码的调用,业务逻辑同特定领域问题的关系通方面来封装、维护,这样原本分散在在整个应用程序中的变动就可以很好的管理起来。
核心关注是一个软件最主要的关注。在传统的编程语言中,将软件分解成模块的主要方式,是支配性分解,即按主关注进行模块分解。用来描述、设计、实现一项给定关注的软件构造单位是方法。如果两个关注的实现的方法存在交集,则称谓这两个关注相互交叉切入(crosscut)。
面向方面编程的核心概念,是从核心关注中分离出交叉切入关注。面向方面编程,在支配性分解的基础上,提供叫做方面(aspect)的一种辅助的模块化机制,这种新的模块化机制可以捕捉交叉切入关注。
接合点模型
面向方面编程语言的通告相关构件,定义了一个接合点模型(JPM)。一个JPM定义了三种东西:
- 何时通告可以运行。这些叫做接合点,因为在一个运行的程序中,它们是可以有用的接合上额外行为的点。一个接合点想要有用,它必需是可寻址的,并且对普通程序员是可理解的。它还应该经历无关紧要的程序变更而保持稳定,使得一个方面经历这种变更而保持稳定。很多AOP实现支持方法执行和字段引用作为接合点。
- 规定(或量化)接合点的方式,这叫做点切入。点切入确定是否匹配一个给定接合点。最有用的点切入语言使用像基础语言的语法(例如AspectJ使用Java签名),并允许通过命名和组合来重新使用。
- 指定在接合点要运行的代码的手段。AspectJ称之为通告,并且可以在接合点之前、之后和周围运行。一些实现还支持在一个方面中定义另一个类上的一个方法。
对接合点模型进行比较可以基于:所暴露的接合点,如何规定接合点,在接合点上允许的操作,能够表达的结构性增强机制。
AspectJ的接合点模型
所有有效的Java程序也是有效的AspectJ程序,但是AspectJ容许编程者定义叫方面的特殊构造。方面包含一些对于标准类不能获得到的实体。它们是扩展方法、点切入和通告。
扩展方法
扩展方法允许编程者在这个方面之内向现存的类增加方法、字段或接口。下面例子中,方面VisitAspect
向类Point
增加一个acceptVisitor
方法(参见访问者模式):
aspect VisitAspect {
void Point.acceptVisitor(Visitor v) {
v.visit(this);
}
⋮
}
点切入
在AspectJ中接合点包括:方法或构造子调用或执行,一个类或对象的初始化,字段读或写访问,异常处理等。接合点不包括:循环、super
调用,throw
子句,多个语句等。
点切入是通过组合“原始点切入指示符”(PCD - primitive pointcut designator)来规定的。例如:
aspect VisitAspect {
⋮
pointcut set() : execution(* set*(*) ) && this(Point) && within(com.company.*);
⋮
}
这个点切入匹配一个方法执行接合点,如果这个方法名字开始于set
,并且此对象(this
)是在com.company
包中类型Point
的实例。这个点切入可以使用名字set()
来提及。点切入可以复合和命名来重新使用。
“种类”PCD匹配特定种类的接合点(比如方法执行),并且倾向于接受类似Java模样的签名作为输入:
execution(* set*(*))
这个点切入匹配一个方法执行接合点,如果这个方法名字开始于set
,并且精确的只有一个任何类型的实际参数。
“动态”PCD检查运行时间类型和绑定变量:
this(Point)
这个点切入在当前执行对象是类Point
的实例之时匹配。注意一个类的未限定名字可以通过Java的正常类型查找来使用。
“范围”PCD限制接合点的词法作用域:
within(com.company.*)
这个点切入匹配在com.company
包中任何类型的任何接合点。*
是一种形式的通配符,它用来匹配具有一个签名的任何东西。
通告
通告规定在(通过点切入指定的)一个接合点(之前、之后或周围)运行特定代码(指定如若一个方法中的代码)。面向方面编程的运行时间系统,在这个点切入匹配一个接合点的时候,自动调用通告。例如:
aspect VisitAspect {
⋮
after() : set() {
Display.update();
}
}
这在效果上指定了:“如果set()
点切入匹配这个接合点,在接合点完成之后,运行代码Display.update()
。”
实现
下列编程语言已经实现了AOP,于语言之内或外部库:
- .NET Framework语言(C# / VB.NET)[3]
- PostSharp[4],是一个商业AOP实现,具有免费但有限制的版本。
- Unity,提供到在核心编程领域包括数据访问、安全性、日志、异常处理和其他之中经过实践检验的设施的API。
- ActionScript[5]
- Ada[6]
- AutoHotkey[7]
- C / C++[8]
- COBOL[9]
- The Cocoa Objective-C框架[10]
- ColdFusion[11]
- Common Lisp[12]
- Delphi[13][14][15]
- Delphi Prism[16]
- e(IEEE 1647)
- Emacs Lisp[17]
- Groovy
- Haskell[18]
- Java[19]
- AspectJ
- Spring Framework
- JavaScript[20]
- Logtalk[21]
- Lua[22]
- make[23]
- Matlab[24]
- ML[25]
- Perl[26]
- PHP[27]
- Prolog[28]
- Python[29]
- Racket[30]
- Ruby[31][32][33]
- Squeak Smalltalk[34][35]
- UML 2.0[36]
- XML[37]
參考文獻
- Kiczales, G.; Lamping, J.; Mendhekar, A.; Maeda, C.; Lopes, C.; Loingtier, J. M.; Irwin, J. (PDF). ECOOP'97. Proceedings of the 11th European Conference on Object-Oriented Programming. LNCS 1241. 1997: 220–242. CiteSeerX 10.1.1.115.8660 . ISBN 3-540-63089-9. doi:10.1007/BFb0053381. (原始内容存档 (PDF)于2016-01-12).
- "Adaptive Object Oriented Programming: The Demeter Approach with Propagation Patterns" Karl Liebherr 1996 ISBN 0-534-94602-X presents a well-worked version of essentially the same thing (Lieberherr subsequently recognized this and reframed his approach).
- Numerous: Afterthought 的存檔,存档日期2016-03-15., LOOM.NET 的存檔,存档日期2008-08-27., Enterprise Library 3.0 Policy Injection Application Block 的存檔,存档日期2007-01-19., AspectDNG 的存檔,存档日期2004-09-29., DynamicProxy 的存檔,存档日期2015-12-05., Compose* Wikiwix的存檔,存档日期2005-08-21, PostSharp 的存檔,存档日期2016-05-03., Seasar.NET 的存檔,存档日期2006-07-25., DotSpect (.SPECT) 的存檔,存档日期2006-03-31., Spring.NET 的存檔,存档日期2006-04-02. (as part of its functionality), Wicca and Phx.Morph 的存檔,存档日期2006-12-07., SetPoint 的存檔,存档日期2008-10-07.
- . [2022-02-13]. (原始内容存档于2022-07-05).
- . as3commons.org. [5 May 2018]. (原始内容存档于3 October 2014).
- (PDF). adacore.com. [5 May 2018]. (原始内容存档 (PDF)于18 April 2016).
- . autohotkey.com. [5 May 2018]. (原始内容存档于17 January 2013).
- Several: AspectC++, FeatureC++ (页面存档备份,存于), AspectC 的存檔,存档日期2006-08-21., AspeCt-oriented C 的存檔,存档日期2008-11-20., Aspicere
- . vub.ac.be. [5 May 2018].
- . neu.edu. [5 May 2018]. (原始内容存档于26 October 2007).
- . 5 November 2005 [5 May 2018]. (原始内容存档于5 November 2005).
- . [11 August 2015]. (原始内容存档于23 February 2011).
- . [11 August 2015]. (原始内容存档于9 September 2015).
- . [11 August 2015]. (原始内容存档于10 September 2015).
- . [11 August 2015]. (原始内容存档于25 December 2014).
- . codegear.com. [5 May 2018]. (原始内容存档于23 January 2012).
- . gnu.org. [5 May 2018]. (原始内容存档于24 October 2011).
- Monads allow program semantics to be altered by changing the type of the program without altering its code: De Meuter, Wolfgang. . International Workshop on Aspect-Oriented Programming at ECOOP. 1997: 25. CiteSeerX 10.1.1.25.8262 . Tabareau, Nicolas; Figueroa, Ismael; Tanter, Éric. . Proceedings of the 12th Annual International Conference on Aspect-oriented Software Development. Aosd '13. March 2013: 171–184 [2022-03-02]. ISBN 9781450317665. S2CID 27256161. doi:10.1145/2451436.2451457. (原始内容存档于2018-11-06). Type classes allow additional capabilities to be added to a type: Sulzmann, Martin; Wang, Meng. . Proceedings of the 6th Workshop on Foundations of Aspect-oriented Languages. March 2007: 65–74. ISBN 978-1595936615. S2CID 3253858. doi:10.1145/1233833.1233842..
- Numerous others: CaesarJ 的存檔,存档日期2008-12-19., Compose* Wikiwix的存檔,存档日期2005-08-21, Dynaop 的存檔,存档日期2007-07-24., JAC 的存檔,存档日期2004-06-19., Google Guice (as part of its functionality), Javassist 的存檔,存档日期2004-09-01., JAsCo (and AWED) 的存檔,存档日期2005-04-11., JAML 的存檔,存档日期2005-04-15., JBoss AOP 的存檔,存档日期2006-10-17., LogicAJ 的存檔,存档日期2006-05-04., Object Teams 的存檔,存档日期2005-08-31., PROSE 的存檔,存档日期2007-01-24., The AspectBench Compiler for AspectJ (abc) 的存檔,存档日期2014-12-16., Spring framework (as part of its functionality), Seasar, The JMangler Project 的存檔,存档日期2005-10-28., InjectJ 的存檔,存档日期2005-04-05., GluonJ 的存檔,存档日期2007-02-06., Steamloom 的存檔,存档日期2007-08-18.
- Many: Advisable 的存檔,存档日期2008-07-04., Ajaxpect 的存檔,存档日期2016-07-09., jQuery AOP Plugin 的存檔,存档日期2008-01-13., Aspectes Wikiwix的存檔,存档日期2006-05-08, AspectJS 的存檔,存档日期2008-12-16., Cerny.js 的存檔,存档日期2007-06-27., Dojo Toolkit 的存檔,存档日期2006-02-21., Humax Web Framework 的存檔,存档日期2008-12-09., Joose 的存檔,存档日期2015-03-18., Prototype - Prototype Function#wrap 的存檔,存档日期2009-05-05., YUI 3 (Y.Do) 的存檔,存档日期2011-01-25.
- Using built-in support for categories (which allows the encapsulation of aspect code) and event-driven programming (which allows the definition of before and after event handlers).
- . [11 August 2015]. (原始内容存档于17 July 2015).
- . [11 August 2015]. (原始内容存档于24 July 2012).
- . [11 August 2015]. (原始内容存档于24 September 2015).
- . [11 August 2015]. (原始内容存档于5 December 2010).
- Adam Kennedy. . [11 August 2015]. (原始内容存档于31 August 2013).
- Several: PHP-AOP (AOP.io) Wikiwix的存檔,存档日期2014-08-18, Go! AOP framework 的存檔,存档日期2013-03-01., PHPaspect 的存檔,存档日期2016-08-22., Seasar.PHP 的存檔,存档日期2005-12-26., PHP-AOP, Flow 的存檔,存档日期2018-01-04., AOP PECL Extension 的存檔,存档日期2017-04-11.
- . www.bigzaphod.org. [5 May 2018]. (原始内容存档于20 April 2016).
- Several: PEAK 的存檔,存档日期2005-04-09., Aspyct AOP, Lightweight Python AOP 的存檔,存档日期2004-10-09., Logilab's aspect module 的存檔,存档日期2005-03-09., Pythius 的存檔,存档日期2005-04-08., Spring Python's AOP module 的存檔,存档日期2016-03-04., Pytilities' AOP module 的存檔,存档日期2011-08-25., aspectlib 的存檔,存档日期2014-11-05.
- . [11 August 2015]. (原始内容存档于5 September 2015).
- . [11 August 2015]. (原始内容存档于12 August 2015).
- Dean Wampler. . [11 August 2015]. (原始内容存档于26 October 2007).
- . GitHub. [11 August 2015]. (原始内容存档于4 January 2015).
- . tu-ilmenau.de. [5 May 2018]. (原始内容存档于6 January 2006).
- . [11 August 2015]. (原始内容存档于29 July 2015).
- . iit.edu. [5 May 2018]. (原始内容存档于12 December 2008).
- . [11 August 2015]. (原始内容存档于12 September 2015).
站外链接
- 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)簡介 (页面存档备份,存于)