MVC in MFC or WTL

关于MVC

    MVC是一种分离用户界面和业务逻辑的开发架构。
    ●  模型(Model):体现应用程序业务信息(数据)和业务数据的处理。所有有关数据库的操作只限制在该模型中。
    ●  视图(View): 代表用户交互界面
    ●  控制器(Contrlloer):控制器负责接收、截取用户请求(如键盘输入,鼠标点击),但不处理业务信息,它只把用户的信息传递给模型,告诉模型该做什么,由模型返回最终的处理结果。控制器再选择符合要求的视图返回给用户。

背景

    做Web或者Java的对MVC会比较熟悉,对于用MFC开发桌面应用程序的developer来说,已经习惯于拖一个按钮,然后双击,在CxxxDlg.cpp中添加事件响应。随着业务逻辑的复杂,这一个文件包含了所有的界面代码,逻辑处理,数据操作…。频繁的界面修改可能会破坏比较稳定的业务代码。将业务逻辑分离出来,由一个控制器负责,就可以避免这种干扰。

    去搜索了下MVC在桌面应用程序开发上的资料,找出两篇:
    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调用Model(CalculatorModel)的运算方法得到计算结果,再回传给View更新显示。由控制器通知视图刷新

    看完这两个已经搞不定到底哪种才是真正的MVC,后来又查了资料说:
   
MVC模式有许多变体。第一种,由模型通知视图刷新,称为主动MVC;如果由控制器更新模型以后通知视图,称为被动MVC结构。在许多应用中,没有明显的控制器角色,也没有视图嵌套。可见根据实际需要,构成MVC的三个模式上都可能出现变化。Web浏览器就是被动MVC结构的一个实例。

实践

    我将上面第二个用C#写的计算器的例子,改用WTL作为界面库,采用MVC架构设计。(据说MVC不适合小中型引用程序,多大才算中小呢?只有自己去开发体会了)

    image

    先来看下代码结构:
    image
     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:     // 先析构哪个呢?

 23:     delete pController;

 24:     delete pModel;

    2. CalcController类构造函数需要传递Model和View的指针,接收从View传递来的用户事件,然后调用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:     // 用户点击数值(0~9)

 29:     virtual void ClickValue(double dValue)

 30:     {

 31:         // ......省略

 32: 

 33:         // 生成新操作数

 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:         // 计算出上一次运算符的结果

 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中调用View的方法(ShowOperateResult,ShowOperateExpression)来更新View中的显示。那么View又是怎么传递用户事件给Controller的呢?
    3. 通过m_pCalcView->AddController(this)  将Controller传给View。View就可以在接收到用户请求之后,就可以调用Controller中的事件处理函数(ClickValue,ClickOperate)

  1: class CalcView

  2: {

  3: public:

  4:     CalcView()

  5:         : m_pCalcController(NULL)

  6:     {

  7:     }

  8: 

  9:     void AddController(CalcController* pController)

 10:     {

 11:         ATLASSERT(pController);

 12:         m_pCalcController = pController;

 13:     }

 14: 

 15: // interface

 16:     // 显示运算表达式

 17:     virtual void ShowOperateExpression(CString strExpression) {}

 18:     // 显示运算结果

 19:     virtual void ShowOperateResult(CString strResutl) {}

 20: 

 21: protected:

 22:     CalcController* m_pCalcController;   

 23: };

    CMainDlg从CalcView继承,重载接口实现计算结果的显示。MVC的一个目标就是把用户界面分离,如果要换一个界面,或者改用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类,只负责数值计算,以及返回运算结果

  1: class CalcModel

  2: {

  3: public:

  4:     CalcModel() : m_dResult(0)

  5:     {

  6:     }

  7: 

  8:     // 执行计算,返回计算结果

  9:     double Calc(OPERATE_TYPE op_type, double dValue)

 10:     {

 11:         switch ( op_type )

 12:         {

 13:         case OP_NULL:   // 第一个操作数默认执行和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; // 结果

 58: };

 

完整代码:MVC in MFC or WTL

你可能感兴趣的:(mvc)