« 让 win32 程序也可以从 console 输出信息 | 返回首页 | google 和 baidu 的用户习惯之比较 »

从 Command 模式看 C++ 之缺陷

设计有 UI 的软件,Command 模式可以说是可能用到的设计模式中最常用的一种了。它隐藏了关于命令发起者的相关对象以及命令处理过程的细节。也就是建立一个第三方的对象,用来解耦发送者和接收者的联系。

Command 模式最常用的用途就是给软件做 undo/redo 的功能。只用维护一个链表放置命令序列,就可以方便的记住发出的命令。每个 Command 对象实现一个乒乓开关(或者实现一对 undo/redo ),在对象内部保留住相关对象,自己执行 undo 或 redo 操作即可。

在动态语言里,一个 Command 对象,往往用一个 closure 来实现。closure 天然的具有封装参数和过程的能力。而 C++ 中,我们必须实现一个单独的类。这不仅使代码冗长,而且因为这个类往往和命令操作的对象栖息相关。不想写过多的供外部使用的方法的话,最直接的方案是 friend 一下所有的相关 Command 类。可无论怎样,一大堆代码或是难看的宏是避免不了了。

由于 C++ 没有 gc 的能力,所以在命令执行中构造和删除对象要十分小心。因为带 undo 功能的软件,即使你删除一系列对象,也不能真正从内存中释放掉。转而应该保留在 Command 对象内部。

典型的需求是向一个 TreeView 中删除节点的操作,只能是从 TreeView 上摘除(unlink)而不是删除,否则无法 undo 。当然还有一种实现方法是:从命令序列的头开始从头执行所有的指令,效率极低且不总是有效。只要命令序列中有涉及外部数据的读取,由于我们无法保证外存数据的不变,所以就不能保证 undo 的有效性。

智能指针是一种折中的解决方法。而一般在 C++ 中更高效的使用 Command 模式的方案是:任何的对象的诞生和消亡都由 Command 对象来负责,而 Document 中的对象之间只用指针记录其关系。这在设计角度,实在是没有支持 gc 和 closure 的动态语言来的简洁。

Comments

实现gc其实有很多方法。 可以自己写,也可以用现成的库。 为什么一定要C++支持呢?
to Cloud:也许吧~ IMHO第一次看到。
请教各位大哥.就是如何读取Jpeg图片的高度,宽度.主要是直接通过内存来读去,比方说向这样 FILE* ostream = ::fopen( this->GetFullName(), "rb" ); if( !ostream ) return; if( !_tcsicmp( TEXT("jpg"), szExt ) ) { ::fseek( ostream, 18, SEEK_SET ); //18是不对的,这点应该移动到什么地方呀.请教各位.谢谢了哈.实在是急 ::fread( &dwTemp, sizeof( DWORD ), 1, ostream ); m_nWidthOriginal = dwTemp; ::fseek( ostream, 22, SEEK_SET ); ::fread( &dwTemp, sizeof( DWORD ), 1, ostream ); m_nHeightOriginal = dwTemp; }
滥用 template…… 也许真的是滥用,不过那很好玩
to gql_w : 这是对 gc 的误解 (IMHO)
还是效率的原因,现在的计算机速度对某些应用来说还是很慢,不得不使用相当的算法,技巧,甚至“偷工减料”来使速度达到可以接受的程度,比如游戏实施计算渲染。虽然总有人说计算机运行的越快了,但我们需要的增长总是比硬件速度增长的更快,。现在在C++中引入语言级的gc功能,技术上似乎没问题,不过跟c++的设计初衷和它试图要覆盖的问题集无法找到交集,除非有一天gc可以快到手工管理对象一样的效果。 见笑~
但是反过来想想,要不没有C++的这些个硬伤,哪有后来的这些语言的进步呢?
C++ builder 以及 delphi 中的 closure 实在是弱了一点:只能绑定一个对象指针。这离好用还差了很远。不过对 C++ 来说也算一个进步,C++ 要实现这样的功能就会受多继承的限制。如果人为的排除多继承的干扰,把派生类的成员函数指针转换成基类相同原型的成员函数指针还是安全的。 结果 C++ 弄出个 bind 这样不伦不类的东西出来。我觉得如此滥用 template 绝对不会是 C++ 的方向。
C++Builder倒是有closure,但这不是C++标准,而是从Delphi那里“兼容”过来的。就像C++Builder既有标准C++的编译时Meta,也有运行时的Meta,后者同样是来自Delphi。 也许C++的确应该增加对GC的支持,如C++/CLI。 但是我觉得,这样的C++还是C++吗?那还不如直接用支持这些feature的VM语言或动态语言来实现。 其实C++的定位现在看来的确是比较尴尬的。
其实gc也可以靠代引用计数的智能指针来做,不过和有虚拟机的语言相比,还是有很多限制
说到closure,C++也有类似的东西,boost::bind 不过没有支持 gc 仍然是硬伤。boost虽然会维护绑定的参数对象的生命周期,但是那是隐晦和危险的

Post a comment

非这个主题相关的留言请到:留言本