不知道各位大大有没有这种经验:
一个类包含有好几个部份的功能,每个功能的实现函数都不少,有些功能又依赖于其它功能,而且这个类,还只能用作基类。
总之函数多,功能复杂,写下来块头大(上百行的类头文件我就受不了)。正在为自己的工作及网站写一个工具软件,MFC+WEBBROWSER写的,基础部分已经实现,虽然Debug版本测试没有发现内存泄露,但还是没什么底气。
因为我完全是自学MFC的,不知道编程中的一想法不知道是不是合理,请大大们指教。特别是结构上的问题。
因为是自己写着玩的,只要没什么严重问题就可以。但害怕因为结构性问题导致太监,所以请大大们看看我的程序结构,看我这种结构离太监有多远。原本是将所有函数都写在窗口类中,但后来函数太多,难以管理,对这种情况我想到的办法就是:按照功能写多个类,然后层层继承(之所以是层层继承,而不是多继承,是因为想留下向C#改造的方便)。
这样我就可以按功能区分不同的头文件,便于管理,不至于要在冗长的头文件找一个函数,查看它的参数列表。JJ的,改写花了一个多月。详情如下:A.基本情况:
>>
为了软件看上去不太难看,但没胆子去用第三方库或都自己贴图来美化软件,只好用纯粹的Webbrowser做界面,因为写网页还是会一点点的,CSS也比较好用。为了统一风格,使用无边框、无标题栏的对话框,只有一个Webbrowser控件,窗口拖拉弹唱全部由网页事件+消息来处理(IHTMLElement::attachEvent + IDispatch::invoke【sendmessage...】)。为了提高效率,没有用任何javascript,全部是直接操作各种IHTMLXxxx接口(最开始是用javascrit做的模型,最后转向纯MFC)。因为操作HTML元素时,特例太多,所以用回调函数来解决。因为使用innerHTML插入元素时,如果元素复杂【目前最极端的是插入一个80KB的内容,如果是单线程,要阻塞3~5秒】,会很慢,只好使多线程(其实也就是为了那几秒钟的效果)。因为Webbrowser吃内存的问题,使用了多进程,并用消息+共享内存来协调多进程。同上,但考虑到现在大部份办公电脑配置的内存及屏幕比我开发用的笔记本还好,所以对子功能窗口的打开方式提供了多个选择(网页中的选项卡,网页中模拟的弹出对话框,真实的弹出对话框【模态或者非模态】,新的应用程序),所以多进程多线程多窗口多选项卡全都有,于是用混合map,list,IStream[100],CCriticalSection还有消息来管理窗口。因为要根据窗口大小调整网页,CSS中的固定的图片不能适应窗口大小,于是抄了一个HTTPSever(总感觉不太稳定),再自己用GDI+处理图片。
考虑到参数表、皮肤、HTML页面等小规模的更新,模拟javascript用MFC实现了AJAX,方便小规模的修补程序(主要是不懂人们写的软件中自动更新机理,也搞不定socket,只好用ajax)。B.类层次及程序结构:
>>
Class DataBase {...} 
1.基础数据类(大约30个static函数),主要处理各种数据,特别是各种不同数据序列化。 Class XmlBase:public DataBase {...}
2.XML处理(大约30个static函数),包括文档加载,节点处理、配合xsl进行转换 Class HtmlBase:public XmlBase {...}
3.通用HTML元素处理(大约100个static函数),比如添加、删除、更改、查询HTMLDOM节点。 Class EventMagager:public HtmlBase  {...}
4.HTML元素事件管理(大约80个static函数),文档加载及HTMLDOM节点添加时负责添加事件,文档卸载及HTMLDOM节点删除事件、HTMLDOM节点变更时重置事件。 Class HtmlControl:public HtmlBase {...}
5.HTML复合元素过渡类,主要功能是log。其派生类一定要调用其构造函数。 Class CXxxx:public HtmlControl {...}
6.近20个HtmlControl派生类,其构造函数一定要调用其基类HtmlControl的构造函数。 Class CBaseForm:public CDialog,public HtmlControl {...}
7.基本窗口类(大约100个函数),主要处理各种消息、进程间通信、常用功能比如文件读写、打印,还有一些测试函数。
  这里使用了多继承,为了方便调用1~5的函数。 Class CUserForm:public CBaseForm {...}
8.功能窗口类,完成各种具体功能。 typedef HRESULT (CALLBACK* IMGSINITFUNC)(IHTMLElement*,CString)
typedef struct HugeHtmlTag
{
long lTag;
IStream* pStream;
}HUGEHTMLTAG;
...
9.各种回调函数(目前12个,增加中,HtmlBase、XmlBase及HtmlControl派生类中有不少函数需要调用这些回调函数)。
  多线程函数也列在此,有3个。 CHtmlEvent:public IDispatch {...}
10.大约90个基于的IDispatch类,用于HtmlControl事件转发及AJAX处理(MFC中实现AJAX,不是javascript)。 11.抄的HTTPServer及自写的DrawEngine,处理图片并返回给页面(GIF的背景总是黑的,只好用CSS来解决:filter::chroma(color=black))。C.是这样么:
>>实际使用中没有问题,但不是很确定,算是知其然不知其所以然吧。我有一个笨笨的做法,就是1~4这四个类的函数全部是static的。为什么用static函数?假设一种最极端的情况,同一个函数中20多个HtmlControl派生类都有用到,那么这些类实例化时,如果1~4的函数不是static的话,岂不是多用20几倍的内存(以我的认知,实例化一个类,类的方法全都会加载到内存中。是这样么?)。还有如果6~8的类有static方法,可以直接调用1~4的静态函数(static函数不能调用非static函数。可以不是这样么?)。最重要的是,线程函数中也可以使用这些static函数(线程中使用IHTMLElement接口指针时需要列集,线程函数中不能直接也不能【间接】【^_^】调用全局static IHTMLElement接口指针。可以不这样抓狂么?)。D.好烦:
>>
最最最痛苦的莫过于调试困难,比javascript调试还难,javascript至少有F12。首先我VC6.0的调试功能用的不熟。其次,IHTMLElement系列的接口指针,看不到HTML元素的内容、属性,什么都看不到,唯一知道的是指针是不是为空。所以写了一堆十几个专用于“调试”的函数,还要log。E.展望:
>>
话说看到过一句话说是这样的说的是,写winform的想将窗口写的像网页,写网页的想将网页写的像winform窗口。
其实我的程序就是用HTML元素集来模仿winform控件,树,复选框,单选框,按钮,下拉列表,datagrid,滑块,微调框,垂直选项卡,横向选项卡,包括文件选择对话框、字体选择对话框,都做到了,如果愿意的话可以做到和winform看起来一模一样(当然不愿意,废那么大功夫就是想自己玩)。如果想换风格,只要重构CSS就图片就可以了。最后,想把这个模型引入到真正的web中。想做到像QQ的WEBQQ那样,不过不知道358KB的C++代码转换成javascript会有多少。不知道有没有人做过这个。