作为C++的坚实粉丝,我一直很排斥JAVA,并不是JAVA这种语言不好,而是Java迷的那副嘴脸,事事都要与C++争,并且还坚称JAVA比C++,甚至连执行效率都要胜过C++,什么JIT运行时能监视代码,选取执行频率最高的代码,根据特定的平台,进行特别处理,生产出最优化的机器代码;又说什么垃圾回收能够解决C++中的内存管理问题,并且内存分配远远胜过C++中的手工人肉管理内存的毛病,其实,内存管理从来就不是C++中的严重问题,只要设计得好,应用层中的代码甚少可以不出现new, delete。至少delete可以不出现,大家知道WHY的;种种论调,无理取闹之极。而最令我受不了的,就是他们闭口开口,都离不开设计模式,搞得设计模式好像成为了JAVA的专有产品。须知,第一本设计模式的书,也是最最经典的那本四人书,其对设计模式的实现语言,采用的就是SmallTalk 和C++,一开始就不关JAVA的一点事情,并且书中C++还占了较大的比重。关于四人书,最让我欣慰的一件事情就是,四位巨头并没有响应JAVA迷的强烈呼声,采用JAVA来实现设计模式的第二版。当然,我也承认,用JAVA来实现设计模式,确实来得要比C++清爽,JAVA的这种语言,好像就是专为设计模式量身订做。只可惜,市面上任何一本JAVA的设计模式书,没有一本及得上我们C++的那一本设计模式圣经,C++中不必再需要设计模式的书了,因为最好的书就已经摆在那里了,涵盖了设计模式中的方方面面,浓缩精华得很。突然想起,C++的教材也不需要那么多,因为老爷子已经写了一本最好的书了,其他书的内容,都已经涵盖在那本C++语言圣经中了。至于那些不被C++圣经所提及的,那都是一些走火入魔的玩意,玩玩还可以,开阔开阔视野也不错,但真要用在实际项目中,还是少用为妙。罪过罪过,C++中的好书确实有好几本,不宜一棍子打死。
有趣的是,设计模式在JAVA中被捧上了天,没有设计模式,JAVA就没法活下去。反而C++作为设计模式的第一实现语言,却不怎么感冒。不能说被轻视,但起码没有普遍被重视。即使C++中的高手,也没有对设计模式如何如何的推崇备至,他们貌似更加喜欢玩语法糖,热衷于在C++中模拟出其他语言的特性。这也难怪,C++有四种编程典范,设计模式的主要应用就在于面向对象,面向对象不过是其中最不成熟的一种,在C++中,最成熟的要数基于对象的编程,当然,拜C语言成熟,面向过程也熟得要烂了,泛型这东西也挺前卫的,而C++中的面向对象,一直臭名昭著,无论怎么努力,都难以搬上台面。注意,C++中的面向对象与虚函数是两码事,至少在我看来是这样的。
好了,该谈谈我对设计模式的看法了,一句话,我对设计模式殊无好感,源于我内心对当前的面向对象编程风格的排斥,因为那是伪面向对象编程,所谓的伪面向对象,就是指通过单根继承和针对接口或抽象类来写代码,就以为是在进行面向对象的编程,那实在是太天真了。设计模式中的那些点子,用得好,确实能解决伪面向对象的一些问题,无可厚非;用得不好,无缘无故地引入一些不必要的东西,模式的应用意味着间接性的增厚。现实中,能恰当地用好模式的人,少之又少,模式总是出现在一些不必要出现的场合下。很多人都是生吞活剥了23种模式之下,内心就沾沾自喜,到处跃跃欲试,鲜有人尝试理解模式背后的统一思想是什么,或者说,如果代码本身就已经能够很好类与类之间的耦合问题,可胜过千万种设计模式。
以下文字,与孟岩大侠的观点,存在部分重复之处,读者认为在下是在拾他的牙慧,我也不反对,毕竟人家的文章反表在前,我无话可说,本文的重点,旨在表达在下对设计模式的鄙视。
既然有伪面向对象,就有真面向对象。真面向对象的编程,就是你只知道一个对象的ID,其他的一切事情,它继承自那些父类,实现了那些接口,统统一概都不得而知,然后你只能通过这个ID给对象发送消息,以此来驱使对象做一些事情,注意,确确实实是只是发送消息,而不是调用该对象的函数,那怕是调用了该对象实现的接口函数,也意味着该对象的依赖,好吧,说错了,是对该接口的依赖,不管怎么样,都是依赖,而且依赖接口,还搞得客户代码和对象都要依赖于同一个接口了。
给对象发送消息,听起来似乎有点抽象,但是,只要联想到WINDOWS的窗口句柄和消息处理函数,就自然明白是怎么回事了。在WINDOWS下,每一个窗口都是一个对象,窗口句柄代表了对象ID。操作窗口时,只能通过SendMessage或PostMessage来让窗口作一些事情。SendMessage或PostMessage的四个参数,分别是对象ID、消息编号、消息参数1和消息参数2。客户代码在使用窗口时,不需要假设什么接口,能做的,仅须做的,仅仅就是给它发送消息,客户代码完全无须依赖于什么鬼接口,非常简单明了。但是,这里存在一个问题,消息参数1和消息参数2都是void*类型,不安全耶,要是误发送错了消息,那该怎么办。在伪面向对象中,客户也可能在调用接口参数时,也可能会传错了参数,只不过是编译器可以帮你找出来。其实类型不安全,也没什么,客户既然要发送消息给对象,自然要遵守使用的协议,自然要清楚自己在做什么事情,这本来就是C语言的精神,一切相信程序员。
好了,该是给WINDOWS大唱赞歌了。WINDOWS系统的窗口是我见过中算是比较象样的面向对象的典范了。将面向对象的精神贯彻到底,窗口对象很好地做到了仅仅是纯粹解析消息,执行消息,与外界的其他对象,不存在任何一点耦合。一个窗口对象中的lpfnWndProc其实可以理解成指向虚函数表的指针,但是它却要比虚函数表的指针功能更加强大灵活,并且还不存在虚拟函数表的种种问题。强大之处,就之于lpfnWndProc相当于指向一个庞大的虚函数表,这个表有2的32次方多个虚函数,灵活之处可以突破虚函数的种种限制。当你要给一个窗口对象添加新的功能,或者不满意它的某些原有操作的时候,你完全可以子类化该窗口,在子类化操作中,截取关心的消息,解析消息,执行消息,至于其他的消息,直接交给lpfnOldWndProc或DefWindowProc就是了。从这个意义上讲,所有的窗口对象继承于DefWindowProc,而子类化窗口即是继承于lpfnOldWndProc,但是,这里的继承,却没有伪面向对象中的继承或实现接口的种种弊端。而且,在你子类化窗口的时候,不用影响到客户的任何一点点代码,客户代码依旧是发送消息,却不知道窗口对象已经旧貌换新颜了,OH,这实在太美妙了。所有的耦合,奇迹般的消失得干干净净了。消息比之于接口,那完完全全是纯粹的正交关系,并且没有接口的种种累赘,种种缺陷。并且更爽的是,即使对象没有处理消息,也没有关系,客户代码依然可以给对象发送消息。
从某种意义上讲,设计模式不过是为了解耦对象与对象之间的耦合关系,当对象之间不存在耦合的时候,设计模式还有什么存在的意义吗?以下结合WINDOWS系统来理解,所谓的设计模式,不过是消息发送的一些应用罢了。下面的举例,例子之间没有什么必然关系,我想到那里就写到什么,模式后面似乎应该带上英文,但我也懒得写了。
观察者模式:一个广播消息就搞定了;
模板方法:不过是按顺序给一个对象发送某些指定的消息而已;
工厂方法、抽象工厂:用一个或几个lpClassName就搞定了;
原型:不过是发送一条WM_COPYOBJECT的消息而已;
装饰者或者状态:嘿,子类化就完成了,并且非常彻底,一点都不觉得别扭;
命令:对象将SendMessage中的消息编号、消息参数1和消息参数2保存起来就是了,这没什么大不了的;
策略:不过一个回调函数,外加必要的参数;
桥接:貌似没什么必要;
……
没有了!落得个一片白茫茫大地真干净!