写一个小界面工具“ImageRuler”,遇到一个问题。有三种类型的参考线,其中“边界参考线”与“图片框参考线”行为基本一致,“按钮参考线”的行为有所不同,我已经将这两种类型抽象成struct,方便使用。他们都有“SetLine”和“FindNearLine”这两种操作。
AreaBorder m_border; //边界参考线
ButtonSplitLines m_buttonLines; //按钮参考线
AreaBorder m_frameLines; //图片框参考线
在进行“SetLine”和“FindNearLine”操作时,如果上层必须知道对哪一种类型的东西进行操作,就必须写switch语句,造成重复代码。去掉这种重复,最明显的思路就是“继承”,因为他们接口类似,所以继承以后,上层就直接调用方法,而不管具体类型是什么了。
No!
AreaBorder和ButtonSplitLines这两种类型是我自己设计的,我自认为他们的相似程度,还不够达到共用接口的程度!如果继承自同一个接口,就可能要面对有一些类似,关键地方又不同的窘境。
在这里,打死我也不继承。
思来想去,用了一种很朴实的办法,将操作封装起来。
enum
{
eOperate_SetLine,
eOperate_FindNearLine,
}
int ModeOperate( int iMode, int iOperateType, void* pData );
参数1 iMode,是选择一种参考线,参数2是选择一种操作,参数3是可变的参数。上层受点累,需要把枚举值传进来(如果真觉得累,封个GetMode()函数,就行啦)。
ModeOperate(...)的实现,通过switch-case对每一种情况写上代码就行。用函数把重复的操作去掉,朴实而简洁。
——————————————————————————————————
对这个实现,我个人非常满意,自认为没有掉进继承的陷阱里。当时非常兴奋,又想起了云风有过类似的文章。看到了他介绍Go语言,就去看Go语言的入门视频。发现Go语言有个解决这种问题的特性:Interface
C++的毛病,与Go语言(还有一些好的动态语言)的区别,用我自己的话说是这样:
我是一个程序员,我有Coding()的方法,于是我继承自CProgrammer。因为我还是一个发烧友兼拳皇玩家,我会EnjoyMusic()和PlayKOF98(),那我需要多重继承一下CHifier和CKOFer。
这样我认为不对。如果用组合代替继承如何?将不同的“操作类”组合到我的大脑里。感觉真别扭。
Go语言与一些我不大会用的动态语言,提供了一种叫做“Duck Type”的特性,如果我会Coding(),那么就可以把我当成Programmer使用,不需要我Coding()的时候,我就去PlayKOF98(),我不会受任何一种基类的制约,也不需要实现我不喜欢的方法,需要我做什么的时候,我就变成需要的那种人(如果我能变)。