zoukankan      html  css  js  c++  java
  • 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

    微信号:yinxufeng503
    本博客文章,有原创和网络搜集,转载请注明出处

    分享到: 更多
  • 相关阅读:
    OpenCV人脸识别的原理 .
    图像特征提取三大法宝:HOG特征,LBP特征,Haar特征
    Qt开发者关于QThread的咆哮——你们都用错了
    Qt 线程基础(QThread、QtConcurrent等)
    [saiku] 在 Tomcat 下部署 saiku
    [saiku] 免登陆进入管理后台
    [saiku] 简化/汉化/设置默认页
    [saiku] schema文件分析
    [saiku] 通过管理台配置用户、schema和数据源
    [saiku] 简介、下载、安装和教程
  • 原文地址:https://www.cnblogs.com/yinxufeng/p/2591185.html
Copyright © 2011-2022 走看看