669
技術社區[雲棲]
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)
ConcreteCommand:AddUserAction
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