MVC in MFC or WTL - yinxufeng - 博客园
MVC in MFC or WTL - yinxufeng - 博客園
關(guān)于MVC
??? MVC是一種分離用戶界面和業(yè)務(wù)邏輯的開發(fā)架構(gòu)。
??? ●? 模型(Model):體現(xiàn)應(yīng)用程序業(yè)務(wù)信息(數(shù)據(jù))和業(yè)務(wù)數(shù)據(jù)的處理。所有有關(guān)數(shù)據(jù)庫的操作只限制在該模型中。
??? ●? 視圖(View): 代表用戶交互界面
??? ●? 控制器(Contrlloer):控制器負責(zé)接收、截取用戶請求(如鍵盤輸入,鼠標(biāo)點擊),但不處理業(yè)務(wù)信息,它只把用戶的信息傳遞給模型,告訴模型該做什么,由模型返回最終的處理結(jié)果。控制器再選擇符合要求的視圖返回給用戶。
背景
??? 做Web或者Java的對MVC會比較熟悉,對于用MFC開發(fā)桌面應(yīng)用程序的developer來說,已經(jīng)習(xí)慣于拖一個按鈕,然后雙擊,在CxxxDlg.cpp中添加事件響應(yīng)。隨著業(yè)務(wù)邏輯的復(fù)雜,這一個文件包含了所有的界面代碼,邏輯處理,數(shù)據(jù)操作…。頻繁的界面修改可能會破壞比較穩(wěn)定的業(yè)務(wù)代碼。將業(yè)務(wù)邏輯分離出來,由一個控制器負責(zé),就可以避免這種干擾。
??? 去搜索了下MVC在桌面應(yīng)用程序開發(fā)上的資料,找出兩篇:
??? 1. vckbase上的,采用MFC編制MVC模式之球體演示程序。處理流程:Controller(CMVCSphereDlg)捕獲后用戶輸入后通知Model(CSphere),Model再通知兩個View(TextView & CGraphicView)更新顯示。由模型通知視圖刷新
??? 2. codeproject上的,Simple Example of MVC (Model View Controller) Design Pattern for Abstraction。處理流程:View(frmCalcView)捕獲用戶事件后傳遞給Controller(CalcController), Controller調(diào)用Model(CalculatorModel)的運算方法得到計算結(jié)果,再回傳給View更新顯示。由控制器通知視圖刷新
??? 看完這兩個已經(jīng)搞不定到底哪種才是真正的MVC,后來又查了資料說:
??? MVC模式有許多變體。第一種,由模型通知視圖刷新,稱為主動MVC;如果由控制器更新模型以后通知視圖,稱為被動MVC結(jié)構(gòu)。在許多應(yīng)用中,沒有明顯的控制器角色,也沒有視圖嵌套。可見根據(jù)實際需要,構(gòu)成MVC的三個模式上都可能出現(xiàn)變化。Web瀏覽器就是被動MVC結(jié)構(gòu)的一個實例。
實踐
??? 我將上面第二個用C#寫的計算器的例子,改用WTL作為界面庫,采用MVC架構(gòu)設(shè)計。(據(jù)說MVC不適合小中型引用程序,多大才算中小呢?只有自己去開發(fā)體會了)
???
??? 先來看下代碼結(jié)構(gòu):
???
???? Model,View,Controller分開,Resource存在一些資源文件。
??? 1. 先來看下App類,怎么將三者組織起來的
1: CMessageLoop theLoop; 2: _Module.AddMessageLoop(&theLoop); 3: 4: // View 5: CMainDlg dlgMain; 6: if(dlgMain.Create(NULL) == NULL) 7: { 8: ATLTRACE(_T("Main dialog creation failed!\n")); 9: return 0; 10: } 11: // Model 12: CalcModel* pModel = new CalcModel(); 13: // Controller 14: CalcController* pController = new CalcController(pModel, &dlgMain); 15: 16: dlgMain.ShowWindow(nCmdShow); 17: 18: int nRet = theLoop.Run(); 19: 20: _Module.RemoveMessageLoop(); 21: 22: // 先析構(gòu)哪個呢? 23: delete pController; 24: delete pModel;??? 2. CalcController類構(gòu)造函數(shù)需要傳遞Model和View的指針,接收從View傳遞來的用戶事件,然后調(diào)用Model和View中中方法來完成用戶請求。
1: // 運算控制器 2: // 3: /// 4: #ifndef _CALCCONTROLLER_H_ 5: #define _CALCCONTROLLER_H_ 6: 7: #include "CalcView.h" 8: #include "CalcModel.h" 9: 10: class CalcController 11: { 12: protected: 13: CalcModel* m_pCalcModel; 14: CalcView* m_pCalcView; 15: 16: public: 17: CalcController(CalcModel* pModel, CalcView* pView) 18: : m_pCalcModel(pModel) // Model 19: , m_pCalcView(pView) // View 20: { 21: ATLASSERT(m_pCalcView); 22: ATLASSERT(m_pCalcModel); 23: m_pCalcView->AddController(this); // 將controller傳給view 24: } 25: 26: // ......省略部分代碼 27: 28: // 用戶點擊數(shù)值(0~9) 29: virtual void ClickValue(double dValue) 30: { 31: // ......省略 32: 33: // 生成新操作數(shù) 34: CString strValue; 35: strValue.Format(_T("%g"), dValue); 36: m_strOperateValue += strValue; 37: 38: m_clickType = click_value; 39: m_pCalcView->ShowOperateResult(m_strOperateValue); 40: } 41: 42: // 用戶點擊操作(+, -, *, ÷) 43: virtual void ClickOperate(OPERATE_TYPE op_type) 44: { 45: // ......省略 46: 47: // 計算出上一次運算符的結(jié)果 48: double dResult; 49: dResult = m_pCalcModel->Calc(m_operaType, _tstof(m_strOperateValue)); 50: CString strResutl; 51: strResutl.Format(_T("%f"), dResult); 52: strResutl.TrimRight('0'); 53: strResutl.TrimRight('.'); 54: 55: // 更新view 56: m_pCalcView->ShowOperateExpression(m_strExpression); 57: m_pCalcView->ShowOperateResult(strResutl); 58: 59: // ......省略 60: } 61: 62: // ......省略 63: }; 64: 65: 66: #endif // _CALCCONTROLLER_H_??? 注意到,在Controller中,View和Model是沒有直接交互的。通過在Controller中調(diào)用View的方法(ShowOperateResult,ShowOperateExpression)來更新View中的顯示。那么View又是怎么傳遞用戶事件給Controller的呢?
??? 3. 通過m_pCalcView->AddController(this)? 將Controller傳給View。View就可以在接收到用戶請求之后,就可以調(diào)用Controller中的事件處理函數(shù)(ClickValue,ClickOperate)
??? CMainDlg從CalcView繼承,重載接口實現(xiàn)計算結(jié)果的顯示。MVC的一個目標(biāo)就是把用戶界面分離,如果要換一個界面,或者改用MFC編寫界面了,只需要重載接口改變顯示方式而已。
1: class CMainDlg : public CDialogImpl<CMainDlg> 2: , public CUpdateUI<CMainDlg> 3: , public CMessageFilter 4: , public CIdleHandler 5: , public CalcView 6: { 7: public: 8: // ......省略 9: 10: BEGIN_MSG_MAP(CMainDlg) 11: MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog) 12: COMMAND_ID_HANDLER(IDC_BUTTON_VAULE0, OnClickValue) 13: COMMAND_ID_HANDLER(IDC_BUTTON_VAULEP, OnClickDecimalPoint) 14: COMMAND_ID_HANDLER(IDC_BUTTON_ADD, OnClickOperate) 15: // ......省略 16: END_MSG_MAP() 17: 18: // 19: LRESULT OnClickValue(WORD, WORD , HWND hWndCtl , BOOL&) 20: { 21: CString strValue; 22: ::GetWindowText(hWndCtl, strValue.GetBuffer(1), 2); 23: strValue.ReleaseBuffer(); 24: 25: m_pCalcController->ClickValue(_tstof(strValue)); 26: 27: return 0; 28: } 29: 30: LRESULT OnClickDecimalPoint(WORD, WORD , HWND , BOOL&) 31: { 32: m_pCalcController->ClickDecimalPoint(); 33: return 0; 34: } 35: 36: LRESULT OnClickOperate(WORD, WORD wID, HWND , BOOL&) 37: { 38: switch (wID) 39: { 40: case IDC_BUTTON_ADD: 41: m_pCalcController->ClickOperate(OP_ADD); 42: break; 43: case IDC_BUTTON_SUB: 44: m_pCalcController->ClickOperate(OP_SUB); 45: break; 46: case IDC_BUTTON_MULT: 47: m_pCalcController->ClickOperate(OP_MULT); 48: break; 49: case IDC_BUTTON_DIVE: 50: m_pCalcController->ClickOperate(OP_DIVE); 51: break; 52: } 53: 54: return 0; 55: } 56: 57: // 58: 59: // Override 60: void ShowOperateResult(CString strResutl) 61: { 62: GetDlgItem(IDC_STATIC_RESULT).SetWindowText(strResutl); 63: } 64: 65: void ShowOperateExpression(CString strExpression) 66: { 67: GetDlgItem(IDC_STATIC_EXPRESSION).SetWindowText(strExpression); 68: } 69: };??? 4. 最后看一下Model類,只負責(zé)數(shù)值計算,以及返回運算結(jié)果
1: class CalcModel 2: { 3: public: 4: CalcModel() : m_dResult(0) 5: { 6: } 7: 8: // 執(zhí)行計算,返回計算結(jié)果 9: double Calc(OPERATE_TYPE op_type, double dValue) 10: { 11: switch ( op_type ) 12: { 13: case OP_NULL: // 第一個操作數(shù)默認執(zhí)行和0相加 14: case OP_ADD: // 加法 15: Add(dValue); 16: break; 17: case OP_SUB: // 減法 18: Sub(dValue); 19: break; 20: case OP_MULT: // 乘法 21: Mult(dValue); 22: break; 23: case OP_DIVE: // 除法 24: Dive(dValue); 25: break; 26: } 27: 28: return m_dResult; 29: } 30: 31: protected: 32: // 加 33: void Add(double dValue) 34: { 35: m_dResult += dValue; 36: } 37: 38: // 減 39: void Sub(double dVaule) 40: { 41: m_dResult -= dVaule; 42: } 43: 44: // 乘 45: void Mult(double dVaule) 46: { 47: m_dResult *= dVaule; 48: } 49: 50: // 除 51: void Dive(double dVaule) 52: { 53: m_dResult /= dVaule; 54: } 55: 56: protected: 57: double m_dResult; // 結(jié)果 58: };?
完整代碼:MVC in MFC or WTL
超強干貨來襲 云風(fēng)專訪:近40年碼齡,通宵達旦的技術(shù)人生總結(jié)
以上是生活随笔為你收集整理的MVC in MFC or WTL - yinxufeng - 博客园的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 写在11期培训班第五次课后作业前面的话
- 下一篇: Android软键盘的显示与隐藏