同时问其他OO高手:我的类该怎样安排

slowgrace 2009-03-06 11:19:51
加精
我做了个树类,里面有
(1)treeview类型的成员变量m_tree,类初始化的时候指向相关联的treeview控件
(2)还有m_strTreeWndName,指向这个treeview所在的窗体名字。注意它有可能是也可能不是treeview的父窗体,由程序员根据自己的需求来定。
(3)还有m_strNodeTable,指向存储这个树控件的节点信息的节点表。
(4)还有m_strTreCtlName,指向这个treeview控件的控件名。这个直接用m_tree.name是得不到的,应为msctlcomlib.treeivew类没有name这个属性。

同一个节点表可能对应多棵树,同一个窗体上也可能有多个树。反正吧,上面4个变量唯一地规定了,当前这个类对象是哪个窗体上哪个树控件以及它是展示的哪个表的数据。

另外还有些用于树生成、增删改拖拽拷贝节点的方法。

除了一些常规的树操作,还有些我自己特别的需求,比如需要知道树节点的值和层次百分比等等,总之吧,为了方便,我给node安排了好些标志位,分别安排在node对象的text, tag, key属性里。

我的树上的节点可以多挂,就是具有同样细节的节点,既可以挂在这棵树上,也可以挂在那颗树上,只要符合一定的规则。所以,节点有多挂计数,知道自己被挂了几次。如果这个计数变为0的话,这个节点对应的细节也会被删掉。(数据库里主要是两类表,节点表和细节表。节点表存各树的节点之间的父子关系,细节表存各种细节的具体信息。节点都有细节类型,不同类型的节点的细节信息存在相应的细节表里)。

说了这么多,我是想说,我这些树它们是联动的,它们是牵一发而动全身的,因为有多挂节点的存在。

而因为存在多树联动的问题,我的树类模块就膨胀得很快,现在要从头翻到尾的都有点累手了。我自己也知道,其中有些过程和函数其实并不是这个树类特有的或直接相关的。[b]我想请教,我该如何安排这些函数?是把它们移出到标准模块里,还是另外写个什么类(可我一时又想不好能抽象出什么对象)[/b]?

举个例子吧。比如我有这么个函数,叫CopyNode。它的功能是把源树atSource上的源节点nodSource移到目标树atDest上的目标节点nodDest下。这个函数,我现在把它放在CAnnaTree类模块中(就是我在开发的这个树类)。可是其实,它并不是直接和CAnnaTree对象绑定的。没错,它是用到了atSource和atDest两个CAnnaTree对象,但是它并不是在处理CAnnaTree的内部事务,它完全是可以用来处理和它本身毫不相干的另外两棵树之间的节点拷贝。所以,我觉得它不应该放在CAnnaTree类里。我另外做个标准模块叫basTree,我把这个函数设为public放在basTree里。但说不出为什么,觉得这种方法不伦不类。呵呵。

再举个例子吧。比如我还有个函数,叫MakeNodeKey。它的功能是根据传来标志位,根据一定的规则,计算返回一个符合标准的节点key值。这个可以说也是和CAnnaTree对象本身不完全相关了,它完全可以用于其他CAnnaTree对象的节点key的计算。

如此种种,感觉很乱。
...全文
1378 80 打赏 收藏 转发到动态 举报
写回复
用AI写文章
80 条回复
切换为时间正序
请发表友善的回复…
发表回复
slowgrace 2009-03-16
  • 打赏
  • 举报
回复
结贴了,在这个帖子里继续讨论。感谢楼上各位的参与和指点。
捧剑者 2009-03-12
  • 打赏
  • 举报
回复
呵呵,没时间细看,顶一下
slowgrace 2009-03-12
  • 打赏
  • 举报
回复
to zhao:

下午我准备另起一个帖子,理一下思路,小结一下。这个帖子准备结了。你下次上来看看我的新帖子,好么?

76楼的具体问题倒不是关键的。我觉得我大局还糊涂,得理理。
slowgrace 2009-03-12
  • 打赏
  • 举报
回复
[Quote=引用 68 楼 ahao 的回复:]
还有一点就是你要分清楚什么是核心数据,什么是UI数据,别混起来
[/Quote]

这两个概念好。但是具体起来又有点糊涂,比如说,节点的名字,是算核心数据还是算UI数据?该存储在M里还是在V里?它的改变由谁负责?
Tiger_Zhao 2009-03-12
  • 打赏
  • 举报
回复
[Quote=引用 73 楼 slowgrace 的回复:]
接着72楼的说。这时候我需要判断同名树上的相应节点的父亲有没有展开,如果展开了,就把节点移过去,如果没展开,就把节点暂时删了,等以后用户点的时候再自动根据数据表里的新父子关系加这个节点。
[/Quote]
应该是叶子节点的移动吧,那么其实是两步
oTree.Remove(ChildID)
oTree.Add(ToParentID,ChildID)
这样在 CTreeCtl 收到的事件 NodeAdded(ParentID,Child) 中已经有 ParentID 了,哪里用得着 Node.Parent。
即使要取得父子关系,也应该在 CTree 中查找。 CTreeCtl 唯一要关心的 TreeView 层次关系就是某个节点是否已展开。
Tiger_Zhao 2009-03-12
  • 打赏
  • 举报
回复
数据是否要全体载入,完全看你的运行环境和数据量。
这里数据量不是简单的记录数,最直观的方法是你按字段最大长度填充一个集合类,看内存增长多少。

TreeView 要展开时动态添加,是因为每个节点的添加都要耗用一定的时间,乘上记录数就很可观了。
而纯数据结构的集合类,填充速度基本与内存耗用(记录数×记录数据量)有关,而且与数据库操作相比就是小头。在物理内存足够的情况下尽量全部载入。

在一般计算机只有 128M 甚至更低内存的时候,程序一次操作 10^3 的记录是很慢的。
但是 IBM 在演示 DB2 数据库时是同时操作 10^6 的记录,眨眼间完成,因为:他们有 4GB 内存的服务器,在内存中开了临时表空间,然后通过临时表操作数据。——这就是运行环境的影响。
slowgrace 2009-03-12
  • 打赏
  • 举报
回复
接着72楼的说。这时候我需要判断同名树上的相应节点的父亲有没有展开,如果展开了,就把节点移过去,如果没展开,就把节点暂时删了,等以后用户点的时候再自动根据数据表里的新父子关系加这个节点。
slowgrace 2009-03-12
  • 打赏
  • 举报
回复
[Quote=引用 71 楼 Tiger_Zhao 的回复:]
引用 54 楼 slowgrace 的回复:
微软的treeview有个bug,当treeview不可见时,node的parent,next等属性是nothing.

这也是我想有自己的节点集合的原因,我要在自己的节点集合里记录清楚parent,child, next firstsibling等等,而不管它是否可见。所以ctreectl里要记录节点关系,而不是CTree


CTree里也记录节点关系,它是树表的映射。

而CTreeCtl里记录的节点关系是用户点击结果的映射
按你的问题 http://topic.cs…
[/Quote]

我有多树联动,所以要用到。比如有棵树的节点的移动位置了,其他同名树的相应节点也要移位置,即使暂时invisible
Tiger_Zhao 2009-03-12
  • 打赏
  • 举报
回复
[Quote=引用 54 楼 slowgrace 的回复:]
微软的treeview有个bug,当treeview不可见时,node的parent,next等属性是nothing.

这也是我想有自己的节点集合的原因,我要在自己的节点集合里记录清楚parent,child, next firstsibling等等,而不管它是否可见。所以ctreectl里要记录节点关系,而不是CTree


CTree里也记录节点关系,它是树表的映射。

而CTreeCtl里记录的节点关系是用户点击结果的映射[/Quote]
按你的问题 http://topic.csdn.net/u/20090224/13/ec1e6df0-f022-4b1a-8c9f-6f2521f8559b.html 用 TabStrip 试过,重来没有这种问题。
即使某些情况下有你所说的 Bug,不可见的控件没有界面操作,只需要响应事件刷新,根本不需要用到树控件的节点关系。
slowgrace 2009-03-11
  • 打赏
  • 举报
回复
to tiger_zhao:

有个关键的问题,我想我强调的不够,就是,我的一个节点表有可能对应多棵树。比如,tblPjtTree,这个表里存了project树的所有节点(只是节点之间的关系)。

但是应用里,我可能同时有两个树控件都展示这一个表的内容。比如,考虑这样的应用:树的节点很多,一屏放不下,那么我分成左右两个窗口,各放一个treeview,都显示tblPjtTree的数据,左边的窗口显示上部的,右边的窗口显示下部的,或者vice versa。这样,我同时存在两棵树控件,它们对应同一个数据表,这样关系的树我们不妨称为同名树。同名树需要联动。

其实,由于多挂节点的存在,不仅同名树需要联动、异名树有时也需要联动。

比如,当我更改一个基础节点的value时,如果这个基础节点在多棵异名树下都挂的有,那么这多棵异名树都要做相应的变化,即使对应的树控件并不存在,但是对应的节点表里要做变化。

所以,有时候,我就是要完全撇开树控件,而直接操纵节点表(或称树表,也许更形象,一个树表就维护了一棵树的所有节点关系,根据它就可以完全展开一棵树)。

这个时候,我是没办法到对象模型里去取数据的,因为对应的对象不存在……

也许我该专门抽象一个树表类,来做这些事?

像您在43楼帮我设计的那些函数,不仅在load的时候需要用到,在直接操作树表的数据的时候也要用到,所以得有个地方放它们……我现在都放在标准模块basTree里。

也许按9 楼 bdzwj 的说法,我不要追求去追求绝对的OO?就这么办?有点想不清楚。
zz005 2009-03-11
  • 打赏
  • 举报
回复
好厉害啊!好佩服Tiger_Zhao!
sonic_andy 2009-03-11
  • 打赏
  • 举报
回复
[Quote=引用 56 楼 slowgrace 的回复:]

我脑子有点慢哈,一时有点转不过来的说。

我觉得,至少的话,折中一下。

如果树表的内容一次性全Load到内存里,细节表里的内容还是随用随取吧,毕竟细节表的内容我并不全用于树控件的显示的,大部分是用于细节子窗体的显示的。

而且的话,因为我随用随取,用户一次要取的内容很有限(就1个节点:细节子窗体一次只显示一个当前节点的内容;最多的话,也就在listview里同时拖拽多个节点,不过这时候也用不大到细节了…
[/Quote]

这个方式不错:)
ahao 2009-03-11
  • 打赏
  • 举报
回复
[Quote=引用 65 楼 slowgrace 的回复:]
比较汗的是,人家都拼命地要把数据和视图相分离。我的设计却是努着劲儿的想把数据和视图紧密耦合起来。比如我在我的树类里加了个属性叫m_strNodeTable,用来表明这个树是表现哪个节点表的数据。

[/Quote]

树类不应该知道是表现哪个表,耦合了,表的设计如果改了会很麻烦。
View最好是只知道一个抽象的数据提供者,听他的变化事件,通过数据提供者暴露的接口来控制改变数据。



ahao 2009-03-11
  • 打赏
  • 举报
回复
还有一点就是你要分清楚什么是核心数据,什么是UI数据,别混起来
ahao 2009-03-11
  • 打赏
  • 举报
回复
恩,没仔细看前面的回复,其实好几位都已经回答了,差不多的意思。
但细节问题还是很复杂的,VB不懂,不好说什么了。
slowgrace 2009-03-11
  • 打赏
  • 举报
回复
Tiger_Zhao的视角和你的不同,但是他的设计是符合这种模式的。
slowgrace 2009-03-11
  • 打赏
  • 举报
回复
恩 有道理。其实微软提供的treeview只是一个视图

而我现在在做的事,是提供控制和model

比较汗的是,人家都拼命地要把数据和视图相分离。我的设计却是努着劲儿的想把数据和视图紧密耦合起来。比如我在我的树类里加了个属性叫m_strNodeTable,用来表明这个树是表现哪个节点表的数据。

ahao 2009-03-11
  • 打赏
  • 举报
回复
其实tree控件有V和C两个角色,你自己分清楚就可以了。
ahao 2009-03-11
  • 打赏
  • 举报
回复
操作者是controller,也就是MVC模式里的C了,MVC和观察者模式要表达的东西其实差不多的,后者没有提C那个部分而已。相对不是那么的关键。


slowgrace 2009-03-11
  • 打赏
  • 举报
回复
[Quote=引用 57 楼 ahao 的回复:]
这种问题应该不难啊,典型的观察者模式

数据部分:
从数据库里取出的要显示数据,以某种形式组织在内存里,比如可以是一个hash表。维护观察者计数和相关信息。

界面部分:
不管你是tree还是什么其他的什么ui,都是观察者,只要listen自己关心的数据就好了。

树之间复制节点什么的之类的问题,根本不应该把两者耦合起来,你管他是树还是什么其他东西呢?就是两个观察者而已,
如果以后你还要支持list,edit,st…
[/Quote]

刚看了下观察者模式,感觉可能不大一样。在观察者模式里,观察者是不操作被观察对象的。而我在树上会拖拽增删改节点,是直接通过用户界面修改后面的数据的,可能不一定适用,你觉得呢?
加载更多回复(54)

7,763

社区成员

发帖
与我相关
我的任务
社区描述
VB 基础类
社区管理员
  • VB基础类社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧