面向方面编程与面向对象编程
像大多数开发人员在这里和全世界一样,我一直在使用面向对象编程(OOP)技术开发软件系统多年。 所以当我读到面向方面编程(AOP)解决了许多传统OOP并没有完全或者直接解决的问题的时候,我停下来思考一下,这是真的吗?
我已经阅读了大量的信息,试图学习这个AOP范例的关键,并且我在同一个地方,所以我想更好地理解它在现实世界应用程序开发中的好处。
有人有答案吗?
为什么“vs”? 这不是“vs”。 您可以将面向方面编程与function编程结合使用,也可以与面向对象方法结合使用。 它不是“vs”,它是“面向对象编程的面向方面编程”(Aspect Oriented Programming with Object Oriented Programming)。
对我而言,AOP是一种“元编程”。 AOP所做的一切都可以通过添加更多的代码来完成。 AOP只是节省你写这个代码。
维基百科有这个元编程的最好的例子之一。 假设你有一个graphics类与许多“设置…()”方法。 在每种设置方法之后,graphics数据改变,因此graphics改变,因此graphics需要在屏幕上更新。 假设重新绘制graphics,您必须调用“Display.update()”。 传统的方法是通过添加更多的代码来解决这个问题。 在你写的每一套方法的末尾
void set...(...) { : : Display.update(); }
如果你有3个设置方法,这不是一个问题。 如果你有200个(假设的),那么随处添加这个会变得非常痛苦。 此外,无论何时添加新的set-method,都必须确保不要忘记将其添加到最后,否则您只是创build了一个bug。
AOP解决这个问题,而不添加大量的代码,而是添加一个方面:
after() : set() { Display.update(); }
而就是这样! 您不要自己编写更新代码,而是告诉系统,在达到set()切入点后,它必须运行此代码,并运行此代码。 无需更新200个方法,不需要确保您不会忘记在新的set方法中添加此代码。 另外你只需要一个切入点:
pointcut set() : execution(* set*(*) ) && this(MyGraphicsClass) && within(com.company.*);
那是什么意思? 这意味着如果一个方法被命名为“set *”(*表示任何名字可以在set之后),不pipe这个方法返回什么(第一个星号)或者它需要什么参数(第三个星号), 并且这是MyGraphicsClass的一个方法。类是包“com.company。*”的一部分,那么这是一个set()切入点。 我们的第一个代码说:“运行任何设置切入点的方法后 ,运行下面的代码”。
看看AOP如何优雅地解决这个问题? 其实这里所描述的一切都可以在编译时完成。 一个AOP预处理器可以修改你的源代码(例如在每个set-pointcut方法的末尾添加Display.update(),甚至在编译类本身之前。
不过,这个例子也显示了AOP的一大缺点。 AOP实际上正在做许多程序员认为是“ 反模式 ”的事情。 确切的模式被称为“ 在远处行动 ”。
远程行动是一种反模式(一种公认的常见错误),其中一个程序的某一部分的行为因为在程序的另一部分难以或难以识别操作而变化很大。
作为一个项目的新手,我可能只是读取任何set-method的代码,并认为它破碎,因为它似乎不更新显示。 我没有看到一个set-method的代码,执行后,一些其他代码将“神奇地”执行来更新显示。 我认为这是一个严重的缺点! 通过修改方法,可能会引入奇怪的错误。 进一步理解代码在某些事情似乎正常工作的代码stream程中,但并不明显(正如我所说,他们只是神奇地工作……不知何故),真的很难。
更新
只是为了澄清一下:有些人可能有这样的印象,我说的AOP是坏东西,不应该使用。 那不是我说的! AOP实际上是一个很棒的function。 我只是说“小心使用”。 AOP只会导致问题,如果你混合正常的代码和AOP为同一方面 。 在上面的例子中,我们有更新graphics对象的值和绘制更新对象的方面。 这实际上是一个方面。 编码的一半作为正常的代码,另一半作为方面是什么增加了问题。
如果您将AOP用于完全不同的方面,例如进行日志logging,则不会遇到反模式问题。 在这种情况下,这个项目的新手可能会想知道“所有这些日志消息是从哪里来的?我在代码中看不到任何日志输出”,但这不是一个大问题。 他对程序逻辑的改变几乎不会破坏日志function,对日志function的改变也不会破坏他的程序逻辑 – 这些方面是完全分开的。 使用AOP进行日志logging有一个好处,即程序代码可以完全专注于做它应该做的事情,而且还可以进行复杂的日志logging,而不会让任何地方的数百条日志消息混淆在一起。 而且,当新代码被引入时,神奇的日志消息将会在正确的时间显示正确的内容。 新手程序员可能不明白他们为什么在那里或者他们从哪里来,但是因为他们会在“正确的时间”logging“正确的事情”,所以他可以高兴地接受这个事实,他们在那里,继续前进。
因此,在我的例子中,AOP的一个很好的用法是,如果任何值已经通过set方法更新,那么总是logging。 这不会造成反模式,也不会成为任何问题的原因。
有人可能会说,如果你可以轻易地滥用AOP来创build这么多问题,那么全部使用它是一个坏主意。 但是哪种技术不能被滥用? 你可以滥用数据封装,你可以滥用inheritance。 几乎所有有用的编程技术都可能被滥用。 考虑一种如此有限的编程语言,它只包含不能被滥用的function; 一种语言的function只能用于最初打算使用的function。 这样的语言将会非常有限,甚至可以用于现实世界的编程也是有争议的。
OOP和AOP不是相互排斥的。 AOP可以是面向对象的良好补充。 AOP特别适用于将日志,性能跟踪等标准代码添加到方法中,而不会使用此标准代码堵塞方法代码。
面向方面的pogramming提供了一个很好的方式来实现诸如日志logging,安全等交叉问题。 这些交叉的概念是一些必须在许多地方应用的逻辑,但实际上与业务逻辑没有任何关系。
您不应该将AOP看作是OOP的替代品,更像是一个很好的附加组件,它使您的代码更加干净,松散耦合,专注于业务逻辑。 所以通过应用AOP,您将获得两大好处:
-
每个问题的逻辑现在在一个地方,而不是分散在整个代码库。
-
类更清洁,因为它们只包含主要关心(或核心function)的代码,而次要问题已被转移到方面。
我认为这个问题没有普遍的答案,但有一点需要注意的是,AOP并不能取代 OOP,而是增加了某些分解特征来解决所谓的主导组合 ( 1 )(或横切关注点)的暴政 。
在某些情况下,只要您控制用于特定项目的工具和语言,它肯定会有所帮助,但是也会增加方面交互的复杂性,并需要额外的工具(如AJDT)来理解你的程序。
Gregor Kiczales曾经在Google技术讲座上就AOP做了一个有趣的介绍性演讲,我推荐他们观看: 面向方面的编程:模块化的激进研究 。
首先AOP不会取代OOP。 AOP扩展OOP。 OOP的想法和实践保持相关。 有一个好的对象devise可能会更容易扩展它的方面。
我认为AOP带来的想法非常重要。 我们需要找出方法来实现对程序中不同类的交叉关注,而不必自己改变类。 但是我认为AOP最终只会成为我们使用的其他工具的一部分,而不是一个单独的工具或技术。 我们已经看到了这种情况。
像Ruby和Python这样的dynamic语言有一些像mixin一样的语言结构可以解决相同的问题。 这看起来很像AOP,但更好地融入了语言。
Spring和Castle以及其他一些dependency injection框架可以select添加行为到他们注入的类。 这是一个运行时编织的方式,我认为这有很大的潜力。
我不认为你必须学习一个全新的范例来使用AOP。 这些想法很有趣,但正在慢慢被现有的工具和语言所吸收。 只要保持知情并尝试这些工具。
AOP是一个处理这个概念的新的编程范例。 一个方面是实现应用程序的特定非function部分的软件实体。
我认为这篇文章是从Aspect Oriented Programming开始的一个好地方: http : //www.jaftalks.com/wp/index.php/introduction-to-aspect-oriented-programming/