鸟在一起的设计——优快云推荐帖

版权声明:可以任意转载,转载时请务必以超链接形式标明如下文章原始出处和作者信息及本声明

作者:xixi

出处:http://blog.youkuaiyun.com/slowgrace/archive/2009/04/20/4095267.aspx

这个帖子里和zhao讨论了很多,zhao还有其他几位朋友给了很多很好的建议,准备把总体框架大改一下。

起初我只想加个NODE类。加它不光是因为我每次传一大堆标志位做函数参数传得我烦,还因为微软的node对象在treeview不可见时不能很好地判断自己的上下左右,至少它的parent会是nothing。前几天我勉强自己每次要取其他node对象的时候把相应的树的可见性toggle一下,但是这种toggle貌似会引起treenode的expand事件,搞的我的见光标志位也是一摊糊涂账。所以,折腾来折腾去我就想加个node类了。

之后ahao和我提到MVP等经典模式。我看了一下资料,感觉我原来设计的对象模型太简单,一个对象负责的东西太多,导致逻辑复杂,变更和维护都很累。所以,思量来去决定大动。具体设计如下:

1、总的思想
基本希望的样子是面向对象的,负责数据的和负责显示的分开,集合类和单体类分开,界面数据和核心数据分开。各司其职,通过事件机制来进行消息传递。

2、节点表和节点细节表

http://blog.youkuaiyun.com/slowgrace/archive/2008/12/08/3474159.aspx

3、类的安排
a)CTree类和CNode类。一个CTree类实例对应一个节点表,一个CNode类实例对应一条节点记录。CTree类包含由CNode类对象构成的集合。这两个类是界面数据类。
b)CTreeCtl类。负责界面工作。一个CTree类可能对应多个CTreeCtl类。
c)CDetail类。这个是核心数据类。负责细节数据的增删改。
d)CMsgMate类。消息使者类。负责触发各种自定义事件

4、数据的装载

和zhao讨论的一个重要分歧在于数据的装载,zhao建议所有数据一次性载入内存,之后对树的增删改都直接在内存上操作,之后根据情况再批量更新到数据库。我承认也理解这种做法在性能上的优越之处,不过对自己的内存不是很自信,毕竟我想用这个树类日积月累维护很多各种各样的数据。

另外,我感觉我的应用对性能其实要求不高。我的树的特点是很深(层次很多),起初的几层节点并不多,只到最底层的叶子节点(这些节点是日积月累的)会突然增多很多。用户交互的时候,一般一次是只看一个节点,或增删改移一个节点或有限的节点。这时候,即使我有频繁的数据库操作,哪怕是改一点点也要访问数次数据库,因为要操作的数据量小,我想在性能上不会有什么感觉的。

所以,我现在在数据装载方面的设计只部分采纳了zhao的建议。现在在这个模型里只装载了全部节点的部分信息,它们存在各个CTree的CNode列表里。而CDetail里是不存数据的,它只是负责直接对节点细节表操作。CNode类里也不是简单的对应于节点表里的一条记录,比如它还有节点名、节点多挂计数等属性,这些属性是从节点细节表里查出来的。总的来说,CNode类里存的是一些界面上常需用到的数据,我把它存在这个模型中,省得做一些动作的时候需要频繁访问数据库。

这样有些数据我存了两个地方:CNode列表里有一份、数据库里有一份。这和zhao的建议有点相似。不过,我在变动数据的时候,不是攒着到最后返回到数据库的。而是变动的时候一竿子捅到底,直接改数据库,同时更改CNode里的数据,保持它最新。我给自己的做法美其名曰,叫模型里只存界面数据,呵呵。

所以,我现在CNode和CDetail是打折扣的zhao的设计。如果日后我感觉还是zhao的设计好,我其他部分的程序基本上还是可以不动。只把CDetail的实现改一下就好了。不过,这只是一个模糊的想法,没有认真地考量过。

5 类之间的协作

当用户对树节点进行增删改之后,各个类之间协作完成相应的数据和界面变动。

a)以改节点名字为例。这种数据存在节点细节表里,其变动在界面上要影响到所有树。
  i. 首先CTreeCtl得到消息,知道用户改了数据
  ii. 然后CTreeCtl通知CDetail改数据
  iii. CDetail改完数据后通知所有CTree这一变动
  iv. 所有CTree改自己相应的Node的属性,并通知和自己关联的CTreeCtl
  v. CTreeCtl得到通知后,做界面的变动

b)以改节点father为例。这种数据存在节点表里,其变动在界面上要影响到所有同名树。
  i. 首先CTreeCtl得到消息,知道用户改了数据
  ii. 然后CTreeCtl通知相应的CTree这一变动
  iii. CTree改自己相应的Node的属性,并通知和自己关联的CTreeCtl
  iv. CTreeCtl得到通知后,做界面的变动

c)以改节点Comments为例。这种数据存在节点细节表里,其变动在界面上不需要显示。
  i. 假设用户是在细节子窗体变动节点Comments。那么直接改就可以了,不需要做任何事。

d)考虑同树独改的情况。上面讨论的其实都是同挂全改的情况,当节点独改和同树独改的时候,就不一样了,以同树独改为例。独改时需要拷贝新细节,并减少老细节的多挂计数,并且要改原节点的细节ID。前2件事由CDetail负责,后1件事由CNode负责。具体过程如下:
  i. 首先CTreeCtl得到消息,知道用户改了数据。并通过用户交互,确定用户希望同树独改数据
  ii. 然后CTreeCtl通知CDetail改数据
  iii. CDetail得到通知后,做两件事
      1. 拷贝制作新细节,并通知相应的CTree这一变动
      2. 减少老细节的多挂计数,并通知所有CTree这一变动
  iv. CTree得到通知后,改自己相应的Node的属性,并通知和自己关联的CTreeCtl
  v. CTreeCtl得到通知后,做界面的变动

6 类之间消息的传递

消息使者类CMsgMate定义了许多事件,并提供许多public函数用于触发这些事件。当我需要在2个类之间传递消息时,我就在这2个类里都加入CMsgMate类型的成员变量,并在对象实例化的时候,让这2个成员变量都指向同一个CMsgMate实例。这样1个类的CMsgMate触发事件的时候,另外一个类的CMsgMate成员也能收到。 具体地:
    i. CTreeCtl通知CDetail:这2个类的CMsgMate成员都指向同一个全局的CMsgMate对象。
    ii. CDetail通知CTree:这2个类的CMsgMate成员都指向同一个全局的CMsgMate对象。
    iii. CTree通知CTreeCtl:可以用全局的CMsgMate对象,也可以用CTreeCtl类里的with events的CTree对象。
    iv. CTreeCtl通知CTree:这2个类的CMsgMate成员都指向同一个全局的CMsgMate对象。

7 这个设计里的弊端

这个设计首发在优快云论坛,被Tiger_Zhao点评“设计出来的结构这么混乱”,呵呵。有两个问题:

(1)我为了防止内存占用,所以没让CDetail保存数据;可是却把本来属于CDetail的name, bytmultigua等数据在cnode里复制了好几份。不仅没省内存,还浪费内存了,呵呵。

(2)各类的职责不清。对象设计中要仔细划分职责,术语叫“降低耦合度”,俗语叫“各人自扫门前雪莫管他人瓦上霜”。比如树中节点的添加/删除,Tree 对象只需要添加/删除父子对应关系,再通知到 DetailList 某个 ID 被使用/解除使用,就完了。至于多挂计数之类的处理,完全是 DetailList 内部事务,管 Tree 鸟事!?

8 进行OO设计的方法

1)设计类之前先要提取角色(Actor),这通常是从要做什么事中对应出来的:

甲——读取A表数据

乙——构建树结构A

丙——在A树中查询节点

丁——显示A树

戊——在A树展开节点

。。。

癸——读取B表数据

。。。

2)然后根据角色,来决定用多少个对象(Object)来担任这些角色,相近的功能会有同一个对象来担任多个角色。

3)根据对象行为归类,比如所用的树结构对象都可以归纳为 CTree,这样就有了类(Class),而且每个类有什么功能通过角色的行为很清楚地决定了。

由于步骤 1) 其实是个思考的过程,程序结构中只反映了 2)、3) 的结果。专业与否的区别就在步骤 1) 里,即使随着经验的增长只需要在大脑中快速过一遍。但不做就是业余的。

你可以将所有要做的行为列出来,这就是函数化设计。然后给每个行为分配一个角色,这就是步骤 1)。虽然决定哪几个行为同属一个角色比较考验能力,但是几个步骤多反复几次应该可以找到到比较合理的划分。

更多树类文章

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值