1、类间传递消息的三种方法
类之间信息的传递可以有两种方式:一种是通过消息传递,另一种是不用消息,直接调用类的方法。比如我想让对象A做件事,有两个选择:一则我给它发个消息(在VB中这通常是通过触发一个它能理解的事件来完成的),它收到消息后做事;二则我直接调用它对外开放的public方法(不妨称为“直接调用法”)。消息可以有多个接收者,VB自动实现逐个调用,而且发消息的耦合性小一些,因为发消息的对象不必关心接受者是谁;而直接调用法只有1个接收者,耦合度也稍大一些。因此这两种方法选择哪种,主要考虑你需要通知1个对象还是多个对象以及对耦合性的关注程度。
如果选择通过消息在两个类之间传递信息,一种常见的方式是通过自定义事件来做。比如A类要通知B类,那么就在B类里嵌一个以with events声明的A类成员变量,这样A类触发自定义事件,B类就能通过内嵌的A类对象得到消息,在相应的事件过程里处理事件。这种方法相当于A直接通知B,我们不妨称这种方法为“直接通知法”。
还有一种方法,可能算是我自己瞎想出来的方法,就是专门安排一个消息使者类:CMsgMate。它定义了许多事件,并提供许多public函数用于raise这些事件。当我需要在A类和B类之间传递消息时,我就在这2个类里都以With Events加入CMsgMate类型的成员变量,并在对象实例化的时候,让这2个成员变量都指向同一个CMsgMate实例。这样1个类的CMsgMate触发事件的时候,另外一个类的CMsgMate成员也能收到。注意,这种方案里A和B是对称的,也就是A可以通知B,B也可以通知A,这时候通知是间接通过第三方类CMsgMate送达的,我们不妨称这种方法为“间接通知法”。
比如,看这段Tiger_Zhao给我的建议:A)在任何地方修变更NodeDetail后,调用一下oDetailList.OnDetailChanged(ID),而在CTreeData对象中用WithEvents oDetailList响应消息,对属于自己的节点进行刷新显示。B)一个CTreeData 的数据就只能是一个实例,如果有多处的显示,再添一个CTreeCtl负责界面工作。树的变化也通过事件从CTreeData通知到CTreeCtl中。
上面这个建议里主要用了直接调用法(调用OnDetailChanged)和直接通知法(CTreeData类内嵌CDetailList对象、CTreeCtl类内嵌CTreeData对象)。
2、树类框架中消息的传递
具体到我现在开发的这个树类,应该采用哪种消息传递的方法呢?不妨以Value的变化为例来讨论,考虑同树全改的方式。当用户在细节子窗体中修改了Value字段,数据修改和更新显示的流程如下:
(1)CDtlSfr发出ValueChanged消息
(2)CDetailList收到消息后更改数据,并发送ValueChanged消息和DetailIdChanged消息
(3)CTreeData收到消息后传播数据更改,并发送ValueChanged消息
(4)CTreeCtl收到消息后更改标签,之后发送SelChanged消息
(5)CTreeWnd收到消息后更新显示,可能需要通知到CDtlSfr
(1)CDtlSfr通知CDetailList:可以用直接调用法,也可以用间接通知法,但是不好用直接通知法。因为CDetailList和CDtlSfr是一对多的关系,如果要在CDetailList里内嵌多个CDtlSfr对象,并挨个为每个CDtlSfr对象写相应的事件过程,那不是太累了么?如图,我选的间接通知法,因为想降低耦合度。
(2)CDetailList通知CTreeData:只能用间接通知法。因为这两个类之间是多对多的关系。同一细节可能在多棵树下都有挂,从而一个细节表(CDetailList)可能对应多个节点表CTreeData;一个节点表(CTreeData)可能包含多个种类的节点,从而对应多个CDetailList。
(3)CTreeData通知CTreeCtl:可以用直接通知法也可以用间接通知法。用直接通知法,消息传递地更精准一些。
(4)CTreeCtl通知CTreeWnd:这是多对一,可以用直接调用法或者间接通知法。
(5)CTreeWnd通知CDtlSfr:这是一对多,可以用直接通知法,也可以用用间接通知法。
3、小结:类间消息传递方法的选择
如果希望A类向B类发消息,可选择的消息传递的方法如下
(1)直接调用法:适用于A与B是多对一或一对一的情况;
(2)直接通知法:适用于A与B是一对一或一对多的情况;
(3)间接通知法:适用于A与B是多对多的情况及其他所有情况。
感觉以上3种方法的类间耦合度渐降,消息通知的精准度也渐降。