像这里和全世界的大多数开发人员一样,多年来我一直在使用面向对象编程(OOP)技术开发软件系统。因此,当我读到面向方面编程(AOP)解决了许多传统OOP不能完全或直接解决的问题时,我停下来思考,这是真的吗?

我已经阅读了大量的信息,试图学习这个AOP范式的关键,我也在同样的地方,所以,我想更好地理解它在现实世界应用程序开发中的好处。

有人知道答案吗?


当前回答

OOP主要用来组织你的业务逻辑,而AOP帮助组织你的非功能性的东西,如审计、日志、事务管理、安全等。

通过这种方式,您可以将业务逻辑与非功能逻辑解耦,从而使代码更加清晰。

另一个优点是您可以非常一致地应用通知(例如审计),而不需要实现任何接口,这为修改提供了很大的灵活性,而不涉及业务逻辑。

这种方法很容易实现关注点分离和单一责任。

此外,当业务逻辑只解决业务问题时,很容易将业务逻辑从一个框架(比如Spring)移植到另一个框架(将来)

其他回答

OOP和AOP不是相互排斥的。 AOP可以是OOP的一个很好的补充。 AOP特别便于向方法添加标准代码,如日志记录、性能跟踪等,而不会用标准代码阻塞方法代码。

面向方面的编程提供了实现横切关注点(如日志记录、安全性)的好方法。 这些横切关注点是必须应用于许多地方的逻辑片段,但实际上与业务逻辑没有任何关系。

你不应该把AOP看作是OOP的替代品,而应该看作是OOP的一个不错的附加 使您的代码更加干净、松散耦合并专注于业务逻辑。

因此,通过应用AOP,您将获得两个主要好处:

每个关注点的逻辑现在都在一个地方,而不是分散在整个代码库中。 类更简洁,因为它们只包含主要关注点(或核心功能)的代码,次要关注点已经转移到方面。

我认为这个问题没有通用的答案,但有一点需要注意,AOP并没有取代OOP,而是增加了某些分解特性,这些分解特性解决了所谓的主导组合(1)(或横切关注点)的暴政。

在某些情况下,只要您能够控制用于特定项目的工具和语言,它肯定会有所帮助,但它也增加了方面交互的复杂性,并且需要AJDT等其他工具来理解您的程序。

Gregor Kiczales曾经在谷歌Tech Talks上做了一个关于AOP的有趣的介绍性演讲,我推荐大家观看:面向方面的编程:模块化的根本研究。

首先,AOP不会取代OOP。AOP扩展了OOP。面向对象编程的思想和实践始终是相关的。拥有一个好的对象设计可能会使它更容易扩展到方面。

我认为AOP带来的思想是重要的。我们需要想办法在程序中不同的类上实现横切关注点,而不必改变类本身。但是我认为AOP最终会成为我们使用的其他工具的一部分,而不是一个单独的工具或技术。我们已经看到这种情况发生了。

一些动态语言(如Ruby和Python)具有像mixins这样的语言构造,可以解决相同的问题。这看起来很像AOP,但是更好地集成在语言中。

Spring和Castle以及其他一些依赖注入框架都有向它们注入的类添加行为的选项。这是一种执行运行时编织的方式,我认为它有很大的潜力。

我认为您不需要学习一个全新的范例来使用AOP。这些想法很有趣,但正在被现有的工具和语言慢慢吸收。只要保持了解并尝试这些工具。

为什么“大战”?它不是“vs”。可以将面向方面编程与函数式编程结合使用,也可以将面向对象编程与面向对象编程结合使用。它不是“vs”,而是“面向方面编程与面向对象编程”。

对我来说AOP是某种“元编程”。AOP所做的任何事情都可以通过添加更多的代码来完成。AOP只是节省了您编写这些代码的时间。

维基百科是这种元编程最好的例子之一。假设您有一个具有许多“set…()”方法的图形化类。每次设置方法后,图形的数据发生变化,导致图形发生变化,需要在屏幕上更新图形。假设要重新绘制图形,必须调用“Display.update()”。经典的方法是通过添加更多的代码来解决这个问题。在你写的每个set方法的末尾

void set...(...) {
    :
    :
    Display.update();
}

如果你有3个set-method,这不是问题。如果你有200个(假设),那么到处都加起来会很痛苦。此外,每当你添加一个新的set-method时,你必须确保不要忘记在最后添加这个,否则你就创建了一个错误。

AOP无需添加大量代码就能解决这个问题,相反,你只需添加一个方面:

after() : set() {
   Display.update();
}

就是这样!您不需要自己编写更新代码,只需告诉系统在到达set()切入点之后,它必须运行这段代码,并且它将运行这段代码。不需要更新200个方法,不需要确保你没有忘记在一个新的set-method上添加这段代码。另外,你只需要一个切入点:

pointcut set() : execution(* set*(*) ) && this(MyGraphicsClass) && within(com.company.*);

这是什么意思?这意味着如果一个方法被命名为“set*”(*意味着set后面可能有任何名称),不管该方法返回什么(第一个星号)或它接受什么参数(第三个星号),它是MyGraphicsClass的一个方法,这个类是包“com.company.*”的一部分,那么这是一个set()切入点。而我们的第一个代码表示“在运行任何作为一个集合切入点的方法之后,运行下面的代码”。

看到AOP如何优雅地解决这里的问题了吗?实际上,这里描述的所有内容都可以在编译时完成。AOP预处理器可以在编译类本身之前修改源代码(例如,在每个set-pointcut方法的末尾添加Display.update())。

然而,这个例子也显示了AOP的一个很大的缺点。AOP实际上正在做一些被许多程序员认为是“反模式”的事情。确切的模式被称为“超距行动”。

超距作用是一种 反模式(公认的公共 错误)在其中一个部分的行为 一个程序的差异很大 难以辨认的难以或不可能辨认的 操作的另一部分 程序。

As a newbie to a project, I might just read the code of any set-method and consider it broken, as it seems to not update the display. I don't see by just looking at the code of a set-method, that after it is executed, some other code will "magically" be executed to update the display. I consider this a serious downside! By making changes to a method, strange bugs might be introduced. Further understanding the code flow of code where certain things seem to work correctly, but are not obvious (as I said, they just magically work... somehow), is really hard.

更新

Just to clarify that: Some people might have the impression I'm saying AOP is something bad and should not be used. That's not what I'm saying! AOP is actually a great feature. I just say "Use it carefully". AOP will only cause problems if you mix up normal code and AOP for the same Aspect. In the example above, we have the Aspect of updating the values of a graphical object and painting the updated object. That is in fact a single aspect. Coding half of it as normal code and the other half of it as aspect is what adds the problem.

If you use AOP for a completely different aspect, e.g. for logging, you will not run into the anti-pattern problem. In that case a newbie to the project might wonder "Where do all these log messages come from? I don't see any log output in the code", but that is not a huge problem. Changes he makes to the program logic will hardly break the log facility and changes made to the log facility will hardly break his program logic - these aspects are totally separated. Using AOP for logging has the advantage that your program code can fully concentrate on doing whatever it should do and you still can have sophisticated logging, without having your code being cluttered up by hundreds of log messages everywhere. Also when new code is introduced, magically log messages will appear at the right time with the right content. The newbie programmer might not understand why they are there or where they came from, but since they will log the "right thing" at the "right time", he can just happily accept the fact that they are there and move on to something else.

因此,在我的例子中,AOP的一个很好的用法是,如果任何值已通过set方法更新,则总是记录日志。这不会创建反模式,也几乎不会成为任何问题的原因。

One might say, if you can easily abuse AOP to create so many problems, it's a bad idea to use it all. However which technology can't be abused? You can abuse data encapsulation, you can abuse inheritance. Pretty much every useful programming technology can be abused. Consider a programming language so limited that it only contains features that can't be abused; a language where features can only be used as they were initially intended to be used. Such a language would be so limited that it's arguable if it can be even used for real world programming.