Что может АОП сделать то, что ООП не может сделать? - PullRequest
31 голосов
/ 11 мая 2011

Я в первую очередь разработчик Java. Я встречал довольно много разработчиков Java, которые любят AOP. Я также вижу, что в последнее время появляется все больше и больше «шаблонов» проектирования АОП, которые, похоже, получили широкое распространение. Несмотря на это, я все еще не убежден, что AOP в ОО-коде является хорошей идеей в целом по нескольким причинам.

  1. Добавляет "магию" к коду в форма непрозрачной сложности, которая может быть чрезвычайно трудно отлаживать, и может сделать его чрезвычайно сложным для отладки объектно-ориентированный код, который он влияет.

  2. Мне кажется, в основном ненужный и (что еще хуже) часто используемый чтобы избежать необходимости хорошо проектировать, или чтобы компенсировать предыдущие бедные дизайн.

Вот пример, который я видел много за последние пару лет, в качестве фона для моего вопроса.

До АОП (из документов 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;
    }
}

После АОП

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

Для многих АОП кажется очевидной победой АОП. Для меня исходная проблема является симптомом несовместимых уровней абстракции API. Таким образом, EntityManager - намного более низкий уровень, чем API бизнес-уровня сообщения, использующего его. Эта проблема может быть решена с помощью более подходящего уровня абстракции и лучшего (ОО) дизайна.

OO Solution

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

В этом решении предполагается, что объект database содержит ту же логику транзакций, что и ответственный аспект, управляющий методами @Transactional. Это решает мои проблемы выше, делая более очевидным, что есть что-то, управляющее взаимодействием с EntityManager, и не вводящее другую парадигму программирования.

Итак, наконец, мой вопрос: что может АОП делать, чего не может ООП? Я немного убежден в его полезности для ведения журнала трассировки и , может быть по умолчанию toString() реализаций или чего-то подобного, но я хотел бы знать, если кто-то обнаружил, что он значительно лучше, чем ОО для определенных типов проблемы.

Ответы [ 7 ]

12 голосов
/ 11 мая 2011

АОП - ОО; Аспекты являются объектами.

Я не понимаю, почему или / или менталитет.

AOP - идеальный выбор для комплексных, сквозных задач (например, ведение журнала, безопасность, транзакции, удаленное проксирование и т. Д.)

UPDATE:

Я думаю, что критика, предложенная ФП, субъективна и не так широко распространена, как указано. Утверждения, изложенные без доказательств, могут быть отклонены без доказательств.

Я не верю в использование магии, но АОП не магия, если вы понимаете это. Я понимаю, это. Возможно, ОП не делает. Если это так, и OP удобнее с OO-решением, я бы сказал, пойти на это.

«Мне кажется ненужным» - это простое мнение, которое предлагается без доказательств. На это нет ответа, кроме «Я не согласен».

Я думаю, что AOP идеально подходит для этих случаев, потому что я могу применить его 101 * * декларативным способом. Я могу написать класс аспектов один раз и применять его во многих местах с детальным контролем, изменяя его в конфигурации, а не в коде. Я могу выбрать, какие методы, классы и пакеты имеют аспект, применяемый к ним в конфигурации.

Попробуйте это с помощью рукописного ОО подхода.

Кроме того, AOP является объектно-ориентированным. Вы можете смотреть на это как на умного человека, который дает вам предметно-ориентированный язык или структуру для того, что вы хотите сделать вручную. Общие черты были обобщены в нечто более общее. Зачем кому-то возражать против этого?

10 голосов
/ 11 мая 2011

Краткий ответ ... ничего. AOP, тем не менее, добавляет часть того, что мы называли в мои дни в США морскими пехотинцами как FM, что при очистке для гражданской аудитории означает «Волшебство магии». Вы правы, что в первом случае абсолютно ничего не достигнуто, а во втором - нет. Я думаю, что главной причиной движения является вопрос ясности и растущей мантры «Меньше церемоний» в коде. Таким образом, вы можете написать код для обработки транзакций или обойтись без церемонии с AOP, который предоставляется поставщиком, или контейнер, по-видимому, лучше протестирован, чем код, который вы пишете вручную. Еще один момент в пользу AOP заключается в том, что он МОЖЕТ быть изменен в дескрипторах развертывания, файлах конфигурации Spring и т. Д., А затем может быть изменен, если ваши требования изменятся без каких-либо изменений в вашем реальном коде. Таким образом, написанный вами дорогой код продолжает выражать бизнес-логику, за которую вы платили, а слой «FM» обрабатывает такие вещи, как транзакции, ведение журналов и т. Д. С обильным разбросом пыли в АОП.

YMMV конечно.

8 голосов
/ 11 мая 2011

Для меня АОП это сокращение от Шаблон перехватчика . И сам образец перехватчика получен (или под влиянием, или получил идею) из Шаблонный метод , AFAICS.

Популярным примером Interceptor является Servlet Filter. И мы знаем, что они довольно полезны во многих случаях.

Поскольку все эти шаблоны полезны, следовательно, АОП, который является производным от них, также полезен. И, как вы сами сказали, несколько из его использования.

6 голосов
/ 11 мая 2011

В общем, все вопросы формы "Что может сделать то, что не могу?"бессмысленны.Все языки общего назначения одинаково сильны (см .: Тезис Черча ).

Разница между языками, следовательно, не в , что они могут сделать, а скорее в как они это делают.Другими словами, сколько работы вам нужно сделать, чтобы получить какое-то поведение на одном языке, и сколько работы нужно, чтобы получить такое же поведение на каком-то другом языке.

2 голосов
/ 11 мая 2011
1 голос
/ 29 мая 2011

Мне не повезло из-за необходимости работать в приложении, где вызовы службы выполнялись с использованием простого ООП.Я ненавидел это и собираюсь рассказать вам, почему:

Во-первых, ваш пример несколько упрощен, так как обычно граница транзакции не связана с одним взаимодействием с базой данных, а вокруг всего вызова бизнес-службы.Итак, давайте для примера рассмотрим следующий метод:

@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. Сложно группировать связанные сервисные операции в сервисе и обмениваться между ними кодом.Да, вы можете поместить все связанные операции в один и тот же пакет и иметь общий суперкласс для совместного использования кода, но опять же, это более многословно, чем подход АОП, заключающийся в простом размещении их в одном классе.Или вы можете сделать такие сумасшедшие вещи, как:

    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();
    

    или запустите транзакцию в неправильном сеансе / базе данных.Транзакции и бизнес-логика - это разные проблемы, которые обычно решаются разными разработчиками и, следовательно, должны быть разделены.

Подводя итог, можно сказать, что простой ООП-подход более многословен и подвержен ошибкам, что приводит к увеличению затрат как на разработку, так и на обслуживание.

Наконец, о вашей критике АОП:

Он добавляет «магию» к коду в виде непрозрачной сложности, которую очень трудно отладить,

Сложность всегда трудно отладить, независимо от источника,Я помню некоторые сеансы отладки с исходным кодом гибернации, чье разумное использование шаблона команд сделало не менее трудным поиск кода, который имел значение.

Наличие перехватчика AOP может быть неочевидным (хотя, если метод аннотирован @Transactional, я бы посчитал это очевидным), поэтому AOP следует использовать экономно (что не является проблемой)так как количество сквозных задач в проекте обычно довольно мало).

и может очень затруднить отладку объектно-ориентированного кода, на который он влияет.

Как так?Я не вижу проблемы, но если бы вы описали ее, я мог бы, вероятно, сказать вам, как я ее решаю / избегаю.

Мне кажется, что это в основном не нужно, и (что еще хуже)часто используется для того, чтобы избежать необходимости хорошего дизайна или для компенсации предыдущего плохого дизайна.

Любая технология может быть использована плохо.Важно то, насколько сложно использовать хорошо, и что мы можем сделать, если мы это сделаем.

1 голос
/ 11 мая 2011

Для меня пока единственным вариантом использования, для которого AOP определенно лучше, чем OOP, является трассировка вызова метода.

...