簡介
MVC設計模式是在20世紀80年代發明的一種軟件設計模式,至今已被廣泛使用,後來被推薦为 Sun 公司 J2EE 平台的設計模式。
隨着Web應用的商業邏輯包含逐漸复雜的公式分析計算、决策支持等,使客戶機越 來越不堪重負,因此將系統的商業分離出來。單獨形成一部分,這样三層結構產生了。 其中‘層’是邏輯上的劃分。[1-3]
體系結構 表現層(Presentation layer):包含表示
代碼、用戶交互GUI、數據驗證。 該層用於向客戶端用戶提供GUI交互,它允許用戶在顯示系統中輸入和編輯數據,同時 系統提供數據驗證功能。
業務邏輯層(Business layer):包含業務規則處理代碼,即程序中與業務 相關專業算法、業務政策等等。該層用於執行業務流程和制訂數據的業務規則。業務邏 輯層主要面向業務應用,为表示層提供業務服務。
數據持久層(Persistence layer):包含數據處理代碼和數據存儲代碼。數據持久層主要包括數據存取服務,負責與數據庫管理系統(如數據庫)之間的通信。 三個層次的每一層在處理程序上有各自明確的任務,在功能實現上有清晰的區分, 各層與其餘層分離,但各層之間存有通信接口。 [3]
編輯本段模式結構
視圖:數據的展現。
視圖是用戶看到並與之交互的界面。視圖向用戶
顯示相關的數據,並能接收用戶的輸入數據,但是它並不進行任何實際的業務處理。視圖可以向模型查詢業務狀態,但不能改變模型。視圖還能接受模型發出的數據更新事件,從而對用戶界面進行同步更新。
模型:應用對象。
模型是應用程序的主體部分。 模型代表了業務數據和業務邏輯; 當數據發生改變時,它要負責通知視圖部分;一個模型能为多個視圖提供數據。由於同一個模型可以被多個視圖重用,所以提高了應用的可重用性。
控制器:邏輯處理、控制實體數據在視圖上展示、調用模型處理業務請求。
當 Web 用戶單擊 Web 頁面中的提交按鈕來發送 HTML 表單時,控制器接收請求並調用相應的模型組件去處理請求,然後調用相應的視圖來顯示模型返回的數據。[2-3]
編輯本段優點
采用三層軟件設計架構後,軟件系統在可擴展性和可复用性方面得到極大提高,在資源分配策略設計合理運用的同時,軟件的性能指標得到提升,系統的安全性也得到改善。
三層體系結構對Web應用的軟件架構產生很大影響,促進基於組件的設計思想, 產生了許多開發Web層次框架的實現技術。較之兩級結構來說,三層結構修改和維護上 更加方便。目前開發B/S結構的Web應用系統廣泛采用這種三層體系結構。 [3]
編輯本段運行機制
在 MVC 模式中,Web 用戶向服務器提交的所有請求都由控制器接管。接受到請求之後,控制器負責决定應該調用哪個模型來進行處理;然後模型根據用戶請求進行相應的業務邏輯處理,並返回數據;最後控制器調用相應的視圖來格式化模型返回的數據,並通過視圖呈現给用戶。[3][7]
1.MVC模式概述
MVC 模式包括三個部分:模型(Model)、視圖(VIEW)和控制器(CONTROLLER),分別對應於內部數據、數據表示、輸入輸出控制部分。模型
是軟件獨立於外部表現的內在抽象,是包含了核心數據和邏輯功能計算的應用,它獨立於界面的表達。視圖View是模型的外在表現。它從模型中獲得
信息,並在屏幕上加以顯示。控制器Controller定義用戶界面對用戶輸入的響應,它的職責是對模型中任何變化的傳播加以控制。確保用戶界面和模型
之間的對應關系,協調着模型和視圖的工作。模型和視圖的分離使得一個模型可以對應多個視圖。模型、視圖和控制器之間的關系如下圖所示。用戶輸入表示
用戶和用戶界面之間的相互交互關系,通過控制器,把用戶輸入與用戶界面連接起來。視圖通過控制器來響應用戶輸入。視圖和模型之間是相互協作的關系。
即一個是同得到用戶的輸入,以此來改變模型狀態。同時,模型又將自己的改變狀態通知给它的所有視圖,而視圖在得知這些變化之後,自己做相應
的改變,將視圖的改變及時展現给不同的用戶。這样做的好處是將用戶輸入、用戶界面的顯示和模型分離開來,減小了他們之間的耦合度。MVC將模型
與視圖的分離,不僅提高了系統的靈活性和复用性,而且为用戶軟件界面的提議接哦故的研究奠定了基礎。
MVC,model-view-control,這是一個架構模式,也是一種開發模式。
不論設計模式還是架構模式,MVC都是最經典的模式。
Symbian OS作为最熱的手機開發平台之一,Symbian OS是一個微內核的系統,它應用了大量的模式進行高度模塊化設計,
便於根據需求的變更和新環境進行擴展和改善适應。MVC便是其中一個模式。
回顧一下MVC的含義吧,
MVC設計模式它提供一種能夠分別修改軟件的不同模塊的能力,提高軟件的健壯性和复用性。
MVC模式能夠幫助軟件的設計者使用面向對象的設計原則,比如:開閉原則,通過繼承而不是修改存在的基類,增加新的代碼和類來擴展設計。 Model:包含和操作程序中的數據,它最重要的部分是應用程序的數據結構 View:定義數據模型向用戶顯示的方式;View傳遞接收到的命令和請求给controller,View是圖形接口,它從Model讀,並且獲取需要顯示的數據给用戶 Controller:定義用戶接口對收到的命令和請求的處理,操作Model中的數據並更新View
MVC可以使得開發者根據面向對象編程的基本原則來設計他們的應用程序,開發者在實現之前必須决定應用程序的那些部分是可以擴展的哪些是不行的。設計階段之後,代碼的開發從一些基類開始,可以通過增加新的特別的類來擴展。MVC和OCP是一致的。
在Symbian S60中,MVC的使用是基於Avkon的GUI框架,框架提供基類實現Model,View和Controller。
這些類可以被應用程序設計者擴展。 Avkon的基類: -CAknApplication,應用程序的基類 -CAknDocument,Modle的基類 -CAknAppUI,Controller的基類 View的父類AVkon沒有提供,但是可以從CONE環境繼承。
應用程序架構(Application Framework)
1、S60應用程序架構
S60平台在底層Uikon應用程序框架上添加了一個用戶界面層(Avkon)。Avkon提供了一套特別为S60設計的UI組件和應用程序框架。
1.1、S60應用程序結構
1.1.1、模型(Model)—視圖(View)—控制器(Controller)模式(MVC)
MVC模式在S60 UI應用程序中是一個通用的設計模式。應用程序被分離成不同的邏輯部分;它們包裝了應用程序的不同方面。每個部分都用特殊的任務。MVC模式分離了應用程序設計,使模型(Model)的代碼得到重用。
模型(Model):
-
封裝了應用程序的狀態和功能。
-
通知視圖(View)進行切換。
-
響應來自視圖(View)的狀態查詢。
視圖(View):
-
呈現模型(View)。
-
接收來自模型(Model)的視圖更新通知。
-
將用戶的輸入發送给控制器(Controller)。
控制器(Controller):
-
定義了應用程序的行为。
-
將用戶操作與模型(Model)更新相映射。
-
響應視圖(View)切換請求。
1.1.2、S60應用程序結構和MVC
S60應用程序通常分離成兩大部分,引擎(Engine)和UI。應用程序引擎,也就是應用程序模型,用來處理邏輯運算和數據結構表示。應用程序UI,用來在屏幕上顯示應用程序的數據和全部的行为。在基於S60應用程序框架下,實現引擎和UI分離模式有三種方式:傳統的Symbian OS應用程序構架、對話框構架、視圖切換構架。不同的構架只反映UI的實現,應用程序類(CAknApplication繼承類)和文檔類(CAknDocument)並沒有區別。
應用程序UI的組成:
CAknApplication繼承類:
-
應用程序框架的启動對象。
-
定義了應用程序的性質。
-
創建文檔(CAknDocument)類。
CAknDocument繼承類:
-
創建AppUi(Controller)。
-
提供了應用程序數據的持久化功能。
CAknAppUi或CAknViewAppUi繼承類(Controller):
-
基類的選擇依賴於應用程序架構。
-
處理應用程序事件。
-
控制應用程序模型(Model)。
-
負責切換視圖(View)。
CCoeControl繼承類(View):
-
顯示模型(Model)狀態。
-
接收用戶輸入。
-
向控制器通知相關事件。
-
根據模型(Model)變化更新顯示。
應用程序引擎(Engine):
-
封裝了應用程序數據和狀態。
-
封裝了非UI依賴的功能,能夠在其他UI平台重用。
-
通常以類庫的形式實現。
-
由AppUi直接操作。
1.2、傳統的Symbian OS 應用程序架構
1.3、對話框(Dialog)架構
1.4、視圖切換架構
視圖切換架構是一種機制,它允許應用程序注冊視圖,並且在任意時刻只有一個視圖被激活。視圖切換架構並沒有規定視圖的具體內容,而是为視圖在屏幕上顯示提供了支持。
與傳統的Symbian OS應用程序架構不同的是,AppUi類(Controller)繼承自CAknViewAppUi,並且引入了一個新類CAknView作为AppUi(Controller)和容器(Control)之間的媒介。在視圖切換架構中,CAknView派生類稱为Avkon視圖(View),它擁有一個容器。AppUi創建每個Avkon視圖並且在服務器端進行注冊。切換視圖時使用視圖UID進行切換。
1.4.1、創建視圖
CAknView派生類實例的創建通常在AppUi對象的ConstructL()方法中進行。該方法中將所有的視圖進行注冊,並設置一個默認視圖:
void CMyViewArchAppUi::ConstructL()
{
BaseConstructL();
CMyViewArchAppView1* view1 = new(ELeave) CMyViewArchAppView1;
CleanupStack::PushL(view1);
view1->ConstructL();
AddViewL(view1); // Transfer ownership to CAknAppUi.
CleanupStack::Pop(); // pop view1.
CMyViewArchAppView2* view2 = new(ELeave) CMyViewArchAppView2;
CleanupStack::PushL(view2);
view2->ConstructL();
AddViewL(view2); // Transfer ownership to CAknAppUi.
CleanupStack::Pop(); // pop view2.
SetDefaultViewL(*view1);
// More code
}
另外,視圖本身並不具有繪制控件的能力,所以每個視圖需要包含派生自CCoeControl和MCoeControlObserver的控件容器:
class CMyViewArchAppView1Container : public CCoeControl, MCoeControlObserver
1.4.2、Avkon視圖類
每個Avkon視圖類就像一個小型的AppUi。它必須提供一個Id()函數,從而系統可以標識這個視圖,並且必須實現DoActivateL()和DoDeactivate()函數來完成視圖激活和注銷時的具體操作,此外還應該實現HandleForegroundEventL()、HandleCommandL()和HandleStatusPaneSizeChange()函數用於處理各種事件。
DoActivateL()
當視圖被激活時,將調用該函數。該函數負責實例化並顯示視圖的控件。在視圖被注銷前可能多次調用該函數,所以實現該函數必須考慮重复實例化控件。
DoDeactivate()
當視圖被注銷時,將調用該函數,負責銷毀視圖中的控件。只有當應用程序退出時,或者激活同一個程序的另一個視圖時,激活的視圖才被注銷。該函數不能異常退出。
HandleForegroundEventL()
該函數只有當視圖處於激活狀態下才會被調用,也就是在DoActivateL()調用之後和DoDeactivate()調用之前這段時間。當視圖到達前台時,視圖將接收到HandleForegroundEventL(ETrue)。當視圖從前台被移除時,視圖將接受到HandleForegroundEventL(EFalse)。只用視圖在前台的狀態實際改變時才會調用該函數。因为擁有視圖的應用程序可能在前台和後台間來回切換多次,所以該函數會被調用多次。函數的實現可能用來設置焦點或控制屏幕更新。
HandleCommandL()
當視圖的菜單發出消息事件時時,該函數將調用來處理菜單事件。
HandleStatusPaneSizeChange()
由於狀態面板的改變導致客戶區域大小的變化將調用該函數。
視圖在活動期間接收事件的典型順序:
1. DoActivateL() 2. HandleForegroundEventL(ETrue) 3. HandleForegroundEventL(EFalse) 4. DoDeactivate()
成對出現HandleForegroundEventL()可能在視圖活動期間多次調用。DoActivateL()可能在DoDeactivate()調用之前多次調用。
1.4.3、視圖資源(AVKON_VIEW)
典型情況下,視圖都需要擁有菜單項或CBA。通過將視圖資源(AVKON_VIEW)的ID傳遞给視圖的BaseConstructL()方法,可以很容易为每個視圖提供自己唯一的菜單項。
RESOURCE AVKON_VIEW
{ hotkeys= ; menubar= ; cba= ; }
1.4.4、視圖切換
本地視圖切換
本地視圖切換就是在同一應用程序內進行視圖切換。切換時只需要指定需要切換視圖的UID:
ActivateLocalViewL( TUid::Uid(1) );
外部視圖切換
2、事件處理
2.1、命令(Commands)事件
命令事件是由Avkon框架產生,用來響應用戶選擇菜單項和系統特殊事件。AppUi或Avkon視圖將會通過HandleCommandL()方法處理命令事件。
2.1.1、AppUi對命令事件的處理
在傳統的應用程序架構中,AppUi的HandleCommandL()方法將處理命令事件。
void CContainerAppUi::HandleCommandL(TInt aCommand) { switch (aCommand) { case EEikCmdExit: { Exit(); break; } case ECmdXXX: { // implementation of cut operation break; } ... default: break; } }
用戶自定義的命令ID(如ECmdXXX)在.hrh文件中定義,Avkon系統命令ID(如EAknSoftKeyBack)定義在avkon.hrh中。EEikCmdExit是系統標准的應用程序退出命令ID。
2.1.2、Avkon視圖對命令事件的處理
在視圖切換應用程序架構中,當前視圖的HandleCommandL()方法將處理命令事件。
void CMyAppView1::HandleCommandL(TInt aCommand) { switch (aCommand) { case EMyAppCmdSwitchToView2:
{ AppUi()->ActivateLocalViewL(KView2Id); break;
} case ECmdXXX: { // Implement cut operation break; } // ... Other view-specific command handling here case EAknSoftkeyBack: { ((MEikCommandObserver*)AppUi())->ProcessCommandL(EEikCmdExit); break; } default:
AppUi()->HandleCommandL(aCommand); break; } }
2.2、按鍵事件與控件棧
按鍵事件是用戶與應用程序交互時鍵盤所產生的事件。鍵被按下時被FEP轉換成相應的鍵事件,最終由應用程序框架傳遞给當前的應用程序。應用程序框架只將鍵事件傳遞给在控件棧中的對象。AppUi是缺省的鍵事件處理對象,但是CCoeControl派生類的UI控件應該第一時間處理鍵事件。因此必須將控件放入控件棧中,通常情況下由AppUi的AddToStackL()方法將控件放入控件棧中。
在傳統和對話應用程序構架中,在AppUi的構造階段(Construction()方法)將控件放入控件棧,控件將處於棧頂。當按鍵事件產生時,應用程序框架首先要求處於棧頂的控件處理事件,如果控件沒有處理事件,那麼應用程序控件將要求棧中的第二個控件處理,以此類推,當棧中的所以控件都沒有處理是,將由AppUi的HandleKeyEventL()方法處理。
void CMyAppUi::ConstructL() { BaseConstructL(); iCurrentView = CMyAppMainView::NewL(ClientRect()); AddToStackL(iCurrentView); // to enable key events }
在視圖切換應用程序架構中,控件放入控件棧通常在視圖對象的DoActivateL()中控件完成構造後進行。
void CMyView::DoActivateL(const TVwsViewId&, TUid, const TDesC8&) { if(!iUiControl) // iUiControl is CCoeControl-derived UI ctrl { iUiControl = CMyUiControl::NewL(this, ClientRect()); AppUi()->AddToStackL(*this, iUiControl); // to ctrl stack. } }
當不希望該控件再接收按鍵事件時,應該使用AppUi的RemoveFromStack()方法將控件移除控件棧。在視圖切換應用程序機構中,通常在視圖對象的DoDeactivate()方法中實現從棧中移除控件。
void CMyView::DoDeactivate() { if(iUiControl) { AppUi()->RemoveFromStack(iUiControl);
} delete iUiControl; iUiControl = NULL; }
在控件棧中的控件響應按鍵事件是通過調用他們的OfferKeyEventL()方法來實現的。調用的順序是以控件在控件棧中的順序進行,最後放入的控件將先調用它的OfferKeyEventL()方法。OfferKeyEventL()方法必須重寫,因为它的默認實現不處理任何事件。當控件處理了事件就應返回EKeyWasConsumed,因为如果返回EKeyWasNotConsumed,應用程序框架將使控件棧中的下一個控件來處理。如果所以的控件都返回EKeyWasNotConsumed,那麼最後將在AppUi的HandleKeyEventL()方法中處理。
TKeyResponse CMyUiControl::OfferKeyEventL(const TKeyEvent& aKeyEvent,
TEventCode aType) { TKeyResponse response = EKeyWasNotConsumed; // pass key press events to the listbox. It will typically // consume Up, Down, and Selection keys. if (aType == EEventKey && iListBox) { response = iListBox->OfferKeyEventL(aKeyEvent, aType); } return response; }
From:CSDN
留言列表