像这里和整个世界的大多数开发人员一样,我多年来一直在使用面向对象编程(OOP)技术开发软件系统.因此,当我读到面向方面编程(AOP)解决传统OOP无法完全或直接解决的许多问题时,我会停下来思考,这是真的吗?
我已经阅读了很多信息,试图学习这个AOP范例的关键,并且我在同一个地方,所以,我想更好地理解它在现实世界应用程序开发中的好处.
有人有答案吗?
为什么"vs"?它不是"vs".您可以将面向方面编程与函数编程结合使用,也可以与面向对象编程结合使用.这不是"VS",它是"面向方面的编程与面向对象编程".
对我来说,AOP是某种"元编程".只需添加更多代码,AOP所做的一切也可以在没有它的情况下完成.AOP只是为您节省了编写此代码的费用.
维基百科拥有此元编程的最佳示例之一.假设您有一个包含许多"set ...()"方法的图形类.在每个设置方法之后,图形的数据改变,因此图形改变,因此图形需要在屏幕上更新.假设要重新绘制图形,您必须调用"Display.update()".经典的方法是通过添加更多代码来解决这个问题.在你编写的每个set方法的最后
void set...(...) { : : Display.update(); }
如果你有3个set方法,那不是问题.如果你有200(假设),那么到处添加它真的很痛苦.此外,无论何时添加新的set-method,都必须确保不要忘记将其添加到最后,否则您只是创建了一个bug.
AOP在不添加大量代码的情况下解决了这个问题,而是添加了一个方面:
after() : set() { Display.update(); }
就是这样!您只需告诉系统在达到set()切入点后,它必须运行此代码并运行此代码,而不是自己编写更新代码.无需更新200个方法,无需确保不要忘记在新的set方法上添加此代码.另外你只需要一个切入点:
pointcut set() : execution(* set*(*) ) && this(MyGraphicsClass) && within(com.company.*);
那是什么意思?这意味着,如果一个方法被命名为"设置*"(*指集之后的任何名称可能跟随),无论什么样的方法返回(第一个星号),或者什么参数需要(第三星号)和它MyGraphicsClass的方法与此class是"com.company.*"包的一部分,然后这是一个set()切入点.我们的第一个代码说" 在运行任何设置切入点的方法之后,运行以下代码".
了解AOP如何在这里优雅地解决问题?实际上这里描述的所有内容都可以在编译时完成.AOP预处理器可以在修改类本身之前修改源代码(例如,将Display.update()添加到每个set-pointcut方法的末尾).
但是,这个例子也显示了AOP的一大缺点.AOP实际上正在做一些许多程序员认为是" 反模式 "的东西.确切的模式称为" 远距离行动 ".
远距离处的动作是反模式(公认的常见错误),其中程序的一个部分中的行为基于在程序的另一部分中难以或不可能识别操作而变化很大.
作为一个项目的新手,我可能只是读取任何set-method的代码并认为它被破坏了,因为它似乎没有更新显示.我没有看到只是看一下set-method的代码,在执行之后,一些其他代码将"神奇地"执行以更新显示.我认为这是一个严重的缺点!通过对方法进行更改,可能会引入奇怪的错误.进一步理解代码的代码流,其中某些东西似乎正常工作,但不明显(正如我所说,它们只是神奇地工作......不知何故),真的很难.
只是为了澄清:有些人可能会有这样的印象,我说AOP是坏事,不应该使用.那不是我说的!AOP实际上是一个很棒的功能.我只是说"小心使用".如果您将同一方面的普通代码和AOP混淆,AOP只会导致问题.在上面的示例中,我们有更新图形对象的值并绘制更新对象的Aspect.这实际上是一个方面.将其中的一半编码为普通代码,将另一半编码为方面是增加问题的原因.
如果您将AOP用于完全不同的方面,例如用于日志记录,则不会遇到反模式问题.在这种情况下,项目的新手可能会想知道"所有这些日志消息都来自哪里?我在代码中看不到任何日志输出",但这不是一个大问题.他对程序逻辑所做的更改几乎不会破坏日志工具,对日志工具所做的更改几乎不会破坏他的程序逻辑 - 这些方面是完全分开的.使用AOP进行日志记录的优势在于,您的程序代码可以完全专注于执行任何应该执行的操作,并且您仍然可以进行复杂的日志记录,而不会使代码在任何地方被数百条日志消息混乱.此外,当引入新代码时,神奇的日志消息将在正确的时间出现在正确的内容中.
因此,在我的示例中,AOP的良好用法是始终记录是否已通过set方法更新任何值.这不会产生反模式,也不会成为任何问题的原因.
有人可能会说,如果你很容易滥用AOP来创造这么多问题,那么使用它就不是一个好主意.但是哪种技术不能被滥用?您可以滥用数据封装,可以滥用继承.几乎所有有用的编程技术都可能被滥用.考虑一种如此有限的编程语言,它只包含不能被滥用的功能; 一种语言,其功能只能用于最初打算使用的功能.这种语言是如此有限,如果它甚至可以用于现实世界的编程,它是有争议的.
面向方面的编程提供了一种很好的方法来实现日志记录,安全性等跨领域问题.这些交叉概念是必须在许多地方应用的逻辑部分,但实际上与业务逻辑没有任何关系.
您不应该将AOP视为OOP的替代品,更多的是作为一个不错的附加组件,它使您的代码更加干净,松散耦合并专注于业务逻辑.因此,通过应用AOP,您将获得以下两项主要优势:
每个问题的逻辑现在都在一个地方,而不是分散在整个代码库中.
类更清晰,因为它们只包含主要关注(或核心功能)的代码,并且次要问题已转移到方面.
OOP和AOP不是互斥的.AOP可以很好地添加到OOP中.AOP特别方便用于向方法添加标准代码(如日志记录,性能跟踪等),而不会使用此标准代码阻塞方法代码.
我认为这个问题没有一般性的答案,但有一点需要注意的是,AOP并没有取代 OOP,而是增加了某些分解特征,这些特征解决了所谓的主导构成的暴政(1)(或横切关注点).
在某些情况下,只要您掌握了用于特定项目的工具和语言,它肯定会有所帮助,但也增加了关于方面交互的新复杂程度以及对AJDT等其他工具的需求仍然可以理解你的计划.
Gregor Kiczales曾经在Google Tech Talks上就AOP做了一个有趣的介绍性演讲,我建议观看:面向方面编程:模块化中的激进研究.
首先,AOP不会取代OOP.AOP扩展了OOP.OOP的想法和实践保持相关.拥有一个好的对象设计可能会更容易扩展它与方面.
我认为AOP带来的想法很重要.我们需要找到方法来实现程序中不同类的跨领域关注,而无需自己更改类.但我认为AOP最终将成为我们使用的其他工具的一部分,而不是一个单独的工具或技术.我们已经看到了这种情况.
一些动态语言(如Ruby和Python)具有类似mixin的语言结构,可以解决相同的问题.这看起来很像AOP,但更好地集成在语言中.
Spring和Castle以及其他一些依赖注入框架可以选择向它们注入的类添加行为.这是一种做运行时编织的方法,我认为这有很大的潜力.
我认为你不必学习使用AOP的全新范例.这些想法很有趣,但现在慢慢地被现有的工具和语言所吸收.随时了解并试用这些工具.