好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

MVC in MFC or WTL

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不适合小中型引用程序,多大才算中小呢?只有自己去开发体会了)

    

    先来看下代码结构: 
      
     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

作者: yinxufeng

出处: http://www.cnblogs.com/yinxufeng/

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

分类:  WTL ,  系统架构

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于MVC in MFC or WTL的详细内容...

  阅读:43次