Introduction to MVC for Desktop ApplicationCourse C1001He Shiming2010-9
MVC in Windows Desktop Application with Cross-Platform Considerationideal desktop application development
Obviously:MFC is not a good architecture/standardMost likely, there isn’t such an ideal architecture, like the Rails framework, or even something close to CocoaWindows API and C++ isn’t helping that much on architecture of productsThe hard work has to be done by ourselves
Revised MVC Architecture for UsDataControllerViewModel
Revised MVC Architecture for UsModel represents information, an underlying file format, database connections and queriesView represents user interface, the window and displayed content designed by application logicController is the action processor, responding to command sent from views, pick the right model to process it, and update views to reflect action result
An Example, Redesigning Notepad
An Example, Redesigning NotepadFrame is the same (this is the root view):class CMainFrame: public CFrameWindowImpl<CMainFrame>{public:CEditm_edit; BEGIN_MSG_MAP(CMainFrame) MESSAGE_HANDLER(WM_CREATE, OnCreate)MESSAGE_HANDLER(WM_SIZE, OnSize) COMMAND_ID_HANDLER(ID_FILE_NEW, OnFileNew) COMMAND_ID_HANDLER(ID_FILE_OPEN, OnFileOpen) COMMAND_ID_HANDLER(ID_FILE_SAVE, OnFileSave)END_MSG_MAP()...
An Example, Redesigning NotepadWM_CREATE handling is the same (still a part of view):LRESULT CMainFrame::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled){m_edit.CreateWindow(m_hWnd, WS_CHILD|WS_VISIBLE|...WM_SIZE too (another part of view)LRESULT CMainFrame::OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled){ RECT rc_client;GetClientRect(&rc_client);m_edit.SetWindowPos(NULL, &rc_client...
An Example, Redesigning NotepadBefore we go further, we research and design this application, in other words, we’ll decide what controller does and what model does
An Example, Redesigning NotepadWe knew that Notepad should be able to process text files, so we should have a TextFile class that handles reading and writing, this would be a single threaded, static class whose only job is to accept a file name, and return std::wstringto us (this is a model)
An Example, Redesigning NotepadSo we have:class TextFile{public: static boolReadFile(constwchar_t* filename, std::wstring& content_out); static boolWriteFile(constwchar_t* filename,constwchar_t* content_in);};
An Example, Redesigning NotepadWe knew that user may issue command such as OpenFile, SaveFile, NewFile, so we design a FileController class that has these methodsAdditionally, this controller should have a buffer that maintains the current displayed text for editing (for use with views), thus we should have methods such as SetContent, and GetContentTo deal with large files and potential slowness in networked opening, we may need to make OpenFile, SaveFileasynchronized, therefore we may need a method named IsLastFileOpCompleted for views to decide when toretrieve content (this is a controller)
An Example, Redesigning NotepadSo we have:class FileController{public:boolOpenFile(constwchar_t* filename);boolSaveFile(constwchar_t* filename);boolNewFile();boolSetContent(constwchar_t* content);wchar_t* GetContent();boolIsLastFileOpComplete();private:std::wstringm_buffer; // boost::shared_ptr<boost::thread> m_fileop_thread; // boost::mutexm_buffer_mutex;};
An Example, Redesigning NotepadWhat about views?LRESULT CMainFrame::OnFileOpen(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled){CFileDialogfd(TRUE, NULL, NULL, OFN_EXPLORER, L”*.txt”, m_hWnd); if (!fd.DoModal(m_hWnd)) return 0; if (!m_view.m_filecontroller.OpenFile(fd.m_szFileName)) {MessageBox(L”There is a problem with file opening”); return 0; }SetTimer(TIMER_CHECKFILEOPENCOMPLETE, 50); // in WM_TIMER, we check m_filecontroller.IsFileOpComplete() // to decide whether to refresh the m_view using the content // retrieved via m_filecontroller.GetContent...
An Example, Redesigning NotepadWe used timed polling to make sure our controller doesn’t know any details about views, and is thus directly portableOf course timed polling is bad, it consumes and wastes extra resources while checking for return values, due to the stupidity in its designBut it can be easily controlled, when it’s required to cancel such a file operation, you kill the timer, and tell the controller to stop file operation
An Example, Redesigning NotepadTimed Polling (Timer) versus Mediator/Observer (Java Callback) versus Delegates (C# Callback)Observer pattern should be used in strict sense*MVC is macro-architecture and design pattern is micro-architecture
An Example, Redesigning NotepadApplying Observer pattern:class IFileObserver{public: virtual void SetFileOpComplete(booliscomplete) = 0;};class FileObserver: public IFileObserver{public: void SetReceiveWindow(HWND hwnd) {m_hwnd_receive = hwnd; } virtual void SetFileOpComplete(booliscomplete) { // call SetWindowText depending on |iscomplete|...
An Example, Redesigning NotepadApplying Observer pattern:class FileController{public: void AttachObserver(IFileObserver* observer); void DetachObserver();...void _OpenFile_WorkerThread(constwchar_t* filename, bool& ret) { // after file read successm_observer->SetFileOpComplete(true); }...
An Example, Redesigning NotepadTo implement multi-tabbing, we put FileController inside CEdit, and replicate CEdit (via std::vector<CEdit>) so that each instance has a controller, a TabController might also be needed to manage tabsTo implement syntax highlight, we implement HiliteNode (base model), HiliteParser (model) that translates wchar_t* string into std::vector<HiliteNode>, and make sure our FileController can output stuff returned by HiliteParser, then subclass CEdit to implement actual paintingFor unicode BOM, only need to revise TextFile class
Redesigning Notepad, What We’ve Achieved:Complete isolation of UI and application logicComplete isolation of application logic and low-level data formatsPossible for independent development for most componentsPossible to run unit-test for each components, so that product quality can be assuredPossible to easily port to other platformsClear application logic for future referenceReusable components for future projects
Arch. Ver. of Notepad v.s. No-arch. Ver.Primary technical difference is class design, class relation, class differenceUnderlying logic, including frame window management, low-level file reading/writing are mostly the same
When Portability is Our Concern:Remember Cocoa/Objective-C is compatible with C++Establish user interface on Cocoa framework like before, the equivalent view on Windows cannot be usedBind Cocoa’s interface controller actions to our real portable controllersOnly controllers and models can be 100% portable
Architecture and MVC is Mostly About:Class design
Recommended Readings
References Regarding Design Patternshttp://stackoverflow.com/questions/516411/raw-function-pointer-from-a-bound-method/516537http://stackoverflow.com/questions/946834/is-there-a-design-pattern-that-deals-with-callback-mechanism
References Regarding MVC and Othershttp://en.wikipedia.org/wiki/Model–View–Controllerhttp://www.oracle.com/technetwork/articles/javase/mvc-136693.htmlhttp://www.djangoproject.com/http://code.google.com/webtoolkit/

MVC for Desktop Application - Part 4

  • 1.
    Introduction to MVCfor Desktop ApplicationCourse C1001He Shiming2010-9
  • 2.
    MVC in WindowsDesktop Application with Cross-Platform Considerationideal desktop application development
  • 3.
    Obviously:MFC is nota good architecture/standardMost likely, there isn’t such an ideal architecture, like the Rails framework, or even something close to CocoaWindows API and C++ isn’t helping that much on architecture of productsThe hard work has to be done by ourselves
  • 4.
    Revised MVC Architecturefor UsDataControllerViewModel
  • 5.
    Revised MVC Architecturefor UsModel represents information, an underlying file format, database connections and queriesView represents user interface, the window and displayed content designed by application logicController is the action processor, responding to command sent from views, pick the right model to process it, and update views to reflect action result
  • 6.
  • 7.
    An Example, RedesigningNotepadFrame is the same (this is the root view):class CMainFrame: public CFrameWindowImpl<CMainFrame>{public:CEditm_edit; BEGIN_MSG_MAP(CMainFrame) MESSAGE_HANDLER(WM_CREATE, OnCreate)MESSAGE_HANDLER(WM_SIZE, OnSize) COMMAND_ID_HANDLER(ID_FILE_NEW, OnFileNew) COMMAND_ID_HANDLER(ID_FILE_OPEN, OnFileOpen) COMMAND_ID_HANDLER(ID_FILE_SAVE, OnFileSave)END_MSG_MAP()...
  • 8.
    An Example, RedesigningNotepadWM_CREATE handling is the same (still a part of view):LRESULT CMainFrame::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled){m_edit.CreateWindow(m_hWnd, WS_CHILD|WS_VISIBLE|...WM_SIZE too (another part of view)LRESULT CMainFrame::OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled){ RECT rc_client;GetClientRect(&rc_client);m_edit.SetWindowPos(NULL, &rc_client...
  • 9.
    An Example, RedesigningNotepadBefore we go further, we research and design this application, in other words, we’ll decide what controller does and what model does
  • 10.
    An Example, RedesigningNotepadWe knew that Notepad should be able to process text files, so we should have a TextFile class that handles reading and writing, this would be a single threaded, static class whose only job is to accept a file name, and return std::wstringto us (this is a model)
  • 11.
    An Example, RedesigningNotepadSo we have:class TextFile{public: static boolReadFile(constwchar_t* filename, std::wstring& content_out); static boolWriteFile(constwchar_t* filename,constwchar_t* content_in);};
  • 12.
    An Example, RedesigningNotepadWe knew that user may issue command such as OpenFile, SaveFile, NewFile, so we design a FileController class that has these methodsAdditionally, this controller should have a buffer that maintains the current displayed text for editing (for use with views), thus we should have methods such as SetContent, and GetContentTo deal with large files and potential slowness in networked opening, we may need to make OpenFile, SaveFileasynchronized, therefore we may need a method named IsLastFileOpCompleted for views to decide when toretrieve content (this is a controller)
  • 13.
    An Example, RedesigningNotepadSo we have:class FileController{public:boolOpenFile(constwchar_t* filename);boolSaveFile(constwchar_t* filename);boolNewFile();boolSetContent(constwchar_t* content);wchar_t* GetContent();boolIsLastFileOpComplete();private:std::wstringm_buffer; // boost::shared_ptr<boost::thread> m_fileop_thread; // boost::mutexm_buffer_mutex;};
  • 14.
    An Example, RedesigningNotepadWhat about views?LRESULT CMainFrame::OnFileOpen(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled){CFileDialogfd(TRUE, NULL, NULL, OFN_EXPLORER, L”*.txt”, m_hWnd); if (!fd.DoModal(m_hWnd)) return 0; if (!m_view.m_filecontroller.OpenFile(fd.m_szFileName)) {MessageBox(L”There is a problem with file opening”); return 0; }SetTimer(TIMER_CHECKFILEOPENCOMPLETE, 50); // in WM_TIMER, we check m_filecontroller.IsFileOpComplete() // to decide whether to refresh the m_view using the content // retrieved via m_filecontroller.GetContent...
  • 15.
    An Example, RedesigningNotepadWe used timed polling to make sure our controller doesn’t know any details about views, and is thus directly portableOf course timed polling is bad, it consumes and wastes extra resources while checking for return values, due to the stupidity in its designBut it can be easily controlled, when it’s required to cancel such a file operation, you kill the timer, and tell the controller to stop file operation
  • 16.
    An Example, RedesigningNotepadTimed Polling (Timer) versus Mediator/Observer (Java Callback) versus Delegates (C# Callback)Observer pattern should be used in strict sense*MVC is macro-architecture and design pattern is micro-architecture
  • 17.
    An Example, RedesigningNotepadApplying Observer pattern:class IFileObserver{public: virtual void SetFileOpComplete(booliscomplete) = 0;};class FileObserver: public IFileObserver{public: void SetReceiveWindow(HWND hwnd) {m_hwnd_receive = hwnd; } virtual void SetFileOpComplete(booliscomplete) { // call SetWindowText depending on |iscomplete|...
  • 18.
    An Example, RedesigningNotepadApplying Observer pattern:class FileController{public: void AttachObserver(IFileObserver* observer); void DetachObserver();...void _OpenFile_WorkerThread(constwchar_t* filename, bool& ret) { // after file read successm_observer->SetFileOpComplete(true); }...
  • 19.
    An Example, RedesigningNotepadTo implement multi-tabbing, we put FileController inside CEdit, and replicate CEdit (via std::vector<CEdit>) so that each instance has a controller, a TabController might also be needed to manage tabsTo implement syntax highlight, we implement HiliteNode (base model), HiliteParser (model) that translates wchar_t* string into std::vector<HiliteNode>, and make sure our FileController can output stuff returned by HiliteParser, then subclass CEdit to implement actual paintingFor unicode BOM, only need to revise TextFile class
  • 20.
    Redesigning Notepad, WhatWe’ve Achieved:Complete isolation of UI and application logicComplete isolation of application logic and low-level data formatsPossible for independent development for most componentsPossible to run unit-test for each components, so that product quality can be assuredPossible to easily port to other platformsClear application logic for future referenceReusable components for future projects
  • 21.
    Arch. Ver. ofNotepad v.s. No-arch. Ver.Primary technical difference is class design, class relation, class differenceUnderlying logic, including frame window management, low-level file reading/writing are mostly the same
  • 22.
    When Portability isOur Concern:Remember Cocoa/Objective-C is compatible with C++Establish user interface on Cocoa framework like before, the equivalent view on Windows cannot be usedBind Cocoa’s interface controller actions to our real portable controllersOnly controllers and models can be 100% portable
  • 23.
    Architecture and MVCis Mostly About:Class design
  • 24.
  • 25.
    References Regarding DesignPatternshttp://stackoverflow.com/questions/516411/raw-function-pointer-from-a-bound-method/516537http://stackoverflow.com/questions/946834/is-there-a-design-pattern-that-deals-with-callback-mechanism
  • 26.
    References Regarding MVCand Othershttp://en.wikipedia.org/wiki/Model–View–Controllerhttp://www.oracle.com/technetwork/articles/javase/mvc-136693.htmlhttp://www.djangoproject.com/http://code.google.com/webtoolkit/

Editor's Notes

  • #18 Search “C++ Interface” to learn more about interface classes