文档—视窗结构批判

类别:VC语言 点击:0 评论:0 推荐:

MFC和文档—视窗结构的适用面

(V0.01)

VC/MFC(以下简称MFC,笔者使用的版本是VC 6.0/MFC 6.0)的App Wizard可以自动生成使用MFC应用程序框架(Application Framework)的Win32应用程序源程序(以下也简称MFC应用程序框架或者MFC框架应用程序),MFC应用程序框架是标准的以文档为中心的桌面(单机)应用程序,支持文档—视窗结构。

目前的底层封装(非可视化)的类库以及应用程序框架,例如MFC、OWL以及QT/KDE(Linux)均支持文档—视窗结构,支持以文档为中心的桌面应用程序开发。什么是以文档为中心的应用程序呢?简而言之文档就是应用程序使用的数据或者处理的数据(并不一定是文档文件),以文档为中心的应用程序泛指处理数据的应用程序,计算机从诞生的那一天起就是用于计算的,而计算的核心就是使用算法处理数据,所以除极少数纯用于控制的应用程序以外,大部分应用程序都是处理数据的应用程序,也就都可以认为是以文档为中心的应用程序,不过现在以文档为中心的桌面应用程序主要指处理(编辑)数据(文档),并能将文档永久化[将文档存储到永久存储介质(例如磁盘)上,成为文档文件以及从永久存储介质上的文档文件中加载文档]的桌面应用程序,例如Word、WPS等都是这种应用程序。

目前桌面应用程序大部分都处理数据或者直接编辑文档,不管文档是否永久化(简而言之就是有无打开/保存文件的功能),都可以看作是以文档为中心的桌面应用程序,那么从理论上说,是不是MFC 应用程序框架和文档—视窗结构就适合开发大部分桌面应用程序呢?可以这样说,但是实际情况并不是这样,文档—视窗结构是一种非常好的思想,但是MFC应用程序框架支持的文档—视窗结构对于现代的桌面应用程序是否全部适合,答案并不一定是肯定的。

文档—视窗结构的基本思想是:文档类/对象存储文档,视窗类/对象显示文档,文档的存储和显示相分离,一个文档对象可以和多个视窗对象关联,这意味着:

⑴一种(或者一个)文档可以有多种显示方式(视窗),例如Word有普通视图、页面视图、大纲视图等多种文档显示方式。
⑵一个文档可以同时被多个视窗显示,例如Word可以将在多个视窗中同时编辑同一文档的不同部分。

为了使得一个文档对象可以和多个视窗对象关联,MFC应用程序框架使用文档模板(Document Template)对象自动管理(创建/删除)文档对象和视窗对象,并建立文档对象和视窗对象的关联(OWL也相似)。在MFC应用程序框架中,更新视窗(显示文档)的标准方法是由文档对象通知与之关联的所有视窗对象更新视窗,具体说来就是调用文档对象的UpdateAllViews成员函数,那么所有与之关联的视窗对象的OnUpdate成员函数(虚函数)都会被引发(调用)。视窗类重写OnUpdate虚函数,添加更新视窗的代码,该代码调用视窗对象的GetDocument成员函数,获取指向与视窗对象关联的文档对象的指针,进而获取文档,显示文档。

上述过程似乎看来没有任何问题,但是实际存在一个问题,那就是应该由文档对象修改(编辑)文档还是应该由视窗对象修改文档?根据文档—视窗分离的原则,文档对象只应该完成与文档本身相关的工作,例如初始化文档、清理文档、文档永久化等工作,修改文档也应该属于与文档本身相关的工作,也应该由文档对象完成,对于标准的Win32应用程序,确实如此,因为视窗是普通窗口,而普通窗口一般不包含控件,也不接收用户输入,用户输入使用对话框完成,对话框包含控件,可以在文档类中添加打开对话框进行用户输入并修改文档的代码(MFC应用程序框架中,命令消息,即菜单、工具条、加速键等引发的WM_COMMAND消息可以在视窗类、文档类、框架窗口类和应用类中映射),这就相当于文档对象修改文档,视窗对象只显示文档。但是实际上目前Win32应用程序大部分在视窗中修改文档(例如Word、WPS等),也就是由视窗对象修改文档,那么必然在视窗类中添加调用视窗对象的GetDocument成员函数,获取指向与视窗对象关联的文档对象的指针,进而修改文档的代码,又因为一个文档对象可以和多个视窗对象关联,该代码还要通过指向文档对象的指针调用文档对象的UpdateAllViews成员函数,通知与之关联的所有视窗对象更新视窗,而不能更新了当前视窗就完事,这就形成了一个特殊的修改文档过程:

调用视窗对象的GetDocument成员函数获取指向与视窗对象关联的文档对象的指针——修改文档——通过指向文档对象的指针调用文档对象的UpdateAllViews成员函数,通知与之关联的所有视窗对象——更新视窗

整个修改文档过程中对象的调用过程:

视窗对象——文档对象——文档对象——视窗对象

这样必然增加了编程的复杂度。视窗对象修改文档,文档对象只完成了存储文档和初始化文档、清理文档、文档永久化等工作以及通知与之关联的所有视窗对象更新视窗的工作,但通知与之关联的所有视窗对象更新视窗是必需的,因为一个文档对象可以和多个视窗对象关联。

如果一个文档对象只与一个视窗对象相关联,那么视窗对象修改文档,视窗类中只需要添加获取文档,修改文档,然后自己更新视窗的代码就可以了,在这种情况下文档对象已经失去了通知与之关联的所有视窗对象更新视窗的作用,仅完成存储文档和初始化文档、清理文档、文档永久化等工作,最终文档对象可以成为一个视窗类/对象的成员变量,甚至文档不再是对象,而仅仅是视窗类/对象中的普通数据类型变量,初始化文档、清理文档、文档永久化等工作也由视窗类的代码完成了,最后视窗对象可能也不存在了,框架窗口可以取代视窗了,实际上此时文档—视窗结构已经消失了。也就是说在一个文档对象只与一个视窗对象相关联,又由视窗对象修改文档的情况下,文档—视窗结构已经失去意义了。

目前随着可视化软件开发/快速软件开发(RAD)的流行,以窗体(Form)为核心(Form泛指带有控件的用户界面,例如Windows Forms、Web Forms等)的用户界面已经取代以窗口和对话框为核心的用户界面,显示文档和修改文档都由窗体上的控件完成,通常不会出现一个文档可以同时被多个窗体显示和修改的情况,因为一般不会用两个带有控件的窗体去显示和修改同一文档,至于一种(或者一个)文档可以有多种显示方式的情况,可以用同一窗体上的不同控件解决,相当于一个文档对象只与一个视窗对象相关联,又由视窗对象修改文档的情况,此时使用文档—视窗结构没有意义,所以目前的可视化软件开发工具一般不使用文档—视窗结构,所以尽管VB、Delphi、C++ Builder等可视化软件开发工具都有Application Wizard工具,也可以生成应用程序框架,但是都不使用文档—视窗结构,文档仅仅是窗体类/对象中的普通数据类型变量。

MFC是底层封装(非可视化)的类库,它主要是对Win32对象和Win32 API的直接底层封装,保留了Win32应用程序开发的基本结构和元素,例如消息、资源等。VC也不是真正的可视化软件开发工具,它只是编写代码和编辑资源可以实现可视化(通过App Wizard、Class Wizard和Resource Editor),文档—视窗结构对一个文档对象可以和多个视窗对象关联的情况比较适用,MFC应用程序框架功能齐全,所以MFC主要适合开发Win32底层应用程序以及某些要求比较高(要求一个文档对象可以和多个视窗对象关联,例如Word、WPS等)的以文档为中心的桌面应用程序。又因为MFC主要是对Win32对象和Win32 API的直接底层封装,效率较高,所以也适合开发效率要求较高的应用程序。

VCL(Delphi和C++ Builder使用的类库)是可视化的类库,对Win32对象和Win32 API以及Win32应用程序开发的基本结构和元素做了彻底封装,封装高度抽象化和统一化,并使用了全新的支持可视化开发的应用程序框架,Delphi和C++ Builder也是完全的可视化软件开发工具,所以VCL主要适合开发需要进行可视化软件开发/快速软件开发的Win32应用程序,但是不是说Delphi和C++ Builder不能开发Win32底层应用程序,因为它们都能使用Win32 SDK开发Win32应用程序,C++ Builder也支持MFC。

目前可视化软件开发/快速软件开发已经成为软件开发的主流,Microsoft Visual Studio.NET(Microsoft Visual Studio 7.0)在Microsoft .NET Framework上已经支持可视化软件开发[C#、VB.NET(VB 7.0)、VC.NET(VC 7.0,使用Managed C++)],.NET Framework Class Library和VCL系出同门,仅使用标准C/C++的VC 7.0还支持MFC和ATL。

VC 6.0中,App Wizard自动生成使用MFC应用程序框架时,可以选择不需要文档—视窗结构支持,但是此时视窗类的基类竟然不能自己选择(例如CFormView),失去了接近可视化软件开发的能力,实在是MFC的一处败笔!

 

本文地址:http://com.8s8s.com/it/it2175.htm