MVC設計模式參考一

先談優點:

1)將M.V.C.分離可以讓不同的專家負責不同的模塊,一般情況下,M部分由熟悉數據庫,網絡傳輸的專家來負責;V則交給對UI有研究的專家。這 對於項目的管理者而言是多麼的誘人,分工意味着可以提高效率並可以按照傳統的責任劃分來處理軟件開發過程。對開發者而言也可以專心於一個領域。這樣做的前 提是接口要明確,MVC的分離思想正爲其提供了基礎。

2)一旦V的部分發生變化,可以迅速的重構而不必引起整個工程的返工。如今的軟件表現層的部分變化實在是太快了…

3)M的部分,因爲足夠抽象,可以方便的重複利用,符合OO的思想。另一方面我們可以利用JUnit等單元測試工具對M進行測試,保證工程質量。

談完了優點再來看看缺點:

1)利用MVC模式(也包括近代的其他一些模式)暗示我們通過多產生一些類,來提高程序的可讀性與健壯性。附帶來的缺點就是類的數量的膨脹。說句笑話,MVC就好像是發麪時用的速效粉一樣,是最爲方便的代碼膨脹劑,相信大家都深有體會:)

2)MVC雖然定義了M.V.C.個個部件的含義,但並不具體,而且沒有非常明確的固定三者之間的聯繫。所以一直以來除了View沒有爭論外,其他 方面都有很多爭論,大家都想把自己的理解作爲正解。尤其是“Model到底是屏幕數據的集合還是實體數據”、“控制器的作用”是兩個經常爭論的問題。前面 提過MVC變種很多,這也給初學者留下了不少的陷阱。後面結合實例將會分析幾種常見的做法。

3)MVC的實現成本偏高。但請注意是這是相對的,一般而言項目越大,越可以看出其優勢。

常見的MVC模式實踐 下面將會介紹在midp平臺幾種常見的實踐,最後是我習慣的做法

M—V形式 (或者MC—V 、M—VC

這也是在j2me中一種慣用的方法,精煉的說這種方法是以屏幕爲組織單位的,因而很適合RAD工具的開發思路。一個屏幕及其控制被抽象成一個VC 類,而這個類中有一個私有的Model對象來代表屏幕上要用到的數據元素。屏幕對象並不保存任何的實體數據,這些數據被組織在了Model對象中。大概因 爲屏幕對象很直觀,控制器的作用也不明晰(它絕大部分的功能被view或是model取代,具體取決於你的實現),所以也常常稱呼爲model-view 模式。形式如下:

class MyFrame extend Frame{

private Model model;

private StringItem name;

MyFrame(Model model){

this.model=model;

name=new StringItem(model.getName());//請求模型的數據

append(name);

}

}

class Model{

private String name="M-C pattern";

public String getName(){//這是一個服務接口

return name;

}

}

上面看到的是個典型的M—V模型,我們可以理解這種以屏幕爲核心的分離的含義。Model組織起屏幕的數據,view向Model索要其希望顯示的 數據,注意這一操作一定要通過預先協商好的接口訪問,而不是直接操作。如果出現複雜的事務邏輯(用戶選擇的某種操作),有人將其放在Model端,也有人 放在View端,但一般上放在Model端,這時Model帶有嚴重的Controller的色彩。

這種形式的優點是非常的直觀,也有限的分離了顯示和數據。如果常看j2medev.com站長Mingjava的文章,可以看到大部分他寫的例子都是這種模式。並且這種模式也常常用於RAD工具。

這種模式的缺點是它與RAD工具一樣鼓勵你從屏幕開始思考問題,這往往讓你陷入RAD的陷阱——不先考慮事務的流程,而是從用戶接口直接下手去分析問題,這往往扼殺了你的全局構思。

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哦。

儘管理論上可能很清晰,但實踐帶來的複雜性是驚人的。這正是軟件開發的問題,太多的細節困擾這開發者對大局的把握。

發佈了103 篇原創文章 · 獲贊 17 · 訪問量 48萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章