Sun blueprints: Smart Ticket中使用的MVC模式
著名的蓝图程序Smart Ticket中使用了MVC模式,并且这一模式帮助Sun的程序员在MIDP2发布时,快速的将Smart Ticket的view部分从MIDP1.0 更新到MIDP2.0。
Sun针对MIDP的特点,设计并改进了这一模式,在SUN的解决方法中是一个很标准的方法,只是 Controller变成了一个巨大的事务处理器,所有由UI对象收集到的用户的需求都转发给Controller处理。Controller内部保存了一组常量。在一个dispose(int id)形式的方法里一个巨大的switch case语句根据比较不同的常量,处理不同的请求。这种技术有时也将Controller称为处理器,或者屏幕导航器。这种模式的提出者主要是要集中处理j2me里频繁的画面导航。
很多人都觉得,在j2me中将Controller改造成巨大的事务处理器是一个很好的方法。我对此持保留意见。
iFeedback 中简化的MVC
为了大大减少类的数量,iFeedback的作者,将MVC封装到一个类中,用不同的方法来代表对这三者的分离,这种举动证明对减少类的数量又很大帮助。
public abstract class MVCComponent implements CommandListener { // Set from outside at beginning public static Display display;
// Returns the screen object from the derived class public abstract Displayable getScreen();
public Displayable prepareScreen () throws Exception { if ( getScreen() == null ) { initModel(); createView(); } else { updateView(); } getScreen().setCommandListener ( (CommandListener) this ); return getScreen (); }
public void showScreen() { try { display.setCurrent( prepareScreen() ); } catch (Exception e) { e.printStackTrace(); Alert a = new Alert("Error in showing screen"); a.setTimeout(Alert.FOREVER); display.setCurrent(a); } }
// Initialize. If a data member is not backed by RMS, make sure // it is uninitilzed (null) before you put in values. protected abstract void initModel () throws Exception;
protected abstract void createView () throws Exception;
protected abstract void updateView () throws Exception;
public abstract void commandAction(Command c, Displayable s);
} | 因为都在一个类里面,你在也不必被MVC三者之间的关系操心了,这种退化的做法,是对MIDP有限资源的妥协。
我的习惯做法
下面结合我对MVC的理解和大家交流一下。我使用的是一种UML标准的做法,最大程度上对的体现分离的思想。首先和大家交流一下词汇表:
View代表屏幕。
View通过预先商定好的接口向Controller索要数据,View同时收集用户的输入,View并不处理这些输入,而是根据不同的输入回调Controller不同的方法。通常View的子类使用UI后缀。
Controller 控制器
提供View调用的接口,负责和model交流。控制器和View共同担负起和用户交流的作用。
Model 泛指一系列的实体对象
需要注意的是我理解的Model并不是屏幕数据的组织单位。Model代表一系列的实体对象。由Controller跟Model交流。我觉得RAD工具中常常将Model代表屏幕数据的集合正式导致MVC概念混乱的一个原因。RAD工具中Model,大体相当于这里的Controller所起的作用。
控制器并不总是联系着Model,有时只是依赖关系。并且Controller往往通过Model的对应的生命期类来获得Model对象。在这种形式中,层层隔离,View与Controller紧密相连,而Model有很高的独立性,可以很好的重用。
一般的结合UML设计的过程,对MVC的各个类有相应的命名习惯。
View 称为Boundary类(边界类) 以UI结尾
Controller 称为 Controller类(控制类) 以Workflow结尾
Model 称为Entity类(实体类) 以Entity结尾或者没有尾缀
Model对应的Lifecycle类(生命周期类) 以Locator结尾
边界类和控制类的基础类如下
BaseView.java /** * @author Favo * * 视图类 */ public abstract class BaseView {
public abstract Display getDisplay();
/** * 简单的返回包装的屏幕对象,不要做任何准备屏幕的操作! */ public abstract Displayable getScreen();
/** * 创建屏幕 */ protected abstract void createView() throws Exception;
/** * 更新屏幕 */ public abstract void updateView() throws Exception;
/** * 返回控制器 */ public abstract BaseController getController();
/** * 准备屏幕 * 返回准备好的屏幕对象 */ public Displayable prepareScreen() throws Exception { if(getScreen()==null){ createView(); } else { updateView(); } return getScreen(); }
/** * 显示当前屏幕 */ public void displayScreen(){ try{ getDisplay().setCurrent(prepareScreen()); } catch (Exception e) { e.printStackTrace(); Alert al=new Alert("Error",e.toString()+'\n'+e.getMessage(),null,AlertType.ERROR); al.setTimeout(Alert.FOREVER); getDisplay().setCurrent(al); } }
}
BaseController.java /** * @author Favo * * 控制类 */ public abstract class BaseController { public abstract BaseView getView(); public abstract void setView(BaseView view); } | 注意到这些基础的类并没有向MFC框架那样产生完整的框架,而是设计成了抽象类,一来希望强迫大家实现抽象类(防止出错);二来希望增加一点灵活性。所以两个类之间的通信就要靠大家撰写的子类的构造函数了。一般我的习惯是,初始化好控制器,然后将控制器作为参数传给边界类的构造函数,由边界类的构造函数来回调控制器的setView()来实现的。这些步骤是一定要有的,不然会NULLpointerExcpetion哦。
尽管理论上可能很清晰,但实践带来的复杂性是惊人的。这正是软件开发的问题,太多的细节困扰这开发者对大局的把握。本文接下来,将结合最后这种设计思想,给出一个完整的设计实例。帮助大家从实践的角度理解运用这一模式。敬请大家期待。
|
|