MVC(Model-View-Controller)是一种经典的UI结构设计模式。MVC模式把应用程序或者应用程序的一部分划分为三部分:Model(模型),是应用程序的主体,包括事务逻辑;View(视图),代表了用户接口;Controller(控制器),它的任务是处理用户输入和系统事件,分配任务到模型服务并更新相应的视图。
MVC结构甚至在一个类也有不同的等级;这个结构可以根据不同的功能实现。下面有一些例子——从简单到复杂——将展示如何将所有都在一个类里的J2ME[awv1]客户端转换为一个集成了MVC结构的J2ME[awv2]客户端;三个类如何组织以及这些类里的功能如何分布。在每一种客户端类型中都会讨论该类型的利弊,这样可以帮助开发者选择合适的客户端类型。
所有代码在一个类中的客户端
概念很简单:就是把所有的代码都放在一个类里,包含startApp()、pauseApp()、destroyApp(),像HelloWorld例子。我有时这样编写代码,来测试手机的特征,但是仅此而已。当然不称之为一种模式,但是它让我们理解如何从正开头产生一个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{.}
}
利:
代码长度最短,因为如果把抽象它们为方法,就没有重复的代码。事实上,一些条目可以在不同的视图中共用。
弊:
视图越多,类的结构就变得越复杂,控制逻辑中跳来跳去的样式会使多数开发者感到混淆。
基于视图的组件
基于视图的组件实现类的方式是:应用程序的每一个视图(或者几个类似的视图)被设计成一个类;在一个类中,MVC结构可以根据不同的功能实现。
图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){
…
}
}
利:
结构简单直观;它由屏幕视图组成,并且易于开发者理解。通过超类,我们可以把一些公共的接口放在其中,代码的代码重用性会更高。
弊:
一些基于视图的组件可能有相似的功能;这使得在不同的类中代码重复。重复代码是类设计的臭味,它使应用程序变得难于管理。
Model-View
Model-View结构是对于J2ME[awv3]客户端最普通的结构。实体数据和业务逻辑功能作为模型类实现。
图2 视图-模型结构类图
// 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;
}
}
利:
两个或者更多视图对象可以共用一个模型的功能;从不同类里抽象类似的代码。
弊:
这种结构的缺点是事件处理函数和屏幕流程逻辑分布在不同的视图中。因为每一个视图需要管理和其他视图的关系,如果我们有N个视图单元,每一个保持一个和其他视图的关系,就有N*(N-1)关系链,但是对于一个中间者,就只有N关系链。
单个控制器MVC
单个控制器,也称系统控制器,负责接收和处理所有的系统事件。正常地,控制器将分配大量的工作到模型,控制屏幕流程,处理和分发系统事件。在Sun著名的J2ME[awv5]蓝图——Smart Ticket中,一个大型交换函数被用来作为单个控制器的核心。
图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;
}
}
利:
在标准的MVC结构,业务逻辑(model)、表现逻辑(view)和事件处理逻辑(controller)被很好地划分。一个部分的修改和其他部分相隔离。这使得整个应用程序可扩展性和可升级性提高。
弊:
随着应用程序大小的增加,越来越多的页面流逻辑都集中在了一个控制器中。这个控制器变得很大和难于管理。也许是时候把它分成几部分了。在模型、视图和控制器中的严格分离使得它更加难于调试和测试。
多控制器MVC
多控制器结构通常基于用例,也就是说,一个控制器只处理一个或者几个相关的用例事件。随着单个控制器的划分,整个应用程序可能被划分成几个具有相似结构的包。对于企业级应用程序来说,这个结构是很典型的,但是很少在J2ME[awv6]应用程序中使用。
图5 多控制器MVC结构对象图
利:
通过多控制器结构,控制器变得容易管理,并且应用程序可以被划分成几个部分,然后分配给几个开发者。
弊:
随着类、对象和文件数量的增加,它们之间的交互机制变得更加复杂。
总结
集成的MVC结构要求谨慎地设计和计划,你必须花费足够的时间来考虑不同部分间的交互机制。J2ME[awv7]客户端有它自己的特征:应用程序在大小上通常很小,可用资源有限。由于小屏幕尺寸,J2ME[awv8]应用程序更愿意替换一个视图而不是修改。最好的设计选择就是最适合应用程序要求的,而不是具有最好结构的。
(出处:http://www.knowsky.com)