MFC不是唯一一个使用了设计模式的程序库,也不可能是唯一的一个。可以肯定的说,所有好的框架必然是充满了好的设计模式。本文的目的在于提醒大家学习框架时,运用设计模式的理论和思想去理解其中众多的类之间的联系,从整体的角度去学习部分,显然比从部分去理解整体有更好的效果。本文的后面会给出MFC中的一些设计模式。  但是,不得不悲哀的看到,MFC中的核心类CWnd(不知道的先拖下去打)违反了框架设计的一个基本准则:最小化。可以说,CWnd就是一个“根部肥大”的准则的好例子。不知道有多少人真正理解了CWnd中数以百计的成员函数?某种意义上,MFC普遍采用继承的方式实现,而很少采用组合类或者对象的方式,也是在一定意义上和软件设计模式相违背的。OO的三大特性中的继承,似乎误导我们尽可能的使用继承方式搭建框架,可是在设计模式中,可以看到组合是更加重要的方法。呜呼,本人也曾经是继承思想的受害者之一。  不过,我必须承认,继承是比组合更加容易理解的方式,至少对于我如此。如果从完美的软件设计模式的角度,是否应该把CWnd拆分成更多的小接口?这样,我们的View,Dialog,button等等可以得到更加清晰的接口。但是这样的代价同样会增加框架的理解难度。从一个入门者的角度,我们不可能要求他先学习《设计模式》,然后去学习MFC。我的建议是,在学习MFC一段时间(半年?100个例子?理解MFC的20个类?)后,应该结合设计模式去学习。或许这也是学习框架的一个良好途径。  不得不提醒诸位,请注意对于模式设计的过分使用的后果。netwind (往事如风)的文章(模式设计与形式主义)http://www.sawin.com.cn/doc/SD/Pattern/pattern.htm是一个非常好的例子。对于本人的经历,在刚开始接触设计模式思想的时候,简直有醍醐灌顶的感觉,啊,上帝,我以前的程序为何如此之烂?!现在,我要承认,设计模式好,但是使用不易,尤其对于我这个懒惰的Programmer,“原理要深,应用要快”是我的目标。这一点从我的“金山词霸的词库读取程序”中可以感受,一个运行了一个多小时的程序,我也会懒得再优化。:)  小注:当然,众多晦涩难以理解的宏也为MFC的框架理解增添了不少“光彩”。一定程度上,我同意bigwhiteshark同志在http://dev.csdn.net/article/20/20831.shtm (对VC的批判,我要打击大家学VC的积极性了…… )文中的观点:从微软的策略看VC的前途!微软的策略是先用.net提供一个新的环境,然后逐步修改其OS,最终完全抛弃NT内核,其最终目的是抢占被UNIX和LINUX占据的高端市场份额(这是Borland李维说的,我觉得有道理)。现在推出的.net框架还支持原来的技术(如COM+),VC.net中也还有MFC7,但这只是一个过渡时期的产品,就象Windows 3.1被Windows 95取代一样,最终是要被抛弃的。  最后的建议:限于本人的理论水平和时间精力,建议大家把自己对MFC中设计模式的理解也综合起来,相信这个是非常有意义的工作。我会继续进行这个工作的。欢迎在我的blog:caijingwei.blogcn.com 上发表评论。
Factory
定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method 使一个类的实例化延迟到其子类。
框架创建它的View的过程。CFameWnd就是一个Creator,通过调用CFrameWnd::CreateView()创建不同的ConcreteProduct。
说明:其实CreateView()和动态创建对象的技术密切相关。这是MFC中一个比较有趣的设计,和C++的RTTI(运行时类型信息)不同。大家可以去研究RUNTIME_CLASS, DECLARE_DYNAMIC, DECLARE_DYNCREATE这几个宏。
 Builder:将一个复杂对象的构建和它的表示分离,使得同样的创建过程可以创建不同的表示。
在int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)中,创建框架窗口的过程:m_wndDlgBar.Create()m_wndReBar.Create()
m_wndStatusBar.Create()
是否可以理解为一个简化了的Builder模式?Director和Builder都是CMainFrame。
Adapter
将一个类的接口转换成客户希望的另外一个接口。
STL中的适配器:stack,queue,priority_queue。可以看作简化的Adapter模式,它改变了基类的接口,但是并没有组合其它对象。
说明:STL并不是MFC的一部分,是C++标准库的一部分,之所以放在本文中,可以认为是作者的偷懒。:)Composite:    所有的窗口类都从CWnd派生,而派生的窗口类又可以包含其它从CWnd派生的类,窗口间的父子关系构成了窗口的树形结构。这种机制就是设计模式中典型的结构型模式COMPOSITE(组合),即某个对象拥有一系列类似的对象,而这一系列对象中的每一个又拥有一系列的对象,如此递归下去,管理起来很方便。这是Windows消息处理的基本模型。
Proxy
为其它对象提供一种代理以控制这个对象的访问。智能指针(smart pointer),比如_com_ptr_t
Template:相信学习MFC的同志有一个普遍的感觉:不知道使用哪个消息映射函数,即不知道哪里写代码。比如CDocument的OnNewDocument(),OnOpenDocument(),OnSaveDocument(),OnCloseDocument()。又比如打印的流程相关函数:PreparePrinting(),OnBeginPrinting(),OnPrepareDC(),OnPrint(),OnEndPrinting()。实际上这就是运用了Template模式:定义一个操作中的算法框架,而将一些步骤延迟到子类中。目的是使得子类不需要改变一个算法的结构即可重定义算法的某些特定步骤。很重要的一点是:基类必须指明哪些是钩子操作(可以选择被重定义),哪些是抽象操作(必须被重定义)。
Iterator:STL中的Iterator,从名字和运作方式都很好的吻合了Iterator模式。如果要更加细分,STL中的Iterator属于外部迭代器,即由客户来控制迭代。
C#和VB中的foreach()语句。foreach()语句中操作的对象集合必须实现一个内部迭代器。MVCMVC模式是一个用于将用户界面逻辑与业务逻辑分离开来的基础设计模式,它将数据处理、界面以及用户的行为控制分为:Model-View-Controller。 
Model:负责当前应用的数据获取与变更及相关的业务逻辑 
View:负责显示信息 
Controller:负责收集转化用户的输入 
View和Controller都依赖于Model,但是Model既不依赖于View,也不依赖于Controller,这是分离的主要优点之一,这样Model可以单独的建立和测试以便于代码复用,View和Controller只需要Model提供数据,它们不会知道、也不会关心数据是存储在SQL Server还是Oracle数据库中或者别的什么地方。 。
MFC中的Doc-View结构可以看作是这种模式的一个简化,Document类相当于Model,似乎没有明确出现Controller,Controller的功能可以在Document类中实现。

解决方案 »

  1.   

    记得在书店中看到过不少专门介绍标准C++以及VC.NET所用设计模式的书,专讲MFC的倒是没有。个人觉得理解了设计模式本身,可在自己的项目中用就行了,何必关心别人用了什么模式?学习的话则另当别论。结构的合理上,即使面对同门兄弟ATL,MFC的也差得太远。但请注意它实现的功能!此外,STL主要用于程序业务部分的数据与算法,MFC主要用于界面处理及操作系统特性的应用,有言“道不同不相与谋”,没得可比性。MFC的一切局限都来源它最初的设计目标:
    1.开发现实世界的应用程序。不应该定义新的编程概念,而是扩展现有模型,并用标准的C++思想来表达他们的概念。但不是每个人都是C++的宗师,因些实现中只使用了C++的一个子集,多继承就在这个子集之外。当然,框架对程序员所写代码的C++特性没有任何限制。
    2.简化Windows API。将API分成若干逻辑单元,用类和继承来简化开发概念,同时安全地隐藏大量的细节。但是不可避免地,窗口类和设备环境类的体积
    3.使用已有的知识。尽可能地使用操作系统的已呢特性,使得底层C代码可在MFC中重用。
    4.一个牢固的基础。随着API的增大,MFC要保证它的可扩展性。如OLE,上千行的SDK代码和简单应用OLE类的负担大不相同。不过相关的窗口等类的体积则受到最直接的影响,如CDialog的代码量就多了一倍以上。
    5.小而快的框架。最初目标是20K的内存代价,且不能比同等C/SDK程序慢。高抽象层次的类库和过多的虚函数因此不被允许出现。为保证速度更快,规模更小,AFX发明了其它机制来处理消息。以CObject而言,在类的虚表中增加三个表项,就可实现RTTI,动态创建,序列化,内存诊断等高级功能。
      

  2.   

    MFC一般鲜有与设计模式相联系起来讨论的,稍微想想不难发现,原来MFC里的设计模式使用导致了很多负面效应,并不算成功。楼上已经说到了,MFC很多局限性来源于最初设计的目标。我们不妨从MFC目前的情况反过去猜想一下,最初的设计思想核心是什么?很显然的至少有以下两个目标:    1.API类化使用。无限膨胀的API把程序员压得喘不过气来,怎样利用现有的面向对象语言来简化对操作系统的调用模型,想当然也是MFC开发小组面临的首先问题。不过由于在当时还没有成熟的面向对象的操作系统调用模型供借鉴,所以开发小组也不知道怎么做好,所以MFC就是以API的包装为核心,而不是完全从面向对象的开发模型的角度来设计的,从MFC的很多类都可以看出这种痕迹,相同的函数名,和API调用不同的只是前面加了一个对象操作符而已,很多系统调用过程和API开发有着惊人的相似,这也是为什么在MFC中程序里往往会夹杂着很多直接API的调用,即然过程都是一样的,许多人都会觉得不使用类会更简便一点,这大概是最初设计时没有想到的。这个出发点的不同直致导致了mfc与java类库的显著区别。    2.框架化。如果仅仅是第一点,我想MFC早就消声匿迹了。MFC最大的卖点就是实现“开发框架化”,在理论上这可以节省大量的开发时间。实际上却有很多问题。首先,楼主提到的MFC框架的几个重要的设计模式在这里都起了反作用。设计模式的运用往往会提高底层代码的复杂程度。如果这个框架能够满足绝大多数的开发需要,那么还无伤大雅,可惜它满足不了,许多应用程序都不与这个框架相吻合,开发人员必须要对框架提供的默认模型进行改造甚至动大手术,由于几个并不简单的设计模式的使用以及交叉运用,让MFC的改造使用变得相当困难。这也是为什么用MFC来学写一些哪怕很小的应用程序的难度也远远大于学习其它的(诸如VB、Delphi、java),需要付出的努力程度是悬殊的。因此,我对每一个在这里走着VC/MFC路线的程序员表示致敬,当然也包括我自己:)
      

  3.   

    回复:happyparrot(快乐鹦鹉) 
    此文乃本人原创。不足之处,敬请批评。同意QunKangLi(雾痕) 的观点:个人觉得理解了设计模式本身,可在自己的项目中用就行了,何必关心别人用了什么模式?
    本文的目的并不在于证明MFC对设计模式的运用之妙。虽然MFC的诋毁之词甚多,但是,既然使用者众,将其和设计模式联系进行研究,只是希望帮助大家更好的理解吧!赞成nlstone(天外流星) 的观点:
    因此,我对每一个在这里走着VC/MFC路线的程序员表示致敬,当然也包括我自己:)