什么是 AOP 能做而 OOP 做不到的事情?

发布于 2024-11-06 02:45:58 字数 1543 浏览 3 评论 0原文

我主要是一名 Java 开发人员。我见过不少热爱 AOP 的 Java 开发人员。我还看到最近出现了越来越多的 AOP“设计模式”,它们似乎被相当广泛地采用。即便如此,出于以下几个原因,我仍然不相信 OO 代码中的 AOP 通常是一个好主意。

  1. 它为代码中添加了“魔法” 不透明的复杂性形式可以 非常难以调试,并且可以 使调试变得极其困难 面向对象的代码 影响。

  2. 在我看来主要是 不必要的,并且(更糟糕的是)经常使用 以避免必须进行良好的设计,或者 弥补之前的贫困 设计。

这是我在过去几年中经常看到的一个例子,作为我的问题的背景。

AOP 之前(来自 Hibernate 文档)

public void saveMyEntityToTheDatabase(MyEntity entity) {
    EntityTransaction tx = null;
    try {
        tx = entityManager.getTransaction();
        tx.begin();
        entityManager.persist(entity);
        tx.commit();
    } catch (RuntimeException e) {
        if(tx != null && tx.isActive()) {
            tx.rollback();
        }
        throw e;
    }
}

AOP 之后

@Transactional
public void saveMyEntityToTheDatabase(MyEntity entity) {
    entityManager.persist(entity);
}

对于很多人来说,AOP 似乎是一个明显的胜利。对我来说,最初的问题是 API 抽象级别不一致的症状。也就是说,EntityManager 的级别比使用它的消息的业务级 API 低得多。这个问题可以通过更合适的抽象级别和更好的(OO)设计来解决。

OO 解决方案

public void saveMyEntityToTheDatabase(MyEntity entity) {
    database.performInTransaction(new Save(entity));
}

该解决方案假定database 对象包含与负责管理@Transactional 方法的方面相同类型的事务逻辑。这解决了我上面的担忧,让更明显的是有一些东西管理与 EntityManager 的交互,而不是引入另一种编程范例。

最后,我的问题是:AOP 能做什么而 OOP 不能做什么?我稍微相信它在跟踪日志记录中的有用性,也许默认的 toString() 实现或类似的东西,但我很想知道是否有人发现它是对于特定类型的问题,明显优于 OO。

I'm primarily a Java developer. I've met quite a few Java devs who love AOP. I've also been seeing more and more AOP "design patterns" emerging recently that seem to be fairly widely adopted. Even so, I'm still not convinced that AOP in OO code is a good idea in general, for a couple of reasons.

  1. It adds "magic" to the code in the
    form of opaque complexity that can
    be extremely hard to debug, and can
    make it extremely hard to debug
    object oriented code that it
    affects.

  2. It seems to me to be mostly
    unnecessary, and (worse) often used
    to avoid having to design well, or
    to compensate for previous poor
    design.

Here is an example that I've been seeing a lot of over the past couple of years, as a background for my question.

Before AOP (from the Hibernate docs)

public void saveMyEntityToTheDatabase(MyEntity entity) {
    EntityTransaction tx = null;
    try {
        tx = entityManager.getTransaction();
        tx.begin();
        entityManager.persist(entity);
        tx.commit();
    } catch (RuntimeException e) {
        if(tx != null && tx.isActive()) {
            tx.rollback();
        }
        throw e;
    }
}

After AOP

@Transactional
public void saveMyEntityToTheDatabase(MyEntity entity) {
    entityManager.persist(entity);
}

It seems like an obvious win for AOP to a lot of people. To me the original problem is symptomatic of inconsistent levels of API abstraction. That is, the EntityManager is a lot lower level than the business-level API of the message using it. This problem can be solved with a more appropriate level of abstraction, and a better (OO) design.

An OO Solution

public void saveMyEntityToTheDatabase(MyEntity entity) {
    database.performInTransaction(new Save(entity));
}

This solution assumes that the database object contains the same kind of transactional logic that the aspect responsible that manages @Transactional methods. This addresses my concerns above by making it more obvious that there is something managing the interaction with the EntityManager, and not introducing another programming paradigm.

So finally, my question: what can AOP do that OOP can't? I'm slightly convinced of its usefulness in trace logging, and maybe default toString() implementations or something similar, but I'd love to know if anyone's found it to be significantly better than OO for particular types of problems.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

本宫微胖 2024-11-13 02:45:58

AOP 是面向对象的;方面对象。

我不明白为什么会有非此即彼的心态。

AOP 是解决链式、跨领域问题(例如日志记录、安全性、事务、远程代理等)的完美选择。

更新:

我认为 OP 提出的批评是主观的,并不像所述的那样普遍普遍。没有证据的断言可以在没有证据的情况下被驳回。

我不相信使用魔法,但如果你理解的话,AOP 就不是魔法。我明白了。也许OP没有。如果是这样,并且 OP 对 OO 解决方案更满意,我会说继续使用它。

“在我看来没有必要”只是一个观点,没有任何证据。除了“我不同意”之外没有其他答案。

我认为 AOP 非常适合这些情况,因为我可以以声明方式应用它。我可以编写一个方面类一次,然后通过细粒度控制将其应用到许多地方,在配置而不是代码中更改它。我可以选择哪些方法、类和包在配置中应用了某个方面。

尝试使用手写的面向对象方法。

此外,AOP是面向对象的。您可以将其视为一个聪明的人为您提供特定于领域的语言或框架来完成您想要手动完成的事情。共同特征已被抽象为更通用的东西。为什么会有人反对呢?

AOP is OO; Aspects are objects.

I don't understand why the either/or mentality.

AOP is the perfect choice for chained, cross-cutting concerns (e.g. logging, security, transactions, remote proxying, etc.)

UPDATE:

I think the criticisms offered by the OP are subjective and not as universally widespread as stated. Assertions stated without proof can be dismissed without proof.

I don't believe in using magic, but AOP is not magic if you understand it. I understand it. Perhaps the OP does not. If that's the case, and the OP is more comfortable with an OO solution, I'd say go for it.

"Seems to me to be unnecessary" is a mere opinion, offered without proof. There's no answer to that except "I disagree."

I think AOP is perfect for those cases because I can apply it in a declarative way. I can write an aspect class once and apply it in many places with fine-grained control, changing it in configuration rather than code. I can pick and choose which methods, classes, and packages have an aspect applied to them in configuration.

Try that with a hand-written OO approach.

Besides, AOP is object-oriented. You can look at it as a smart person giving you a domain-specific language or framework for what you want to do by hand. The common features have been abstracted out into something more general. Why would anyone object to that?

小女人ら 2024-11-13 02:45:58

简短的回答是……什么也没有。不过,AOP 增加了一点我们在美国海军陆战队服役时所说的 FM 的内容,当为平民观众清理时,它的意思是“怪异的魔法”。你是对的,在你引用的第一个案例中绝对没有取得任何在第二个案例中没有实现的成就。我认为该运动背后的主要原因是清晰度问题,以及代码中“少一点仪式”的口号。因此,您可以编写代码来处理事务,或者使用供应商提供的 AOP 来省去仪式,或者容器可能比您手工编写的代码经过更好的测试。 AOP 的另一点是,它可以在部署描述符、Spring 配置文件等中进行更改,并且如果您的需求发生变化,也可以对其进行更改,而无需对实际代码进行任何更改。因此,您手写的昂贵代码继续表达您想要付费执行的业务逻辑,而“FM”层则通过大量 AOP 精灵灰尘来处理事务、日志记录等事务。

当然是YMMV。

The short answer is ... nothing. AOP though adds a pinch of what we would refer to back in my days in the US Marines as FM, which when cleaned up for a civilian audience means "Freaking Magic". You're right that there's absolutely nothing achieved in the first case you cite that isn't achieved in the second. I think the main reasoning behind the movement is a question of clarity, and the growing mantra of "Less ceremony" in code. So, you can write code in order to handle the transactions, or dispense with the ceremony with AOP, which being provided by the vendor, or container is presumably better tested than the code you are writing by hand. Another point in AOP's favor is that it CAN be changed in the deployment descriptors, Spring configuration files etc, and can then be altered if your requirements change without any changes to your actual code. So you hand written expensive code continues to express the business logic you meant are paid to do, and the "FM" layer handles things like transactions, logging, etc with a liberal sprinkling of AOP pixie dust.

YMMV of course.

为人所爱 2024-11-13 02:45:58

对我来说,AOP 是拦截器模式的简写。拦截器模式本身是从模板方法、AFAICS 派生(或影响或得到这个想法)的。

Interceptor 的一个流行示例是 Servlet Filter。我们知道这些在很多情况下都非常有用。

由于所有这些模式都是有用的,因此从这些模式派生的 AOP 也很有用。正如您自己所说的它的一些用法。

To me AOP is a shorthand of Interceptor Pattern. And Interceptor Pattern itself is derived (or influenced or got the idea) from Template Method, AFAICS.

A popular example of Interceptor is Servlet Filter. And we know those are pretty useful in many cases.

Since, all these patterns are useful, hence AOP, which is derived from these is also useful. And as you yourself stated few of its usages.

扛刀软妹 2024-11-13 02:45:58

一般来说,所有问题的形式都是“什么可以做,什么不能做?”是没有意义的。所有通用语言都同样强大(请参阅:Church 的论文)。

因此,语言之间的区别不在于它们能做什么,而在于它们如何做。换句话说,你需要做多少工作才能在一种语言中获得某些行为,而在另一种语言中需要花费多少工作才能获得相同的行为。

In general, all question of the form "What can do that cant?" are meaningless. All general purpose languages are equally powerful (See: Church's Thesis).

The difference between languages is, therefore, not in what they can do but rather in how they do it. In other words, how much work do you have to do get some behavior in one language, vs. how much work to get that same behavior in some other language.

可爱暴击 2024-11-13 02:45:58

面向方面编程与面向对象编程

Mecki 的回答AOP 是独一无二的;-)

Aspect Oriented Programming vs. Object-Oriented Programming

The answer of Mecki about AOP is an exclusive one ;-)

风流物 2024-11-13 02:45:58

到目前为止,对我来说,AOP 绝对优于 OOP 的唯一用例是方法调用跟踪。

For me so far, the only use case, for which AOP is definetely better than OOP is method call tracing.

凉风有信 2024-11-13 02:45:58

我很不幸不得不在一个使用普通 OOP 完成服务调用的应用程序中工作。我讨厌它,我将告诉您原因:

首先,您的示例有点简单,因为通常事务边界不是围绕单个数据库交互,而是围绕整个业务服务调用。因此,为了示例,让我们考虑以下方法:

@Transactional
public Employee hire(Person p, Employee manager, BigDecimal salary) {
    // implementation omitted
}

这是使用调用的方法

Employee hired = service.hire(candidate, manager, agreedOnSalary);

在您的情况下,这将变成:

class HireAction implements Callable<Employee> {
    private final Person person;
    private final Employee manager;
    private final BigDecimal salary;

    public HireAction(Person person, Employee manager, BigDecimal salary) {
        this.person = person;
        this.manager = manager;
        this.salary = salary;
    }

    @Override
    public Employee call() throws Exception {
        // implementation omitted
    }
}

并由构造函数调用 构造

Employee hired = session.doInTransaction(new HireAction(candidate, manager, agreedOnSalary));

函数对于确保分配所有参数是必要的(因此如果添加参数,编译器可能会抱怨到一个方法而不更新调用者)。

第二种方法较差,原因如下:

  1. 它违反了 DRY,因为每个参数被提及 3 次,返回类型被提及两次。特别是,您将参数的 JavaDoc 放在哪里?你也抄袭吗?
  2. 这使得将相关服务操作分组到服务中并在它们之间共享代码变得困难。是的,您可以将所有相关操作放在同一个包中,并有一个公共超类来共享代码,但这比简单地将它们放在同一个类中的 AOP 方法要冗长得多。或者你可以做一些疯狂的事情,比如:

    类 HRService {
        类 HireAction {
            // 省略 impl
        }
        类 FireAction {
            // 省略 impl
        }
    }
    

    并使用调用它

    Employee emp = session.doInTransaction(new HRService().new HireAction(候选人、经理、薪水));
    
  3. 应用程序程序员可能会忘记启动事务:

    来 调用它

    Employee e = new HireAction(候选人, 经理, 工资).call();
    

    或者在错误的会话/数据库上启动事务。事务和业务逻辑是不同的关注点,通常由不同的开发人员解决,因此应该分开。

总而言之,普通的 OOP 方法更加冗长且容易出错,导致开发和维护期间的成本增加。

最后,关于您对 AOP 的批评:

它以不透明的复杂性的形式为代码添加了“魔力”,这种复杂性非常难以调试,

无论来源如何,复杂性总是难以调试。我记得一些对 hibernate 源代码的调试会话,其对命令模式的明智使用使得找到重要的代码变得同样困难。

AOP 拦截器的存在可能是不明显的(尽管如果一个方法被注释为@Transactional,我认为它是显而易见的),这就是为什么应该谨慎使用 AOP(这不是问题)因为项目中跨领域关注点的数量通常很小)。

并且会使调试受其影响的面向对象代码变得极其困难。

为何如此?我没有看到这个问题,但如果你要描述它,我可能会告诉你我如何解决/避免它。

在我看来,这几乎是不必要的,而且(更糟糕的是)经常被用来避免必须进行良好的设计,或者弥补以前糟糕的设计。

任何技术都可能用得不好。重要的是用好它有多难,以及用好后我们能取得什么成果。

I have had the misfortune of having to work in an application where service calls were done using plain OOP. I hated it, and I am going to tell you why:

First, your example is somewhat simplistic, as usually the transaction boundary is not around a single database interaction, but around the entire business service invocation. So let's for the sake of the example consider the following method:

@Transactional
public Employee hire(Person p, Employee manager, BigDecimal salary) {
    // implementation omitted
}

which is invoked using

Employee hired = service.hire(candidate, manager, agreedOnSalary);

In your case, this would become:

class HireAction implements Callable<Employee> {
    private final Person person;
    private final Employee manager;
    private final BigDecimal salary;

    public HireAction(Person person, Employee manager, BigDecimal salary) {
        this.person = person;
        this.manager = manager;
        this.salary = salary;
    }

    @Override
    public Employee call() throws Exception {
        // implementation omitted
    }
}

and invoked by

Employee hired = session.doInTransaction(new HireAction(candidate, manager, agreedOnSalary));

The constructor is necessary to ensure that all parameters are assigned (so the compiler can complain if a parameter is added to a method without updating a caller).

The second approach is inferior for the following reasons:

  1. It violates DRY, as each parameter is mentioned 3 times, and the return type twice. In particular, where do you put JavaDoc for the parameters? Do you copy that, too?
  2. It makes it hard to group related service operations in a service, and share code among them. Yes, you can put all related operations in the same package, and have a common superclass to share code, but again this is rather more verbose that the AOP approach of simply putting them in the same class. Or you could do crazy things like:

    class HRService {
        class HireAction {
            // impl omitted
        }
        class FireAction {
            // impl omitted
        }
    }
    

    and invoke it using

    Employee emp = session.doInTransaction(new HRService().new HireAction(candidate, manager, salary));
    
  3. An application programmer can forget to start the transaction:

    Employee e = new HireAction(candidate, manager, salary).call();
    

    or start the transaction on the wrong session / database. Transactions and business logic are different concerns, usually solved by different developers, and hence should be separated.

To summarize, the plain OOP approach is more verbose and error prone, leading to increased costs during both development and maintenance.

Finally, about your critism of AOP:

It adds "magic" to the code in the form of opaque complexity that can be extremely hard to debug,

Complexity is always hard to debug, regardless of origin. I remember some debugging sessions with the hibernate source code, whose judicious use of the command pattern made it no less hard to find the code that mattered.

The presence of an AOP interceptor can be non-obvious (though if a method is annotated @Transactional, I'd consider it obvious), which is why AOP should be used sparingly (which is not a problem as the number of cross-cutting concerns in a project is usually quite small).

and can make it extremely hard to debug object oriented code that it affects.

How so? I don't see the problem, but if you were to describe it, I could probably tell you how I solve / avoid it.

It seems to me to be mostly unnecessary, and (worse) often used to avoid having to design well, or to compensate for previous poor design.

Every technology can be used poorly. What matters is how hard it is to use well, and what we can accomplish if we do.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文