命令模式在MVC架構中的應用

來源:互聯網
上載者:User

標籤:mvc   設計模式   

其實在項目開發中,我們使用了大量的設計模式,只是這些設計模式都封裝在架構中了,如果你想要不僅僅局限於簡單的使用,就應該深入瞭解架構的設計思路。

在MVC架構中,模式之一就是命令模式,先來看看模式是如何定義的。


命令模式:

 

定義:把一個請求或者操作封裝在命令對象中。命令模式允許系統使用不同的請求把用戶端參數化,對請求排隊或者記錄請求日誌,可以提供命令的撤銷和恢複功能。

 


Invoker類被用戶端調用,可以接受命令請求,設計命令隊列,決定是否相應該請求,記錄或撤銷或重做命令請求,記錄日誌等等.

 

public class Invoker {   private Command command;   public void setOrder(Command command) {   this.command = command;       }   public void ExecuteCommand() {           command.ExecuteCommand();       }   }   

 

Command類,將一個請求封裝成一個對象,將一個請求具體化,方便對請求記錄。

public abstract class Command {   protected Receiver receiver;   public Command(Receiver receiver){   this.receiver = receiver;       }   public abstract void ExecuteCommand();   }    ConcreteCommand類,可以將Receiver對象放到這個類裡面,這個類具體實現了要怎麼處理這個使用者的請求。public class ConcreteCommand extends Command {   public ConcreteCommand(Receiver receiver){   super(receiver);       }   @Override  public void ExecuteCommand() {           receiver.Execute();       }   }   


 

Receiver類,其實這個類可以沒有,不過為了讓設計看起來更整潔清楚。

public class Receiver {   public void Execute(){           System.out.println("Receiver excute!");       }   } 

 

最後一個Client類。

public class Client {   public static void main(String[] args) {           Receiver r = new Receiver();           Command c = new ConcreteCommand(r);            Invoker i = new Invoker();           i.setOrder(c);           i.ExecuteCommand();       }   }


命令模式在MVC中的應用:

 

     Struts中,在模型層都要繼承一個Action介面,並實現execute方法,其實這個Action就是命令類。為什麼Struts會應用程式命令模式,是因為Struts的核心控制器ActionServlet只有一個,相當於Invoker,而模型層的類會隨著不同的應用有不同的模型類,相當於具體的Command。這樣,就需要在ActionServlet和模型層之間解耦,而命令模式正好解決這個問題。

 

MVC Model2實現的Web架構:


說明:

控制器採用Servlet實現,整個架構採用同一個Servlet,以實現請求的中轉

Controller Servlet包括兩部分:處理常式和命令


 

     整個Web架構大致的流程是:首先用戶端發送請求,提交JSP頁面給中轉器(Servlet);中轉器根據客戶的請求,選擇相應的模型層,即Logic,Logic進行相應的邏輯處理;如果需要使用資料庫,則通過DAO進行相應的資料庫操作。

 

 

下面主要看一下控制層和模型層的設計,應用程式命令模式:

控制層設計

     控制層主要用來轉寄從視圖層傳來的資料和請求到相對應的模型層,因此,實現它最好的方式莫過於使用Servlet了。當從視圖層擷取請求後,首先通過對web.xml檔案的配置,使其轉入Servlet,在Servlet中完成對頁面中資料的封裝和對相應模型的選擇,然後再到相應的模型層進行資料處理;當在模型層資料處理完畢後,通過RequestDispatcher將處理後的資料返回相應的視圖頁面。

     在Servlet中,將使用doPost()來處理相應的中轉請求,如果開發人員使用get提交方式,則使用如下方式進行處理。範例程式碼如下:

public void doGet(HttpServletRequest req, HttpServletResponse res)  throws ServletException, IOException {      doPost(req, res);  }  //使用post提交方式  public void doPost(HttpServletRequest req, HttpServletResponse res)  throws ServletException, IOException {      do_Dispatcher (req, res);  }  


代碼說明:

不論採用get還是post提交方式,都將執行do_Dispatcher(req, res)方法。

do_Dispatcher(req, res)是用來處理視圖層發送來的請求的方法。

     這樣的做體現了Front Controller模式,如果直接使用request方式來擷取從頁面提交的資料,在要擷取的資料比較多的情況下,會比較煩瑣,而且直接將request傳遞給模型層不符合Model2規範。所以,這裡將對從頁面傳來的值進行封裝,將其放在一個Map中,然後再傳遞給模型層,這樣在模型層就可以直接使用Map中的值。範例程式碼如下:

private HashMap getRequestToMap(HttpServletRequest req) throws Exception {      req.setCharacterEncoding("GBK");          HashMap infoIn = new HashMap();          for (Enumeration e = req.getParameterNames(); e.hasMoreElements ();)                  {//擷取頁面中所有元素名              String strName = (String)e.nextElement();              String[] values = (String[]) req.getParameterValues (strName);                  //根據名稱擷取對應的值              if (values == null) {//假如沒有值                  infoIn.put(strName, "");              } else if (values.length == 1) {//假如只有一個值                  infoIn.put(strName, values[0]);              } else {//假如有多個值                  infoIn.put(strName, values);              }          }      return infoIn;  }  

 

代碼說明:

req.setCharacterEncoding("GBK"),這裡首先將從視圖層傳來的資料設定編碼為GBK。

HashMap infoIn = new HashMap(),定義一個HashMap,用來存放從request中擷取的資料。

req.getParameterNames(),用來擷取從頁面中傳來的所有元素。

req.getParameterValues(),用來根據元素名稱來擷取元素對應的值,並將元素名稱和值的對應關係存入HashMap中。如果元素的值為空白,則在HashMap中將元素名稱對應的值置為空白;如果只有一個值,則將該值存入;如果有多個值,則存入數組。

 

命令模式使用:

一個視圖對應一個模型,也可能一個視圖對應多個模型,但只有一個控制器,所以,為了實現一個控制器可以轉寄到多個模型中去,就需要使用介面,讓所有模型都實現這個介面,然後在控制器裡,僅僅是面對介面編程即可。

 

這裡定義一個介面Action.java,Action.java的範例程式碼如下:

//******* Action.java**************  import java.util.*;  public interface Action{      public HashMap doAction(HashMap infoIn);  }  

 

在控制器中只針對這個介面處理即可。範例程式碼如下:

Actionaction = (Action) Class.forName(getActionName(systemName,  logicName)).newInstance();  HashMap infoOut = action.doAction(infoIn);  


代碼說明:

getActionName()方法是擷取實現介面Action的類的名稱和所在的包。

 

使用RequestDispatcher返回視圖層。範例程式碼如下:

req.setAttribute("infoOut", infoOut);  RequestDispatcher rd = req.getRequestDispatcher("/"+ systemName +  "/jsp/" + forwardJsp+ ".jsp");  rd.forward(req, res);  

                 代碼說明:

這裡表示JSP檔案放在項目中系統名下的jsp檔案夾下。

 

模型層設計

假定有一個模型層類為WebExamAction.java,主要用來負責線上考試系統的業務處理,則這個類要實現Action介面。範例程式碼如下:

//******* WebExamAction.java**************  import java.util.HashMap;  public class WebExamAction implements Action{      //根據頁面的請求,進行動作的轉換      public HashMap doAction(HashMap infoIn) {          String action = (infoIn.get("action") == null) ? "" :  (String)infoIn.get("action");          HashMap infoOut = new HashMap();          if (action.equals(""))           infoOut = this.doInit (infoIn);          else if (action.equals("insert")) infoOut = this.doInsert (infoIn);          return infoOut;      }      /**該方法設定使用者登入時頁面的初始資訊      * @param infoIn      * @return HashMap      */      private HashMap doInit(HashMap infoIn) {          HashMap infoOut = infoIn;          int clerkId = (infoIn.get("clerkId") == null || "".equals  (infoIn.get("clerkId"))) ? -1 : Integer.parseInt((String)  infoIn.get ("clerkId"));          try {              //取得考生姓名              Users user = new Users();              String clerkName = user.getName(clerkId);               infoOut.put("clerkName", clerkName);          } catch(Exception e) {              e.printStackTrace();          } finally {              return infoOut;          }      }      /**該方法用來進行新增     * @param infoIn     * @return HashMap     */      private HashMap doInsert(HashMap infoIn) {          HashMap infoOut = infoIn;          int clerkId = (infoIn.get("clerkId") == null || "".equals  (infoIn.get("clerkId"))) ? -1 : Integer.parseInt((String)  infoIn.get ("clerkId"));          try {              //取得考生姓名              Users user = new Users();              String clerkName = user.getName(clerkId);               infoOut.put("clerkName", clerkName);          } catch(Exception e) {              e.printStackTrace();          } finally {              return infoOut;          }      }  }  

 

代碼說明:

這裡,在doAction中根據從頁面傳來的action進行動作請求的轉換。

通過一個名為infoIn的HashMap,來負責傳入頁面中元素的值。

通過一個名為infoOut的HashMap,來負責將處理後的資料傳出。

可以看出,如果模型層都實現介面Action,實現doAction方法,即可實現動作請求的轉換。

 

命令模式要點:

1.Command模式的根本目的在於將“行為要求者”與“行為實現者”解耦,在物件導向語言中,常見的實現手段是“將行為抽象為對象”。 

2.實現Command介面的具體命令對象ConcreteCommand有時候根據需要可能會儲存一些額外的狀態資訊。 

3.通過使用Compmosite模式,可以將多個命令封裝為一個“複合命令”MacroCommand。 

4.Command模式與C#中的Delegate有些類似。但兩者定義行為介面的規範有所區別:Command以物件導向中的“介面-實現”來定義行為介面規範,更嚴格,更符合抽象原則;Delegate以函數簽名來定義行為介面規範,更靈活,但抽象能力比較弱。 

5.使用命令模式會導致某些系統有過多的具體命令類。某些系統可能需要幾十個,幾百個甚至幾千個具體命令類,這會使命令模式在這樣的系統裡變得不實際。



相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.