AOP能做什么OOP不能做?

时间:2011-05-11 11:02:29

标签: java oop aop

我主要是一名Java开发人员。我遇到了很多喜欢AOP的Java开发者。我也看到越来越多的AOP“设计模式”最近出现,似乎被广泛采用。即便如此,由于几个原因,我仍然不相信OO代码中的AOP一般是个好主意。

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

  2. 在我看来,大多数情况下 不必要的,并且(更糟)经常使用 避免设计好,或 以弥补以前的穷人 设计。

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

    在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明显更好特殊类型的问题。

7 个答案:

答案 0 :(得分:12)

AOP是OO;方面对象。

我不明白为什么要么/或心态。

AOP是链接,跨领域关注点(例如日志记录,安全性,事务,远程代理等)的完美选择。

更新:

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

我不相信使用魔法,但如果你了解它,AOP并不神奇。我明白了。也许OP没有。如果是这种情况,并且OP对OO解决方案更为满意,那我就说去吧。

“似乎对我来说是不必要的”仅仅是一种意见,没有证据。除了“我不同意”之外,没有答案。

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

尝试用手写的OO方法。

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

答案 1 :(得分:10)

简短的回答是......什么都没有。 AOP虽然增加了我在美国海军陆战队作为FM的日子里所提到的一些内容,当为平民观众清理时意味着“怪异的魔法”。你是对的,在你引用的第一种情况下,第二种情况并没有实现。我认为这一运动背后的主要原因是一个清晰的问题,以及代码中“少礼”的颂歌。因此,您可以编写代码来处理交易,或者免除由供应商提供的AOP的仪式,或者容器可能比您手动编写的代码更好地测试。 AOP有利的另一点是它可以在部署描述符,Spring配置文件等中进行更改,然后如果您的需求发生更改而不更改实际代码,则可以更改它。因此,您手写昂贵的代码将继续表达您需要支付的业务逻辑,并且“FM”层处理交易,日志记录等事务,并自由地使用AOP小精灵粉尘。

YMMV当然。

答案 2 :(得分:8)

对我而言AOP是Interceptor Pattern的简写。拦截器模式本身是从Template Method,AFAICS派生(或影响或得到了想法)。

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

因为所有这些模式都是有用的,因此从这些模式衍生的AOP也是有用的。正如你自己所说的那样,它的用法很少。

答案 3 :(得分:6)

一般来说,所有形式的问题“什么不能做?”毫无意义。所有通用语言都同样强大(参见:Church's Thesis)。

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

答案 4 :(得分:2)

Aspect Oriented Programming vs. Object-Oriented Programming

Mecki关于AOP的答案是独家的; - )

答案 5 :(得分:1)

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

答案 6 :(得分:1)

我不幸在一个使用普通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方法只是简单地将它们放在同一个类中就更加冗长了。或者你可以做一些疯狂的事情:

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

    并使用

    调用它
    Employee emp = session.doInTransaction(new HRService().new HireAction(candidate, manager, salary));
    
  3. 应用程序员可能忘记启动事务:

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

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

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

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

      

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

    无论原点如何,复杂性始终难以调试。我记得一些使用hibernate源代码的调试会话,他们明智地使用命令模式使得找到重要的代码同样困难。

    AOP拦截器的存在可能是不明显的(虽然如果一个方法注释@Transactional,我认为这很明显),这就是为什么应该谨慎使用AOP(这不是问题,因为项目中贯穿各领域的问题通常很少。)

      

    并且可能使调试它所影响的面向对象的代码变得非常困难。

    怎么样?我没有看到问题,但如果你要描述它,我可能会告诉你我是如何解决/避免的。

      

    在我看来,大多数情况下都是不必要的,并且(更糟糕的是)经常习惯于避免设计好,或者补偿之前糟糕的设计。

    每种技术都可以使用得很差。重要的是如何使用得好,以及如果我们这样做,我们可以做些什么。