万星星@豌豆荚 欢迎加入我们
一个吃软饭的男人!!!!!我只想写程序####
微博:http://weibo.com/wanlianwen
posts - 172,  comments - 1253,  trackbacks - 0

存在的必是合理的,都值得我们学习。学什么不重要,重要的是有一技之长。
如果你认为MFC垃圾请不要继续看。
如果你认为文档视图结构丑陋请不要继续看。
如果你认为ATL过时了请不要继续看。

MFC应用与框架的联系
要想分离应用与框架,首先得明白它们之间的联系。用向导生产一个多文档程序观察,可以发现只有一处关联:

CMainFrame *  pMainFrame  =   new  CMainFrame;
    
if  ( ! pMainFrame -> LoadFrame(IDR_MAINFRAME))
        
return  FALSE;
    m_pMainWnd 
=  pMainFrame;

    
//  The main window has been initialized, so show and update it.
    pMainFrame -> ShowWindow(m_nCmdShow);
    pMainFrame
-> UpdateWindow();
既然App与Frame耦合这么松,把它们分离是很自然的想法,也是符合软件工程中的高内聚低耦合标准的。

MFC应用与框架的剥离
应用需要的只是一个主框架指针而已,可以定义一个插件接口:
interface IMainFrame : IUnknown
{
    [id(
1), helpstring("method GetFrame"), hidden] HRESULT GetFrame([out, retval]long* pMainFrame);
}
;
为了插件查找以及管理,需要一个类别,所有支持的插件都属于这个类别:
BEGIN_CATEGORY_MAP(CManager)
    IMPLEMENTED_CATEGORY(CATID_FrameCategory)
END_CATEGORY_MAP()
下面就可以实现主框架插件了,生产一个ATL项目,添加主框架类,添加一个组件Manager,实现插件接口:(示例用中文主框架和英文主框架来演示效果)
STDMETHODIMP CManager::GetFrame(long* pDocCls)
{
    CMainFrame
*        pMainFrame    = NULL;
    HINSTANCE        hInst        
= AfxGetResourceHandle();
    pMainFrame 
= (CMainFrame*)(RUNTIME_CLASS(CMainFrame)->CreateObject());
    AfxSetResourceHandle(_Module.m_hInstResource);
    
if(!pMainFrame->LoadFrame(IDR_MAINFRAME))
    
{
        AfxSetResourceHandle(hInst);
        
return E_FAIL;
    }

    AfxSetResourceHandle(hInst);

    pMainFrame
->SetWindowText(_T("中文版本"));
    
*pDocCls = (long)pMainFrame;

    
return S_OK;
}
这样在主框架启动的时候就可以遍历CATID_FrameCategory下面所有框架插件,


选择一个插件框架后:



有必要这样做嘛??
这里演示了一个应用根据选择不同的插件进入不同语言的界面,当然不是为了解决多国语言版本问题而这样做(多国语言版本可以通过资源文件来解决)。
这样做到底有没有必要呢??再来看下向导生产的程序界面:


这里可以清楚看到子框架、文档、视图是一个模块体,主框架、菜单、工具栏、状态条是一个模块体、后面看不见的应用app又是一个模块体。这使我想起小时候的积木,一堆不同形状的木块,可以自由的想象去搭建房子汽车。
软件应该是软的,是像积木一样可以自由搭架自由组合的。有些人一直抱怨MFC死板,我也曾经热衷去的研究一些小巧的界面库WTL、fox、fltk,但是回头来看用的最多的、对自己最有帮助然而也最另自己头痛的还是MFC。
软件需要美观,于是乎出来了许多基于MFC的界面库,其中BCG、XTREME算是有名气的。试想一个项目的开发需要多少人,又有多少人对BCG之类的库熟悉(即使它很好用)。我曾经遇到过一个问题:BCG的DockBar标题在上面,占用很大一部分视图,给人一种拥挤的感觉,而XTREME的标题在左侧,这样视图看上去大许多。但是引入两套界面库是不实际的,于是自己想用MFC做一个。结果是用MFC的DockBar居然加不上去,由于涉及到一些其他问题,所以无奈只能忍受那个霸道的DockBar。最让人无法忍受的是一些别出心裁的人在MFC对话框上加上一个漂亮的XP按钮,哭笑不得。我也冲动认为给我Wnd和DC就可以作出任何想要的界面效果,花上几天时间去绘制一个控件,响应各种消息,做完后才发现离专业美观相差甚远,而且放入到应用中不协调,影响软件的商品化形象。
软件开发已经进入工业化时代了,手工蛮干显得笨拙,对MFC进行封装的界面库自从有了皮肤之后也显得苍白无力。到底如何才能简化软件开发,提高软件应变能力??
回到软件设计的铁定律:高内聚低耦合。从面向对象到面向构件到现在流行的面向服务软件设计理念,无不遵守这条原则,不同只是技术上的更新。对于大型软件的界面开发,抛开美观(可以通过皮肤满足)不谈,MFC是值得我们研究的。从开发者的熟悉程度和提供解决方案能力来讲,MFC都是最佳选择。有人会说MFC提供的控件少,不利于开发,请清楚软件开发已经进入工业化时代了,这已经不再是问题(后续文章会陆续解决)。
软件要想软下来,尽量适应变化,只有降低耦合度。我们必须把MFC向导生产的框架拿来进行解剖,斩断耦合的牵绊。这里把软件抽象看作App、MainFrame、DocView组成,从上面的剥离可以看到,App与MainFrame、MainFrame与DocView之间是松耦合的,每一个部分是内聚的。只要保证每个部分之间可以正常通信,这样一个App就可以对应多个MainFrame,一个MainFrame可以对应多个DocView。企业只要有App、MainFrame、DocView三个向导完成三者之间的通信,就足以开发各种不同需求的软件。

让思维漫步
在App、MainFrame和DocView剥离开之后,我又运行了向导生产的程序。发现选择菜单的时候,状态栏有信息提示。以前从没有考虑过这些再普通不过的功能是如何实现的,突然之间觉得很神奇。我打开msdn,查阅MainFrame的基类CFrameWnd,里面有个SetMessageText函数,是状态栏显示信息用的。嗯,选择菜单的时候一定会调用它。于是进入CFrameWnd的实现文件WINFRM.CPP,搜索SetMessageText发现OnEnterIdle()、OnIdleUpdateCmdUI()调用了它,

void CFrameWnd::OnEnterIdle(UINT nWhy, CWnd* pWho)
{
    CWnd::OnEnterIdle(nWhy, pWho);

    
if (nWhy != MSGF_MENU || m_nIDTracking == m_nIDLastMessage)
        
return;

    SetMessageText(m_nIDTracking);
    ASSERT(m_nIDTracking 
== m_nIDLastMessage);
}

原来空闲的时候一直会检测当前菜单跟踪状态。
静下心来,让思维去自由漫步,当孤独的智者。
代码下载。里面有说明文件。

posted on 2006-07-08 14:42 万连文 阅读(1218) 评论(0)  编辑 收藏 引用 所属分类: MFC

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


简历下载
联系我

<2006年7月>
2526272829301
2345678
9101112131415
16171819202122
23242526272829
303112345

常用链接

留言簿(66)

随笔分类

随笔档案

相册

搜索

  •  

最新评论

阅读排行榜

评论排行榜