写完了Interpreter模式之后,我习惯性的看看下一天的安排,却陡然发现GoF的23个设计模式的解析已经在我不经意间写完了。就像在一年前看GoF的《设计模式》一书,和半年前用C++模拟、实现23种经典的设计模式一般,透过这个写解析的过程,我又看到了另外一个境界。一直认为学习的过程很多时候可以这样划分:自己学会一门知识(技术)、表达出来、教会别人、记录下来,虽然这个排序未必对每个人都合适(因为可能不同人有着不同的特点能力)。学一门知识,经过努力、加以时日,总是可以达到的,把自己学的用自己的话表达出来就必须要将学到的知识加以消化、理解,而教会一个不懂这门知识的人则比表达出来要难,因为别人可能并不是适应你的表述方式,记录下来则需要经过沉淀、积累、思考,最后厚积薄发,方可小成。
设计模式之于面向对象系统的设计和开发的作用就有如数据结构之于面向过程开发的作用一般,其重要性和必要性自然不需要我赘述。然而学习设计模式的过程却是痛苦的,从阅读设计模式的圣经——GoF的《设计模式:可复用面向对象软件的基础》时的枯燥、苦闷、茫无头绪,到有一天突然有一种顿悟;自己去实现GoF的23中模式时候的知其然不知其所以然,并且有一天在自己设计的系统种由于设计的原因让自己苦不堪言,突然悟到了设计模式种的某一个正好可以很好的解决问题,到自己设计的elegant的系统时候的喜悦与思考;一直到最后向别人去讲解设计模式,别人向你咨询设计模式,和别人讨论设计模式。就如GoF在其前言中说到:一旦你理解了设计并且有了一种“Aha!”(而不是“Huh?”)的应用经验和体验后,你将用一种非同寻常的方式思考面向对象设计。这个过程我认为是漫长的,painful,但是是非常必要的。经过了的才是自己的,Scott Mayer在其巨著《Effective C++》就曾经说过:C++老手和C++新手的区别就是前者手背上有很多伤疤。是的在软件开发和设计的过程中,失败、错误是最好的老师,当然在系统开发中,失败和错误则是噩梦的开端和结束,因为你很难有改正错误的机会。因此,尽量让自己多几道疤痕是对的。
面向对象系统的分析和设计实际上追求的就是两点,一是高内聚(Cohesion),而是低耦合(Coupling)。这也是我们软件设计所准求的,因此无论是OO中的封装、继承、多态,还是我们的设计模式的原则和实例都是在为了这两个目标努力着、贡献着。
道不远人,设计模式也是这般,正如我在《设计模式探索(总序)》中提到的。设计模式并不是空的理论,并不是脱离实际的教条。就如我们在进行软件开发的过程会很自然用到很多的算法和结构来解决实际的问题,那些其实也就是数据结构中的重要概念和内容。在面向对象系统的设计和开发中,我们已经积累了很多的原则,比如面向对象中的封装、继承和多态、面向接口编程、优先使用组合而不是继承、将抽象和实现分离的思想等等,在设计模式中你总是能看到他们的影子,特别是组合(委托)和继承的差异带来系统在耦合性上的差别,更是在设计模式多次涉及到。而一些设计模式的思想在我们做系统的设计和开发中则是经常要用到的,比如说Template、Strategy模式的思想,Singleton模式的思想,Factory模式的思想等等,还有很多的模式已经在我们的开发平台中扎根了,比如说Observer(其实例为Model-Control-View模式)是MFC和Struts中的基本框架,Iterator模式则在C++的STL中有实现等。或许有的人会说,我们不需要设计模式,我们的系统很小,设计模式会束缚我们的实现。我想说的是,设计模式体现的是一种思想,而思想则是指导行为的一切,理解和掌握了设计模式,并不是说记住了23种(或更多)设计场景和解决策略(实际上这也是很重要的一笔财富),实际接受的是一种思想的熏陶和洗礼,等这种思想融入到了你的思想中后,你就会不自觉地使用这种思想去进行你的设计和开发,这一切才是最重要的。
之于学习设计模式的过程我想应该是一个迭代的过程,我向来学东西的时候不追求一遍就掌握、理解透彻(很多情况也是不可能的),我喜欢用一种迭代的思想来指导我的学习过程。看书看不懂、思想没有理解,可以反复去读、去思考,我认为这样一个过程是适合向我们不是有一个很统一的时间去学习一种技术和知识(可能那样有时候反而有些枯燥和郁闷)。GoF在《设计模式》一书中也提到,如果不是一个有经验的面向对象设计人员,建议从最简单最常用的设计模式入门,比如AbstractFactory模式、Adapater模式、Composite模式、Decorator模式、Factory模式、Observer模式、Strategy模式、Template模式等。我的感触是确实是这样,至少GoF列出的模式我都在开发和设计有用到,如果需要我这里再加上几个我觉得在开发中会很有用的模式:Singleton模式、Façade模式和Bridge模式。
写设计模式解析的目的其实是想把GoF的《设计模式》进行简化,变得容易理解和接受。GoF的《设计模式》是圣经,但是同时因为《设计模式》一书是4位博士的作品,并且主要是基于Erich的博士论文,博士的特色我觉得最大的就是抽象,将一个具体的问题抽象到一般,形成理论。因此GoF的这本圣经在很多地方用语都比较精简和抽象,读过的可能都有一种确实是博士写出来的东西的感觉。抽象的好处是能够提供指导性的意见和建议,其瑕疵就是不容易为新手所理解和掌握。我的本意是想为抽象描述和具体的实现提供一个桥接(尽管GoF在书中给出了很多的代码和实例,但是我觉得有两个不足:一是不完整,结果是不好直接看到演示,因此我给出的代码都是完整的、可编译运行的;二是给出的都是一些比较大的系统中一部分简单实现,我想GoF的原意可能是想说明这些模式确实很管用,但是却同时带来一个更大的不好的地方就是不容易为新手理解和掌握),然而这个过程是痛苦的,也可能是不成功的(可能会是这样)。这里面就有一个取舍的问题,一方面我想尽量去简化GoF的描述,然而思考后的东西却在很多的时候和GoF的描述很相似,并且觉得将这些内容再抽象一下,书中的很多表达则是最为经典的。当然这里面也有些许的例外,Bruce Eckel在其大作《Thinking in Patterns》一书中提到:Bridge模式是GoF在描述其23中设计模式中描述得最为糟糕得模式,于我心有戚戚焉!具体的内容请参看我写的《设计模式解析——Bridge模式》一文。另外一方面,我又要尽量去避免走到了GoF一起,因为那样就失去了我写这个解析的本意了。这两个方面的权衡是很痛苦,并且结果可能也还是没有达到我的本意要求。
4月份是我最不忙的时候,也是我非常忙的时候。论文的查阅、思考、撰写,几个项目的前期准备(文档、Demo等),俱乐部的诸多事宜,挑战杯的准备,学习(课业、专业等各个方面)等等,更加重要的是Visual CMCS(Visual C_minus Compiler System)的设计和开发(Visual CMCS是笔者设计和开发的C_minus语言(C的子集)的编译系统,系统操作界面类似VC,并且准备代码分发和共享,详细信息请参考Visual CMCS的网站和Blog中的相关信息的发布), Visual CMCS1.0(Beta)终于在4月底发布了,也在别人的帮助下构建了Visual CMCS的网站(在五一假期后将正式上传到服务器)。之所以提及这个,一方面是在Visual CMCS的设计和开发体验了很多的设计模式,比如Factoty模式、Singleton模式、Strategy模式、State模式等等(我有一篇Blog中有关于这个的不完全的描述);另外一方面是这个设计模式解析实际上在这些工作的间隙中完成的,我一般会要求自己每天写一个模式,但是特殊的时候可能没有写或者一天写了不止一个。写这些文章,本身没有任何功利的杂念,只是一个原生态的冲动,反而很轻松的完成了。有心栽花未必发,无心之事可成功,世间的事情可能在很多的时候恰恰就是那样了。
最后想用自己在阅读、学习、理解、实现、应用、思考设计模式后的一个感悟结束这个后记:只有真正理解了设计模式,才知道什么叫面向对象分析和设计。
谨此与大家商榷、共勉。