模式是一个已经被前人充分讨论过的有趣话题。最早谈及模式的书是Erich Gamma等编写的《设计模式》(Boston:Addison-Wesley,1995)。该书出版时,模式还是一种新的思想和概念;而现在该书已经成为设计模式的必备参考书,其中阐述的设计模式成为了各种应用程序的基础。.
本书阐述了应用于.NET 2.0框架的设计模式。其中的一些设计模式来自于该书,一些则来自其他来源。本书的重点并不是要定义各种模式,而是用一种编程语言(例如C#)来演示各种模式的应用。因为最初的设计模式是利用C++进行阐述的,与.NET以及C#之间存在着很大的语言差异。
为什么使用模式
你也许会问:“为什么要写这本书,为什么要使用模式?”的确很奇怪,这个想法是在我培训学生如何使用设计模式的时候冒出来的。当时学生们正在做练习,我看见一位学生编写代码时使用一个类而不是接口作为基类。我问他为什么这样编写代码,他的回答是:“因为我一直都是这样做的。”这位同学的方法是错误的,但是他的回答揭示了一个很有趣的观点:当一个基类足够好的时候为什么还要使用接口呢?就在那时,我突然想到:先教授模式再教授面向对象编程(OOP),理解就会更为容易。
当学习传统的OOP技术时,老师会讲到有关形状、矩形以及其他的抽象主题。你会学到一个类如何负责它自己的数据以及如何实现这些责任。这种学习方式的问题是,它解释了面向对象编程,但是并没有解释如何利用具体的方案来解决问题。例如,如何实例化一个类型,将它的引用传递给另一个类,并将信息持久化到媒介中?传统面向对象编程的问题是,在实现解决方案时只给出了模糊的指导。
模式就不同了,它给出了一个预定义的形式,告诉你在什么时候该做什么。考虑下面的情况:学习如何烘焙蛋糕。你知道,蛋糕是由面粉、鸡蛋、牛奶、发酵粉以及一些其他原料做成的,但并不是所有蛋糕都是一样的。如果加入过多的发酵粉,蛋糕会过分地发酵胀起;如果加入过多的鸡蛋,蛋糕吃起来会像是煎蛋卷。面向对象编程就像是烘焙蛋糕一样,你知道所需的原料,也知道这些原料的作用,但是不知道这些原料的比例以及制作蛋糕的步骤。就像好的食谱能够帮助你把这些原料变成美味的蛋糕一样,模式能够帮助你将代码转换成高效的程序。
在不知道原料的作用以及它们之间是如何相互作用的情况下,也可能只根据食谱制作出蛋糕。但这种方法的问题是它无法奏效。例如,假设你正在准备一顿饭,开始是豌豆汤,主菜是胡椒薄荷沙司调味的鲑鱼、南瓜以及黄豆面条,最后是餐后甜点冰淇淋。尽管可能每道菜的味道都很好,但这个菜谱听起来并不令人胃口大开。其中的问题是模式,就像是菜谱,之间是相互作用和配合的,因此需要协调。协调模式的前提就是对面向对象编程技术有一个基本的理解。
什么是模式
模式就像菜谱,只不过用于创建模式的原料是面向对象的原理。例如,考虑下面的源代码:
类Derived实现了接口IBaseInterface,这就是典型的Bridge模式的实现。一定会有人问,为什么使用接口而不是类呢?从面向对象的角度看,它们的结果很相似。这就引发了一个问题,为什么在编程语言中要用接口呢?答案是:因为模式定义了最佳实践,而该实践已经被证明在应用程序开发中是有益的。从大量的编程经验来看,在使用基类型以及实现Bridge模式时,接口比类更有用。
模式不同于最佳实践,就像理论不同于猜想一样。理论基于已经被科学方法无数次证明的想法,科学方法是一种其他人也能够重新生成结果的实验方法。模式就像是一本食谱,它定义了一系列的步骤以及原料,当按照定义的步骤操作原料时,就能够做出相同的蛋糕。猜想是一种想法,它很可能是正确的,但是并没有被不同的人用必要的重复的实验证明。猜想是理论的先驱,意味着最佳实践是模式的先驱。..
《设计模式》一书中定义的模式已经被证明是有效的,并且已经应用于多种情况。例如,利用Factory模式定义的结构,可以在不同的编程环境中,利用不同的编程语言产生相同的结果。
你能从本书获得什么
除了模式以及面向对象原理,通过本书将学习像音乐家阅读乐谱那样阅读代码。思考一下,音乐家利用只有音乐家才懂的符号来表达他们的思想。乐谱,代表了要演奏的音乐,对于不懂的人来说就像是一堆胡乱画出的符号。然而对于音乐家,它是巴赫、莫扎特、AC/DC乐队或者说唱乐手Eminem,意味深长。当阅读乐谱时,音乐家知道何时演奏他们的乐器,以及当时产生的是一种什么样的情绪。没有对乐谱的简化和抽象,音乐家阅读乐谱后只知道那是一段怎样的音乐。
本书的目的就是使你能够阅读一段代码并且明白其中的意图。在很多开源项目中,经常需要阅读其他人编写的代码。对于其他类型的项目情况就不是这样,每位编程者都使用自己喜欢的风格和技术编写代码。例如,就像是将大括号放在哪里这样的小问题也会引发激烈的争论。会有这种争论的原因是,每位编程者都在实践中或者参加会议的幻灯片中或者别人的注释中学到了一些风格和技术。阅读一段代码并不只是识别出一些关键字,而是理解在一种环境中使用一段关键字解决一个特定的问题。简言之,阅读一段代码就是在模式的环境中诠释OOP。
从哲学的层面考虑一下,一种编程语言为什么会存在?问问自己,为什么一种编程语言包含了一个特定的关键字、概念或者策略?答案是,因为那个关键字、概念或者策略在一个特定的环境中解决了一个特定的问题。但并没有解释何时才是使用这种特征的最佳时机。例如,在C#中,可以使用关键字struct、class、interface、abstract以及sealed来定义一个用于程序的类型。通常编程者知道上述关键字的技术理由以及效果。然而,编程者通常不知道每个关键字在什么情况下应该或者不应该使用。这就回到了起点,不管在什么地方使用,编程者会依赖于他们自己的习惯或风格。这并不是正确的编程方法,因为它有可能依赖于试错(trial and error)。
本书内容
本书的每一章在知识上都是循序渐进的,并且各有明确的目标。
第1章揭示了面向对象编程的本质,利用C#定义和阐述了模块化和异常等概念。阅读这一章有助于更容易地理解各种OOP词汇。
第2章的主要目的是定义和阐述测试驱动开发,它是一种通过一致地创建测试和源代码来开发软件的方法。这种方法的优点是保持源代码的稳定和一致。
.第3章将会接触到作为其他设计模式基础的基本设计模式——Factory(工厂)模式和Bridge(桥接)模式。
第4章演示了将基本模式应用于架构策略的创建。上一章介绍的设计模式是通用的,但并不能用于整体架构。架构策略能够很容易地扩展和维护,阐述了如何解决应用问题。利用定义好的模式,演示了如何开始创建具有正确开端的应用程序。
当实现应用程序时,主要问题经常是利用良好开发的代码使应用程序能够正常工作。接下来的需求和要求会增加新的类或者修改现有的类,而这些增加或修改可能会引发问题。第5章探讨了这个问题,提出了一些用于更容易实现应用程序的模式。
在编写代码时,通常试图走一些捷径来解决问题。这个捷径可能很小但却可能有重大的影响。在第6章中给出了一些模式,定义如何处理想要走捷径的情况,其中解释的模式都很详细,似乎有些大题小作。但是,这些模式的主要目标是简化对代码的扩展和维护。
在编写代码时,目标是使代码尽可能的高效。第7章教你如何编写在扩展以及维护的同时又不增加理解难度的高效代码。
序列化和持久化是很多关于模式的图书回避的话题,然而它们非常重要,第8章中讨论了这个话题。这一章中阐述的模式和策略显著地简化了向其他媒体写数据的过程。
最后一章通过介绍重构将所有一切包装起来。这一章的重点并不是介绍重构的所有方面,而是定义并阐述如何利用本书中定义的模式来重构现有的代码。...