閱讀669 返回首頁    go 技術社區[雲棲]


Struts2 中的設計模式

1. Command Pattern

基本定義: 把Command(Request)封裝成對象,把發出命令(Invoker)的責任和執行命令(Receiver)的責任分割開,委派給不同的對象。

責任劃分有什麼好處?

責任約單一,內聚性越高,可重用的可能性越大,試想下,如果服務員不僅要點菜,還要去做菜,會是什麼情景。

為什麼把Invoker和Receiver解耦好處多?

類之間的耦合越低,可擴展的可能性越高。解耦後,更換一個服務員並不會影響廚師的工作

那麼把Request封裝成對象具體是什麼意思呢?

在遙控器Remote Control例子中,比如我有一個‘開燈’的Request,那麼就應該對應有一個LightOnCommand對象,關燈就應該有LightOffCommand

在Web應用中,我有一個add user的Request,那麼就應有一個AddUserCommand去處理請求, 或者用struts的name convention就是AddUserAction.

那麼在Struts中又是怎麼運用Command Pattern的呢?

Client : FilterDispatcher Servlet

Invoker: ActionInvocation

Command: Action (not a must in Struts2)

ConcreteCommandAddUserAction

Receiver: AddUserServiceImpl (not a must, depends on how many logic you want to put into concreteCommand)

Struts,你什麼時候調用的setCommand()?

用戶定義了Action Mapping在struts.xml裏,在web container啟動時,ConfigurationManager 就裝載了struts.xml,當Request過來時,Struts Framework會根據當前的URL去找ConfigurationManager所對應的concreteCommand/Action, 然後這個Action會被set到ActionInvocation

Command Interface是必須的嗎?

在struts1中,有一個Interface,所有的Action都必須是它的實現。但是在Struts2中,Action可以是任意的POJO,因為在Runtime的時候,具體的Action是什麼,該調用它的什麼方法,都可以通過配置文件(MetaData)+ Java Reflection來實現。這種新方式的好處是POJO Action沒有了對框架的依賴,測試將會更加容易。缺點是因為沒有interface的約束,調用Action的什麼方法完全取決於默認值(比如execute)或是配置文件中的配置。若設置不妥,隻有在Runtime的時候才能發現錯誤。

Receiver 是必須得嗎?

不是,取決於你Action的厚度,如果你想讓Action很輕的話,那麼通常你會在Action中使用UserService.addUser()去做事情,此時的UserService就是Receiver。把Action設計的厚點,直接把addUser的logic放在Action中也是可以的。

Struts2中運用了command的思想,但並沒有嚴格的按照其經典模型實現,而是做了些變通,這些變通乍看起來可能是有點違背設計原則,比如說取消了Action Interface,這不是反模式嗎,反麵向接口的編程嗎,但仔細想想,這裏我們真的需要這個接口嗎?通過配置文件+Reflection,我們同樣可以做到在Runtime的時候給ActionInvocation注入不同的Action的目的。而接口卻增加了用戶實現對框架的依賴,降低了程序的可測性,所以這樣的變通其實是有積極意義的,雖然我們損失了一點點接口作為契約所帶來的好處。

2. Interceptor Pattern

於其說這是模式,不如說這是AOP和Pipeline思想的結合,隻不過Struts2中的實現非常的精巧,我不得不說這應該作為一個模式來推廣。

AOP: 所謂的AOP就是preProcess and postProcess, 係統應用中,許多地方是需要麵向切麵的,比如log,authentication,etc...。Java Dynamic Proxy通常也是實現AOP的方式,但它是通過Java Reflection的機製,動態的產生一個被修改過的method,從而達到預處理和後處理的目的,個人覺得這種方式粗暴不夠優雅,並且可擴展性不好。 不過其優點是它是內嵌在JDK中的AOP,使用比較方麵,對於那些不需要擴展性的需求,動態代理也是可以考慮的。

Pipeline:分層就是把複雜的問題分層多個層次,每一層隻處理問題的一小部分。通信的7層模型就是典型的範例

Interceptor Pattern: 不僅為係統進行分層,而且還提供了AOP的處理,此外,還以一種plug-in的方式為用戶提供了無限擴展的可能。

應該怎麼實現?

Interceptor的調用過程類似於一個棧式調用,所以想到遞歸是很自然的,這既避免了像Dynamic Proxy那樣的class hack,又提供了更好的擴展性。

 還是以Struts中的類作為例子,其類圖如下:


調用過程:

Pseudo 代碼:

ActionInvocation

public Result invoke(){
    if( interceptors.hasNext() ){
        Interceptor interceptor = interceptors.next();
        result = interceptor.intercept(this);
    }
    else {
        action.execute();//如果沒有更多的Interceptor,停止遞歸,調用action
    }
}

InterceptorImpl

public SomeInterceptor implements Interceptor{
    public Result intercept(ActionInvocation actionInvocation){     
       //pre-processing
      
        // 遞歸調用
        result = actionInvocation.invoke();

       //post-processing
       
       return result;
    }
}


More: https://www.cnblogs.com/west-link/archive/2011/06/22/2086591.html

https://bosy.dailydev.org/2007/04/interceptor-design-pattern.html

Struts2 架構圖


Struts 2 framework: https://viralpatel.net/blogs/introduction-to-struts-2-framework/


最後更新:2017-04-02 16:47:43

  上一篇:go ActionBarSherlock — 無縫集成ActionBar
  下一篇:go Gson對Java嵌套對象和JSON字符串之間的轉換