MVC(Model-View-Controller)是一種經(jīng)典的UI結(jié)構(gòu)設(shè)計模式。MVC模式把應(yīng)用程序或者應(yīng)用程序的一部分劃分為三部分:Model(模型),是應(yīng)用程序的主體,包括事務(wù)邏輯;View(視圖),代表了用戶接口;Controller(控制器),它的任務(wù)是處理用戶輸入和系統(tǒng)事件,分配任務(wù)到模型服務(wù)并更新相應(yīng)的視圖。
MVC結(jié)構(gòu)甚至在一個類也有不同的等級;這個結(jié)構(gòu)可以根據(jù)不同的功能實現(xiàn)。下面有一些例子——從簡單到復(fù)雜——將展示如何將所有都在一個類里的J2ME[awv1]客戶端轉(zhuǎn)換為一個集成了MVC結(jié)構(gòu)的J2ME[awv2]客戶端;三個類如何組織以及這些類里的功能如何分布。在每一種客戶端類型中都會討論該類型的利弊,這樣可以幫助開發(fā)者選擇合適的客戶端類型。
所有代碼在一個類中的客戶端
概念很簡單:就是把所有的代碼都放在一個類里,包含startApp()、pauseApp()、destroyApp(),像HelloWorld例子。我有時這樣編寫代碼,來測試手機的特征,但是僅此而已。當(dāng)然不稱之為一種模式,但是它讓我們理解如何從正開頭產(chǎn)生一個MVC模式。
//Code example, items shared in different views
public void commandAction(Command cmd,Displayable disp)
{
if(cmd == getCommand &¤tView==mainForm) then
{. }
else if(cmd == getCommand &¤tView==listForm) then
{ . }
else{.}
}
利:
代碼長度最短,因為如果把抽象它們?yōu)榉椒ǎ蜎]有重復(fù)的代碼。事實上,一些條目可以在不同的視圖中共用。
弊:
視圖越多,類的結(jié)構(gòu)就變得越復(fù)雜,控制邏輯中跳來跳去的樣式會使多數(shù)開發(fā)者感到混淆。
基于視圖的組件
基于視圖的組件實現(xiàn)類的方式是:應(yīng)用程序的每一個視圖(或者幾個類似的視圖)被設(shè)計成一個類;在一個類中,MVC結(jié)構(gòu)可以根據(jù)不同的功能實現(xiàn)。
圖1 基于視圖的組件類圖
public class LoginComponent extends ViewComponent implements CommandListener {
public Display display;
…
// View related functions
public Displayable getView( … );
public Displayable PRepareView (){ … }
public void showView() { … }
// Model related functions
Public bolean loginService(String name, String passWord){
…
}
// Controller
public void commandAction(Command c, Displayable s){
…
}
}
利:
結(jié)構(gòu)簡單直觀;它由屏幕視圖組成,并且易于開發(fā)者理解。通過超類,我們可以把一些公共的接口放在其中,代碼的代碼重用性會更高。
弊:
一些基于視圖的組件可能有相似的功能;這使得在不同的類中代碼重復(fù)。重復(fù)代碼是類設(shè)計的臭味,它使應(yīng)用程序變得難于管理。
Model-View
Model-View結(jié)構(gòu)是對于J2ME[awv3]客戶端最普通的結(jié)構(gòu)。實體數(shù)據(jù)和業(yè)務(wù)邏輯功能作為模型類實現(xiàn)。
圖2 視圖-模型結(jié)構(gòu)類圖
// View example, use form as super class of a view
class UnitView extends Form implements CommandListener{
private LoginEngine engine;
private String result;[awv4]
…
UnitView(String name; LoginEngine engine){
Super(name);
this.engine = engine;
}
String login(String name, String password){
result=model.getService(name, password);
…
}
…
}
//Model example
class LoginEngine{
public String getService(String name,String password){
…
return result;
}
}
利:
兩個或者更多視圖對象可以共用一個模型的功能;從不同類里抽象類似的代碼。
弊:
這種結(jié)構(gòu)的缺點是事件處理函數(shù)和屏幕流程邏輯分布在不同的視圖中。因為每一個視圖需要管理和其他視圖的關(guān)系,如果我們有N個視圖單元,每一個保持一個和其他視圖的關(guān)系,就有N*(N-1)關(guān)系鏈,但是對于一個中間者,就只有N關(guān)系鏈。
單個控制器MVC
單個控制器,也稱系統(tǒng)控制器,負責(zé)接收和處理所有的系統(tǒng)事件。正常地,控制器將分配大量的工作到模型,控制屏幕流程,處理和分發(fā)系統(tǒng)事件。在Sun著名的J2ME[awv5]藍圖——Smart Ticket中,一個大型交換函數(shù)被用來作為單個控制器的核心。
圖3 MVC模型類圖
圖4 MVC對象的時序圖
// login view
public class LoginView extends Form implements CommandListener{
Controller uniController;
Display display;
MVCMidlet mvcMidlet;
TextField userNameField;
TextField passWordField;
Command loginCommand;
public LoginView(String title){
super(title);
uniController=Controller.getInstance();
…
this.append(userNameField);
this.append(passWordField);
this.addCommand(loginCommand);
}
public void prepareView(Display display){
setCommandListener(this);
this.display=display;
}
public void showView(){
display.setCurrent(this);
}
public void commandAction(Command c, Displayable d){
if(c==loginCommand){
Event event = new Event();
event.setByName("userName",userNameField.getString());
event.setByName("password",passWordField.getString());
uniController.handleEvent(Event.LOGINEVENT,event);
}
…
}
}
//Controller
public class Controller {
private static Controller instance;
private Display display;
private LoginEngine loginEngine;
private HomeView homeView;
private LoginView loginView;
private ErrorView errorView;
...
private Controller() {
...
}
public void init(MIDlet midlet){
this.display = Display.getDisplay(midlet);
...
}
public static synchronized Controller getInstance(){
if(instance == null){
instance = new Controller();
}
return instance;
}
public void handleEvent(int eventID, Event e){
switch(eventID){
case LOGIN_EVENT:
String passWord = e.getByName("password");
String userName = e.getByName("userName");
Boolean result = loginEngine.loginService(userName,password);
if(result==true){
homeView.prepareView(display);
homeView.showView();
}
else{
errorView.prepareView(display,"loginError");
errorView.showView();
}
break;
…
}
}
}
//login model
public class LoginEngine {
public boolean loginService(String userName, String passWord){
boolean result = …
…
return result;
}
}
利:
在標(biāo)準(zhǔn)的MVC結(jié)構(gòu),業(yè)務(wù)邏輯(model)、表現(xiàn)邏輯(view)和事件處理邏輯(controller)被很好地劃分。一個部分的修改和其他部分相隔離。這使得整個應(yīng)用程序可擴展性和可升級性提高。
弊:
隨著應(yīng)用程序大小的增加,越來越多的頁面流邏輯都集中在了一個控制器中。這個控制器變得很大和難于管理。也許是時候把它分成幾部分了。在模型、視圖和控制器中的嚴格分離使得它更加難于調(diào)試和測試。
多控制器MVC
多控制器結(jié)構(gòu)通常基于用例,也就是說,一個控制器只處理一個或者幾個相關(guān)的用例事件。隨著單個控制器的劃分,整個應(yīng)用程序可能被劃分成幾個具有相似結(jié)構(gòu)的包。對于企業(yè)級應(yīng)用程序來說,這個結(jié)構(gòu)是很典型的,但是很少在J2ME[awv6]應(yīng)用程序中使用。
圖5 多控制器MVC結(jié)構(gòu)對象圖
利:
通過多控制器結(jié)構(gòu),控制器變得容易管理,并且應(yīng)用程序可以被劃分成幾個部分,然后分配給幾個開發(fā)者。
弊:
隨著類、對象和文件數(shù)量的增加,它們之間的交互機制變得更加復(fù)雜。
總結(jié)
集成的MVC結(jié)構(gòu)要求謹慎地設(shè)計和計劃,你必須花費足夠的時間來考慮不同部分間的交互機制。J2ME[awv7]客戶端有它自己的特征:應(yīng)用程序在大小上通常很小,可用資源有限。由于小屏幕尺寸,J2ME[awv8]應(yīng)用程序更愿意替換一個視圖而不是修改。最好的設(shè)計選擇就是最適合應(yīng)用程序要求的,而不是具有最好結(jié)構(gòu)的。
(出處:http://www.survivalescaperooms.com)
新聞熱點
疑難解答