一、我们都知道设计经验的重要价值。你曾经多少次有过这种感觉:你已经解决过了一个问题但就是不能确切知道是在什么地方或怎么解决的?如果你能记起以前问题的细节和怎么解决它的,你就可以复用以前的经验而不需要重新发现它。然而,我们并没有很好记录下可供他人使用的软件设计经验。如何将面向对象软件的设计经验记录下来呢?设计模式这个概念的出现使我们能从思维上进行理论上系统的把握。
设计模式使人们可以更加简单方便地复用成功的设计和体系结构。将已证实的技术表述成设计模式也会使新系统开发者更加容易理解其设计思路。
二、 至于什么是设计模式呢?GoF在《设计模式》一书中将模式描述为:在一定环境中解决某一问题的方案,包括三个基本元素--问题,解决方案和环境。同时也提出了一个记录模式的模板,有兴趣的读者可以去读该书(强烈建议初学者以该书作为入门数目)。不过,就我个人简单化的习惯的话,最为常用而又最具有意义的就是:意图、动机和适用。这三个方面从实际入手把一个解决方案很好的描述出来,首先是为了解决什么问题,其次是怎样解决问题,最后是现实的实用性。
以下我对怎样学习设计模式的建议:
1)熟悉C++的读者,可以去参考GoF的《设计模式》一书,该书可以说是设计模式教材的鼻祖,以后很多教材都是参考这本书的,是入门的必读之作。熟悉Java的人可以去参考另外一本书:James W. Cooper的Design Patterns,该书好像没有中文版。
2)其次,在学习过程中,要从解决的问题与环境入手,因为设计模式主要是针对解决方案的。只有到了具体的实例中,具体的设计模式才有相应的代码。同时在学习过程中,最好使用两种以上的语言来实现该模式,这样,就可以通过比较来掌握该种情况下如何通过代码来实现,掌握该种情况下的模式怎样实现。所以,最好掌握了C++和Java,然后在学习模式过程中不断的练习。
3)要有目的的学习。在实际工作中,当遇到了某种问题已经解决的时候,要总结自己的经验,同时要把这种经验用设计模式表达出来,这样,以后再次遇到该问题的时候,由于头脑已经有这种模式的思想存在,就能迅速的解决问题了。或者看别人的源代码时,遇到很好的解决方案时,要注意总结,形成自己的设计模式。一句话,只有不断的学习才能最大的成功。
4)最重要的一点,不要老是抱着设计模式的概念不放。GoF的<<设计模式>>一书提出了基本的23种设计模式,但这些设计模式到了实际的应用中千变万化,不可能全部把握,只要记住一点,万变不离其宗。设计模式的意义在于有一个具体的概念来把我们的经验总结,而不是为了模式而学习模式,应该是为了具体的方案来学习模式。只有这样,你才不会被那些千变万化的模式迷惑自己。
最后,我想提出的一点是,平时要多想想,多动一下脑筋,总结自己的经验,这样的话,我们的代码生活也许会变得更加有趣。
三、以下是一些具体实用的概念:
1、先提一个我们怎样组织对象之间关系的概念。DI(Dependency Injection):依赖注入,以前有个叫法是IoC,Martin Fowler在Inversion of Control Containers and the Dependency Injection pattern一书中详细解释了什么是依赖注入。这个概念为我们了解对象之间的关系和怎样在具体代码中实现这些关联很有帮助。事实上,这个概念主要是为了实现容器方便化的配置与更好的软件复用,同时减轻了组件之间的依赖关系,同时也大大提高了组件的可移植性,这意味着,组件得到重用的机会将会更多。熟悉j2ee的读者应该明白这种意思,spring框架就是一个很好的使用了DI的一个框架与容器。这里把这个概念最为片面简单化,用这个概念来表达怎样了解对象之间的关系,以下为依赖注入的几种实现类型(也是在这里为什么介绍DI的原因):
1)接口注入:我们通常借助接口来将调用者与实现者分离。在运行期,容器可以通过dynamic_cast来把接口的实现(或者说是子类)把具体的实现注入该实例中。我们在平时编程时,经常想到的是使用该种方法来建立类之间的关系。
2)设值注入:这在java中很常见,同时也很直观、自然。JavaBean对属性的存取中的思想也是通过这样实现,通过一系列的setter和getter方法来实现。在c++中,这种方法也常见,通常是声明一个私有的成员变量,该变量是指向某对象的指针,类通过提供一个公有方法来访问该对象即可。而在DI中,这种模型得到了最广泛的应用(如spring框架中)
3)构造器注入:通过在构造函数中声明对其他对象的引用来实现,这种方法在c++中比较常见一点。这种方法避免了繁琐的setter方法(这个问题一般IDE都替我们解决),同时因为少了setter方法,无需担心上层代码调用setter方法造成的影响。通过构造子注入,意味着我们可以在构造函数中决定依赖关系的注入顺序,对于一个大量依赖外部服务的组件而言,依赖关系的获得顺序可能非常重要。