2011-05-11 76 views
31

我主要是一個Java開發人員。我遇到了不少喜愛AOP的Java開發人員。我也看到越來越多的最近出現的AOP「設計模式」似乎被廣泛採用。即便如此,我仍然不相信OO代碼中的AOP通常是一個好主意,原因有兩個。OOP不能做什麼?

  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的交互,而不是引入另一種編程範例。

最後,我的問題是:OOP不能做什麼?我略微確信它在跟蹤記錄中的實用性,並且可能是默認toString()實現或類似的,但我想知道是否有人發現它對於特定類型的問題比OO好得多。

+1

帶有字節碼修改的AoP可以例如透明地將代碼添加到(例如)第三方庫而不需要修改源碼。 – 2011-05-11 11:11:07

+1

@Johan,......這使得AOP編譯器成爲一個出色的黑客工具。 – weekens 2011-05-11 11:18:14

+0

@weekens,正是 - 使其不是OOP的替代品,而是一種方便的方法來應用OOP,有時候是通過黑客。 – 2011-05-11 11:20:24

回答

10

簡短的答案是什麼都沒有。 AOP雖然增加了我們在美國海軍陸戰隊作爲FM時代中的一小部分,但當爲清晰的平民觀衆進行清理時,這意味着「Freaking Magic」。你說得對,在你引用的第一種情況下,在第二種情況下沒有實現,這是完全沒有實現的。我認爲這個運動背後的主要理由是清晰的問題,以及代碼中「減少儀式」的不斷增長的口號。因此,您可以編寫代碼來處理事務,或者免除與供應商提供的AOP相關的典禮,或者可以比您手動編寫的代碼更好地測試容器。 AOP支持的另一點是它可以在部署描述符,Spring配置文件等中進行更改,如果您的需求發生更改而不更改實際代碼,則可以對其進行更改。所以你手寫昂貴的代碼繼續表達你意味着付費的業務邏輯,而「FM」層用自由噴灑的AOP精靈灰塵來處理交易,日誌記錄等事情。

YMMV當然。

+0

我喜歡這個答案。關於供應商開發的方面,你有一個很好的觀點,我傾向於認爲我只會使用AOP來處理需要神奇的東西。不過,在這方面,我非常滿意地[麻瓜](http://en.wikipedia.org/wiki/Muggle)。 – Daniel 2011-05-12 06:15:16

12

AOP是OO;方面對象。

我不明白爲什麼要麼/或心態。

AOP是鏈接,交叉問題(例如日誌記錄,安全性,交易,遠程代理等)的完美選擇。)

UPDATE:

我認爲由OP提供的批評是主觀的,不是普遍廣泛的說明。沒有證據的陳述可以在沒有證據的情況下被駁回。

我不相信使用魔法,但如果你理解它,AOP並不是魔術。我明白。也許OP沒有。如果是這樣的話,OP對OO解決方案更加舒適,我會說去做。

「似乎對我來說不必要」僅僅是一種意見,沒有證據就提供。除了「我不同意」之外,沒有任何答案。

我認爲AOP適用於這些情況,因爲我可以將它應用於聲明式方式。我可以編寫一個方面類,並在許多地方使用細粒度的控件,在配置而不是代碼中進行更改。我可以在配置中挑選哪些方法,類和包有一個方面應用於它們。

嘗試用手寫的OO方法。

此外,AOP 面向對象。您可以將其視爲一個聰明的人,爲您提供特定領域的語言或框架,以實現您想要手動完成的任務。共同的特徵已經被抽象成更一般的東西。爲什麼會有人反對呢?

+0

AOP是OO?無法使用AOP脫離OOP範圍?例如在功能編程? – 2011-05-11 12:16:37

+0

我的經驗是方面裝飾物體。如果你的函數式語言將函數當作第一類對象,那麼這是可能的。 Python是一個混合:功能/面向對象。它的裝飾器可以應用於函數或對象方法。 – duffymo 2011-05-11 13:05:25

+0

我並沒有真正發現以這種方式查看AOP非常有用:它對我來說看起來是一個完全不同的範例。你會說任何理由說這是「完美」的選擇(關於這個問題的兩個批評)? – Daniel 2011-05-12 05:56:26

8

對我而言AOP是Interceptor Pattern的簡寫。攔截器模式本身是從Template Method,AFAICS派生(或影響或得到的想法)。

Interceptor的流行示例是Servlet Filter。我們知道這些在很多情況下非常有用。

因爲所有這些模式都是有用的,所以從這些派生出來的AOP也是有用的。正如你自己所說的,它的用法很少。

+0

公平點。我不太喜歡Interceptor模式:我認爲servlet過濾器真的很有用。例如,如果servlet的生命週期在我開發的應用程序中發生變化,我希望它少得多。 AOP有這種危險。除非是非常高級的,或者非常低級的,不變的功能,否則我會非常猶豫。 – Daniel 2011-05-12 06:03:41

6

一般來說,所有形式的問題「什麼可以做到這一點?」沒有意義。所有通用語言都同樣強大(請參閱:Church's Thesis)。

語言的區別,因此,不他們可以做什麼而是如何他們這樣做。換句話說,你需要做多少工作才能在一種語言中獲得某些行爲,而不需要多少工作來獲得某種其他語言的相同行爲。

+0

這對我來說看起來很短暫。根據我的經驗,更少的打字人物並不能轉化爲「你需要做多少工作才能獲得某些行爲」。可維護性是在代碼中花費的時間的更好預測器。 – Daniel 2011-05-12 06:06:20

+0

我沒有說你應該算#字符。在任何你喜歡的時間段內,選擇你喜歡的努力/成本的任何定義。我的觀點是所有語言都是平等的。 – 2011-05-12 10:06:27

1

對我而言,迄今爲止,唯一的使用案例,其中AOP爲definetely好於OOP的是方法調用跟蹤。

+0

這也是我的經驗。我學會了AspectJ來實現它! – Daniel 2011-05-12 06:08:20

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

The con structor有必要確保所有參數都被賦值(所以如果參數被添加到方法而不更新調用者,編譯器可以投訴)。

第二種方法是低劣的原因如下:

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

    或在開始交易錯誤的會話/數據庫。事務和業務邏輯是不同的問題,通常由不同的開發人員解決,因此應該分開。

總之,簡單的面向對象方法更加冗長且容易出錯,導致開發和維護過程中的成本增加。

最後,關於你的AOP的critism:

它增加了「神奇」在不透明的複雜性可能會非常難於調試代碼的形式,

複雜性始終是硬無論來源如何調試。我記得一些關於hibernate源代碼的調試會話,它們明智地使用了命令模式,使得查找重要代碼變得很難。

AOP攔截器的存在可能是不明顯的(儘管如果一個方法註釋@Transactional,我會認爲它很明顯),這就是爲什麼AOP應該謹慎使用(這不是一個問題,因爲數量項目中的交叉擔憂通常很小)。

並且可以很難調試它所影響的面向對象代碼。

是怎麼回事?我沒有看到問題,但如果你要描述它,我可能會告訴你我是如何解決/避免它的。

在我看來,大多數情況下是不必要的,而且(更糟糕的是)經常用於避免設計不當,或者補償以前糟糕的設計。

每種技術都可以使用不佳。重要的是要善用它有多困難,以及如果我們做得好,我們能做些什麼。

+0

我同意:服務層API的示例失敗。我的意圖只是描述一種簡單化的方法。我認爲你對Hibernate和糟糕的設計提出了一些看法。 – Daniel 2011-06-09 03:04:00