Java常用設計模式

設計原則

  • 找出應用中可能需要變化之處,把它們獨立出來,不要和那些不需要變化的代碼混在一起,把會變化的部分取出並”封裝”起來,以便以後可以輕易地改動或擴充此部分,好讓其他部分不會受到影響。

  • 針對接口編成,而不是針對實現編成
    這樣的做法異於以往,以前的做法是:行爲來自超類的具體實現,或是繼承某個接口並由子類自行實現而來。這兩個做法都是依賴於”實現”,我們被實現綁得死死的,沒辦法更改行爲(除非寫更多的代碼)

“針對接口編程”真正的意思是”針對超類型(supertype)編程”

這裏所謂的”接口”有多個含義,接口是一個”概念”,也是一種Java的interface構造,你可以在不涉及Java interface的情況下,”針對接口編程”,關鍵就在多態。利用多態,程序可以針對超類型編程,執行時會根據實際狀況執行到真正的行爲,不會被綁死在超類型的行爲上。”針對超類型編程”這句話,可以更明確地說成”變量的聲明類型應該是超類型”,通常是一個抽象類或者是一個接口,如此,只要是具體實現此超類型的類所產生的對象,都可以指定給這個變量。這也意味着,聲明類時不用理會以後執行時的真正對象類型

  • 多用組合,少用繼承

當你將兩個類結合起來使用,這就是組合(composition)。這種做法和”繼承”不同的地方在於,鴨子的行爲不是繼承來的,而是和適當的行爲對象”組合”來的,如你所見,使用組合建立系統具有很大的彈性,不僅可將算法族封裝成類,更可以”在運行時動態地改變行爲”,只要組合的行爲對象符合正確的接口標準即可利用繼承設計子類的行爲,是在編譯時靜態決定的,而且所有的子類都會繼承到相同的行爲。然而,如果能夠利用組合的做法擴展對象的行爲,就可以在運行時動態地進行擴展

策略模式

定義了算法族,分別封裝起來,讓它們之間可以互相替換,此模式讓算法的變化獨立於使用算法的客戶。

設計原則

爲了交互對象之間的鬆耦合設計而努力
鬆耦合的設計之所以能讓我們建立有彈性的OO系統,能夠應對變化,是因爲對象之間的互相依賴降到了最低。

觀察者模式

定義了對象之間的一對多依賴,這樣一來,當一個對象改變狀態時,它的所有依賴者都會收到通知並自動更新。關於觀察者的一切,主題只知道觀察者實現了某個接口(也就是Observer接口),主體不需要知道觀察者的具體類是誰,做了些什麼或其他任何細節。
任何時候我們都可以增加新的觀察者,因爲主題唯一依賴的東西是一個實現Observer接口的對象列表,所以我們可以隨時增加觀察者。事實上,在運行時我們可以用新的觀察者取代現有的觀察者,主題不會受到任何影響。同樣的,也可以在任何時候刪除某些觀察者。

有新類型的觀察者出現時,主題的代碼不需要修改。假如我們有個新的具體類需要當觀察者,我們不需要爲了兼容新類型而修改主題的代碼,所有要做的就是在新的類裏實現此觀察者接口,然後註冊爲觀察者即可。主題不在乎別的,它只會發送通知給所有實現了觀察者接口的對象。

改變主題或觀察者其中一方,並不會影響另一方,因爲兩者是鬆耦合的,所以只要它們之間的接口仍被遵守,我們就可以自由地改變它們。

關於Java自帶的觀察者模式 java.util.Observable

  • java.util.Observable的黑暗面
    可觀察者是一個”類”而不是一個”接口”,更糟的是,它甚至沒有實現一個接口。不幸的是,java.util.Observable的實現有許多問題,
    限制了它的使用和複用,這並不是說它沒有提供有用的功能,我們只是想提醒大家注意一些事實

  • Observable是一個類
    首先,因爲Observable是一個”類”,你必須設計一個類繼承它。如果某類想同時具有Observable類和另一個超類的行爲,就會陷入兩難,
    畢竟Java是不支持多繼承的,這限制了Observable的複用能力。

  • 再者,因爲沒有Observable接口,所以你無法建立自己的實現。和java內置的Observable API搭配使用,也無法將java.uril的實現換成
    另一套做法的實現。比如說下面這個

  • Observable將關鍵的方法保護起來
    如果你看看Observable API,你會發現setChanged()方法被保護起來了,這意味着,除非你繼承自Observable,否則你無法創建Observable實
    例並組合到你自己的對象中來,這個設計違反了第二個設計原則”多用組合,少用繼承”

Summary

  • 1)觀察者模式定義了對象之間一對多的個系
  • 2)主題(也就是可觀察者)用一個共同的接口來更新觀察者
  • 3)觀察者和可觀察者之間用耦合方式結合(loosecoupling),可觀察者不知道觀察者的細節,只知道觀察者實現了觀察者接口
  • 4)使用此模式時,你可從被觀察者處推(push)或拉(pull)數據(然而,推的方式被認爲更”正確”)
  • 5)有多個觀察者時,不可以依賴特定的通知次序
  • 6)Java有多種觀察者模式的實現,包括了通用的java.util.Observable
  • 7)要注意java.util.Observable實現上所帶來的一些問題
  • 8)如果有必要的話,可以實現自己的Observable,這並不難,不要害怕
  • 9)Swing大量使用觀察者模式,許多GUI框架也是如此
  • 10)此模式也被應用在許多地方,例如:JavaBeans,RMI

設計原則

1)類應該對擴展開放,對修改關閉(開放-關閉原則)
裝飾者模式完全遵循開放-關閉原則,

裝飾者模式

動態地將責任附加到對象上。若要擴展功能,裝飾者提供了比繼承更有彈性的替代方案

Java IO裏的裝飾者模式:
+ 1) LineNumberInputStream 加上了計算行數的能力
+ 2) BufferedInputStream 加入兩種行爲:利用緩衝輸入來改進性能;用一個readLine()方法(用來一次讀取一行文本輸入數據)
來增強接口

BufferedInputStream和LineNumberInputStream都擴展自FilterInputStream,而FilterInputStream是一個抽象的裝飾類

FAQ

F:如果我將代碼針對特定種類的具體組件(例如House-Blend),做一些特殊的事(例如,打折),我擔心這樣的設計是否恰當。
因爲一旦用裝飾者包裝HouseBlend,就會造成類型改變

A:的確是這樣,如果你把代碼寫成依賴於具體的組件類型,那麼裝飾者就會導致程序出現問題。只有在針對抽象組件類型編程時,
纔不會因爲裝飾者而受到影響。但是,如果的確針對特定的具體組件編程,就應該重新思考你的應用架構,以及裝飾者是否適合

Summary

1)繼承屬於擴展形式之一,但不見得是達到彈性設計的最佳方式

2)在我們的設計中,應該允許行爲可以被擴展,而無須修改現有的代碼

3)組合和委託可用於在運行時動態地加上新的行爲

4)除了繼承,裝飾者模式也可以讓我們擴展行爲

5)裝飾者模式意味着一羣裝飾者類,這些類用來包裝具體組件

6)裝飾者反映出被裝飾的組件類型(事實上,他們具有相同的類型,都經過接口或繼承實現)

7)裝飾者可以在被裝飾者的行爲前面與/或後面加上自己的行爲,甚至將被裝飾者的行爲整個取代掉,而達到特定的目的

8)你可以用無數個裝飾者包裝一個組件

9)裝飾者一般對組件的客戶是透明的,除非客戶程序依賴於組件的具體類型

10)裝飾者會導致設計中出現很多小對象,如果過度使用,會讓程序變得很複雜

門面模式

意圖是將複雜的業務邏輯封裝到高層接口中,從而簡化了對子系統的訪問。其實現方式通常是將相關聯的方法調用組織到一起,然後在一個方法中按照順序來調用這些方法。從高層角度來看,每個API都可以看作是門面模式的一種實現,因爲它們都提供了用於隱藏複雜性的簡單接口。對API方法的任何調用都會導致隱藏在其後的衆多子系統方法的調用。javas.servlet.http.HttpSession接口就是門面的一個示例。它將維護會話相關的複雜邏輯隱藏起來,同時又通過少量易於使用的方法來公開其功能。

使用情形

門面模式常常用於如下目的與情形:
+ 爲遺留的後端系統提供簡單且統一的訪問
+ 爲類創建一個公開的API,如驅動程序
+ 爲可用服務提供粗粒度的訪問。服務會被組合,就像洗衣機一樣
+ 減少網絡調用。門面會對子系統發起多次調用,而遠程客戶端只對門面進行一次調用
+ 將應用的流程與內部細節封裝起來,從而提升安全性並簡化操作

注意,門面有時也被實現爲單例抽象工廠

實現門面模式並不複雜。它並沒有強制要求嚴格的結構或是規則集。爲複雜流程提供簡單訪問的任何方法都可以看成是門面模式的實現。

作用及好處

  • 降低了耦合度,因爲客戶端對子系統一無所知
  • 在變更時增強了可維護性與可管理性
  • 實現功能重用,因爲門面模式鼓勵控制重用以及細粒度邏輯
  • 每次方法調用都會調用相同的方法,從而確保了服務執行的一致性
  • 分組相關方法並從一個方法調用中來調用它們,從而降低業務邏輯複雜度
  • 中央化的安全與事務控制管理
  • 可測試性與可模擬性的模式實現

這種實現稱爲POJO門面,它與本章後面將要介紹的有狀態和無狀態實現是不同的

何時以及何處該使用門面模式

門面模式應該用於高層封裝複雜業務邏輯,並通過一個API提供簡潔的訪問單點。如果要爲其他人提供一個接口或API,請首先考慮邏輯的複雜性以及可能會出現的變更。門面模式在提供一個整潔的API的同時又隱藏掉可能會發生變化的部分方面很有優勢。不過,毫無必要地將方法包裝到門面中卻不是好的做法,還會增加不必要的層次。不當的封裝可能會導致太多的調用以及毫無價值的層次。在實現會話門面時,你需要確定用例是否需要維持狀態。只調用門面的一個方法來接受所需服務的用例是非對話式的,因此沒必要在方法調用間保持會話狀態。你應該將這種門面實現爲無狀態會話Bean。
另一方面,如果必須在方法調用間維持會話狀態,那麼最恰當的方式就是將這種門面實現爲有狀態會話Bean。你需要注意到有狀態會話門面的使用,因爲它會佔用服務器資源,直到客戶端觸發會話將其釋放掉或是超時。這意味着在大多數時間內,有狀態會話Bean門面會綁定到客戶端,但卻什麼都沒做;它僅維持着狀態並使用資源。與無狀態會話Bean門面不同的是,它無法在其他客戶端之間重用和共享,因爲每個請求都會創建無狀態門面的新實例,並且維持着該客戶端會話的狀態。
因此,在使用該模式的時候要小心,請仔細分析用例並做恰當的決定。

小結

可將門面模式實現爲POJO,無狀態會話Bean,或是有狀態會話Bean。實現門面模式的各種方法解決了不同用例場景下的不同問題。不過,各種實現並沒有背離其主要意圖:爲複雜的子系統提供一個高層次,簡單的接口。
在決定將門面實現爲有狀態會話Bean時要小心,請確保它導致資源消耗的問題。
設計良好的應用會充分利用門面模式來封裝複雜邏輯,並將子系統與客戶端解耦:不過,對門面模式的不當使用和過度使用會導致更加複雜,擁有多個層次的系統。
會話門面模式類似與實體-控制-邊界架構模式中的邊界,並且也與適配器和包裝模式相關。

單例模式

單例類可以保證其類型只會生成一個實例。只擁有一個實例在很多時候是很有用的,比如說全局訪問以及緩存代價高昂的資源;不過,如果在多線程環境下使用單例,那就有可能引入一些競態條件問題。由於大多數編程語言並未提供創建單利的內置機制,因此開發者需要自己實現。不過,javaEE提供了一種內建機制,開發者可以通過爲類添加註解來方便的創建單例。

使用場景

  • 跨越整個應用程序域來訪問共享數據,比如配置數據
  • 只加載並緩存代價高昂的資源一次,這樣可以做到全局共享訪問並改進性能
  • 創建應用日誌實例,因爲通常情況下只需要一個即可
  • 管理實現了工廠模式的類中的對象
  • 創建門面對象,因爲通常情況下只需要一個即可
  • 延遲創建靜態類,單例可以做到延遲實例化

Spring在創建Bean時使用了單例(默認情況下,Spring Bean是單例的),JavaEE在內部會使用單例,比如說在服務定位器中。JavaSE也在Runtime類的實現中使用了單例模式。因此,如果在正確上下文中使用了單例,那麼單利還是非常有用的。
不過,過度使用單例模式意味着不必要的資源緩存,無法讓垃圾收集器回收對象並釋放寶貴的內存資源。此外,這麼做意味着你無法充分利用對象創建與繼承的好處。大量使用單例模式其實是一種糟糕的面向對象設計的信號,這會導致內存與性能問題。另一個問題就是單例對於單元測試來說並不友好。

單例模式的幾種實現方法

private static MySingleton instance;
private MySingleton(){}
public static MySingleton getInstance(){
    if (null == instance){
        instance = new MySingleton();
    }
    return instance;
}

要解決競態條件問題,你需要獲得一把鎖,並且在實例返回後才釋放。

private static MySingleton instance;
private MySingleton(){}
public static synchronized MySingleton getInstance(){
    if (null == instance){
        instance = new MySingleton();
    }
    return instance;
}

另一種手段就是在加載類的同時創建單例實例,這樣就不必同步單例實例的創建,並在JVM加載完所有類時就創建好單例對象(因此,這是在類調用getInstance()方法前發生的)。之所以可以這樣,是因爲靜態成員與靜態塊是在類加載時執行的

private final static MySingleton instance = new MySingleton();
private MySingleton();
public static MySingleton getInstance(){
    return instance;
}

還可以使用靜態塊,不過,這會導致延遲初始化,因爲靜態塊是在構造方法調用前執行的

private static MySingleton instance = null;
static {
    instance = new MySingleton();
}
private MySingleton(){}
public static MySingleton getInstance(){
    return instance;
}

雙重檢測鎖是另一種非常流行的創建單例的機制,它要比其他方法更加安全,因爲它會在鎖定單例類之前檢查一次單例的創建,在對象創建之前再一次檢查

private volatile MySingleton instance;
private MySingleton(){}
public MySingleton getInstance(){
    if (null == instance){
        synchronized(MySingleton.class){
        if (null == instance){
            instance = new MySingleton();
        }
    }
    }
    return instance;
}

不過,這些方法都不是絕對安全的。比如說,開發者可以通過Java Reflection API將構造方法的訪問修飾符改爲public的,這樣就可以再次創建單例了。在Java中,創建單例的最佳方式是使用Java5中引入的枚舉類型,如下代碼所示,也是Joshua Bloch在其著作Effective Java中所極力推薦的方法。枚舉類型本質上就是單例的,因此JVM會處理創建單例所需的大部分工作。這樣,通過使用枚舉類型,你就無需再處理同步對象創建與提供等工作了,還能避免與初始化相關的問題

public enum MySingletonEnum {
    INSTANCE;
    public void doSomethingInteresting(){}
}

在該示例中,對單例對象的引用是通過如下方式獲得的

MySingletonEnum mse = MySingleEnum.INSTANCE;

一旦擁有了單例的引用,你就可以像下面這樣調用它的任何方法

mse.doSomethingInteresting();:w

依賴(Dependency Injection, DI)注入與CDI

JavaEE旨在處理最複雜的系統,不過它造成了開發的過於複雜。即便對於小型系統也是如此,因此未能達成所願。J2EE最初的設計依賴於極高的複雜性與緊耦合,這導致了諸如Spring與Pico容器等的逐步流行。大多數廠商並不支持和鼓勵開發者使用J2EE容器。然而,不久之後輕量級容器橫空出世,官方對其提供了支持,更有甚者,Spring成爲了非官方事實上的標準,並導致企業級Java完全的重新設計。

依賴注入的優勢

  • 客戶端不必關注被注入資源的不同實現,這使得設計變更變得更加容易
  • 可以輕鬆使用模擬對象實現單元測試
  • 配置外化,降低了變更的影響
  • 鬆耦合的架構支持可插拔的結構

DI背後的基本思想是改變對象創建的地點,使用注入器在恰當的時刻將指定實現注入目標對象中。這看起來像是工廠模式的一種實現,不過整個概念並不止於簡單的對象創建。控制反轉(IoC)改變了對象之間的整個連接,並且讓注入器完成這些工作(很多時候都是很神奇的)。相對於採用工廠將實現提供給調用者,注入器會更主動的確認目的對象何時需要目標對象,並以恰當的方式執行注入。

使用普通代碼實現DI

在沒有EJB容器的幫助下,Java並未提供標準的DI實現,知道上下文與依賴注入(CDI)的引入才改變了這一現狀。雖然有多種DI框架,比如說Spring和Guice,但編寫一個基本的實現也不是難事。最簡單的DI實現是一個工廠,它通過getInstance()方法在請求到來時創建依賴。

class UserService {
    private UserDataRepository udr;

    UserService(){
        this.udr = new UserDataRepositoryImpl();
    }

    public void persisUser(User user){
        udr.save(user);
    }
}
public interface UserDataRepository{
    public void save(User user);
}
public class UserDataRepositoryImpl implement UserDataRepository {
        @Overside
    public void save(User user){
        // Persistence code here
    }
    }
}
public class User {
    //User Specific Code Here
}

以上代碼中,UserService類提供了用於用戶管理的業務邏輯服務,比如說將用戶持久化到數據庫中。在該實例中,對象創建工作是在構造方法中完成的。這耦合了業務邏輯(類的行爲)與對象的創建。接下來進行重構,將對象創建從類中剝離出來放到工廠中。以下代碼創建了UserDataRepository的一個實現並將其傳遞給UserService類的構造方法。修改UserDataRepository類的構造方法以接受這個參數

public class UserServiceFactory{
    public UserService getInstance(){
        return new UserService(new UserDataRepositoryImpl());
    }
}

以下代碼中,UserService構造方法需要一個UserDataRepository實例注入到構造方法中。UserService類實現了與UserDataRepositoryImpl類的解耦。工廠現在負責對象的創建,並將實現注入到UserService的構造方法中,現在成功實現了業務邏輯與對象創建的解耦。

class UserService {
    private UserDataRepository udr;
    UserService (UserDataRepository udr){
        this.udr = udr;
    }

    public void persistUser(User user){
        udr.save(user);
    }
}

使用JavaEE實現DI

JavaEE 5之前,J2EE並未提供開箱即用的DI。相反,在J2EE中,Bean與資源是通過JNDI(Java命名與目錄接口)上下文查找來訪問的。這種方式導致了硬連接,並且依賴於重量級的基於服務器的容器,這使得測試要比編寫實際代碼還要困難。隨着JavaEE 5於EJB 3的發佈,DI成爲了企業級Java平臺的不可分割的一部分。爲了擺脫XML的配置,JavaEE新增了幾個註解來執行注入:
+ @Resource(JSR250),用於注入數據源,Java消息服務(JMS),URL,郵件與環境變量
+ @EJB(JSR220),用於注入EJB
+ @WebServiceRef,用於注入Web Service

隨着JavaEE 6,CDI與EJB 3.1的發佈,DI成爲了Java EE中功能更強,也更有趣的一個主題。在EJB 3.1中,接口對於EJB來說不再是強制的了。此外,新引入的EJB Web Profile提供了簡化,量級更輕的EJB容器。新引入的改進的注入註解@Inject(JSR229與JSR330)也爲Java領域中其他DI框架間的注入提供了公共接口。@Inject註解DI是類型安全的,因爲它會根據對象引用的類型注入依賴。如果要重構以上代碼,則需要刪除掉構造方法,並將@Inject註解添加到UserDataRepository字段,如下代碼所示

class UserService {
    @Inject
    private UserDataRepository udr;

    public void persistUser(User user){
        udr.save(user);
    }
}

CDI容器構建了一UserDataRepositoryImpl實例作爲容器管理的Bean,如果在類型爲UserDataRepository的字段上發現了@Inject註解,那麼就將其注入進去。你可以將容器管理器的bean注入到構造方法,方法與字段中,無論其訪問修飾符是什麼,不過字段不可以是final,方法也不能是抽象的。這引發一個問題。如果UserDataRepository接口有多個實現會出現什麼情況?CDI容器如何識別出要注入的正確實現?爲了消除UserDataRepository接口具體實現間的歧義,你可以使用開發者定義的限定符來註解具體的類。假設有UserDataRepository接口的兩個實現:一個用於MongoDB集合,另一個用於MySQL數據庫。你需要創建兩個限定符(一個用於Mongo實現,另一個用於MySQL實現),具體類要在類級別上使用相關的限定符進行註解,同時在注入UserDataRepository的類中要有一個字段使用相同的限定符進行註解。如果重構代碼如上所示中的UserService類以使用UserDataRepository的Mongo實現,那麼需要像下面這樣將@Mongo註解添加到udr字段上

@Inject Mongo

工廠模式

工廠模式有兩種形式:工廠方法和抽象工廠。它們的意圖是一樣的:提供一個接口,在不指定具體類的情況下創建相關或依賴的一系列對象。

何爲工廠

作爲一種創建型模式,工廠的目的在於創建對象。創建的邏輯被封裝在工廠中,要麼提供一個方法來返回新創建的對象(工廠方法模式),要麼將對象的創建委託給子類(抽象工廠模式)。無論哪種情況,對象的創建都從對象的使用中抽離出來了。客戶端不必考慮接口或類的不同實現,它只需通工廠(工廠方法或抽象工廠)獲取接口實現的一個實例即可,這樣客戶端與對象的創建就實現瞭解耦。解耦是應用依賴反轉原則的結果,這帶來了很多好處,其中最重要的好處就是實現了高層類與底層類之間的解耦。通過解耦,具體類實現的變化不會影響到客戶端,這降低了類與類之間的耦合,並提升了靈活性。通過將創建對象的代碼封裝起來,我們可以藉助工廠模式實現對象創建與底層系統間的解耦。通過這種方式,我們在重構時就會輕鬆很多,因爲重構的改變只會對一個點產生影響。
通常情況下,工廠本身會被實現爲單例或是靜態類,因爲一般來說只需要一個工廠實例即可。這麼做會將工廠對象的創建集中到一個地方,在對代碼進行修改和更新時有利於更好地組織和維護,並減少了錯誤。

依賴反轉原則:
+ 高層模塊不應該依賴於底層模塊,它們都應該依賴於抽象
+ 抽象不應該依賴於細節,細節應該依賴於抽象

工廠方法

定義一個用於創建對象的接口,不過讓子類決定實例化哪個類。工廠方法將類的實例化推遲到了子類。工廠極大降低了new關鍵字的使用次數,並且將初始化過程與不同的具體實現封裝起來。將這些需求中心化可以極大減少向系統中添加或刪除具體類的影響以及具體的類依賴的影響。

普通代碼實現工廠方法

public abstract class DrinksMachine {
    public abstract Drink dispenseDrink();
    public String displayMessage(){
        return "Thank for your custom.";
    }
}
public class CoffeeMachine extends DrinksMachine {
    public Drink dispenseDrink(){
        return new Coffee();
    }
}
public class SoftDrinksMachine extends DrinksMachine {
    public Drink dispenseDrink(){
        return new SoftDrink();
    }
}
public interface Drink{}
public class SoftDrink implements Drink {
     SoftDrink() {
         System.out.println("Soft Drink");
     }
}
public class Coffee implements Drink {
    Coffee(){
        System.out.println("Coffee");
    }
}

何時該使用工廠模式

抽象工廠被認爲是一種隱藏對象創建的有效手段,特別是在對象創建很複雜的情況下。對象創建越複雜,使用工廠來創建對象就越有道理。如果以一致的方式創建對象並且其創建要被嚴格控制是很重要的,那麼你就應該考慮工廠模式的實現。不過,在CDI環境這個美麗的世界中,容器會實例化託管對象,這時使用抽象工廠的意義就不大了。這種情況下,實現工廠模式的最好方式就是使用@Produce註解,它可以將複雜的創建邏輯隱藏到創建者方法中,並將生成的對象注入客戶端。此外,你還可以利用CDI環境的功能,讓容器創建對象,然後從相似對象的池中選擇需要使用的實例。不過,你只能使用簡單對象,即可以通過調用默認構造方法來實例化的對象。

面向切面編程(攔截器)

Aspectj與Spring已經廣爲接受,並且用在Java項目中很長時間了。Java也通過Servlet過濾器提供了一個類似但更加基本的方式,不過它只能用於處理Web請求。藉助於Servlet過濾器,任何請求和相應都可以被攔截,並且可以添加任何額外的行爲。
AOP並不是一種設計模式,它其實是一種編程範式。AOP在編譯時或運行時會使用依賴注入,它會比較既有代碼庫與給定注入標準,然後向每一個匹配點添加所需行爲或功能。執行編譯時注入的框架通常都很不錯,不過它們所生成的class文件並不會與源代碼逐行匹配,這是因爲被注入的代碼在起作用。運行時注入並不會修改源代碼或class文件,它通過攔截調用執行注入,並在原來的執行順序前後執行所需代碼。如果要添加一些重複性動作,那麼AOP就是非常有用的,比如說向代碼庫中添加日誌或安全等。可以根據環境或是項目的不同階段打開和關閉方面。方面可以動態向運行中的代碼添加所需行爲。它們會動態裝飾方式調用,就好像裝飾模式裝飾對象一樣。
AOP非常適合於封裝通用的非業務關注點。不過,如果向業務邏輯添加行爲,那麼AOP就會令人感到困惑。這種實現會導致去中心化,分佈式,以及難以測試和調試的業務邏輯的結果。生成的代碼也難以維護。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章