前 言
缘起
用过去几年互联网上最酷,而在当下已经被用滥的名词来说,我在2004年成为了一名博客,用日志的方式记录自己成长的经历。坦白说,技术的成长远远比身体的发育更加地艰辛与缓慢,尤其是当今信息爆炸的年代,我们担忧的不是吃不饱,而是应该怎么吃,吃什么?营养不良固然令人堪忧,营养过剩却也不是健康之道。如果我们对软件技术做一次全方位的扫描,收获的无疑是面对岔路口的彷徨与迷茫,就像博尔赫斯笔下的曲径分岔的花园。
这是一种梦魇,就像在我的儿童时代,每次发高烧都会做的同一个恶梦一样,跑不掉,挣不脱,惊醒之后却又说不清的感觉。没人愿意走迷宫,除了那些以解谜题为乐趣的天才们。所以,我们在软件设计的迷宫门前停住了脚步;然后,四处顾盼寻找通过迷宫的地图。
不知道世界上是否真的存在穿过软件设计迷宫的地图,但至少有人曾经通过,并且留下了淡淡的足迹。这些足迹或者交互重叠,或者纷繁杂乱,分不清哪里才是走过的正确的路。于是,寻找、识别与尝试就成为我们面对技术更新的时候要经历的三部曲。
经典的技术,特别是经典的设计思想,完全可以免去这几个步骤。例如设计模式,在面向对象世界里,它已经成为了经典的存在,我们不必浪费时间去质疑它的重要性。省去了寻找、识别与尝试的过程,我们可以直接将它设定为亟待攻克的堡垒。正是基于这样的目标,我开始尝试与广大博友分享我的战斗攻略与心得。
博客的风格是“童言无忌”,所以我能够自由写意地耕耘博客园的一块田地。俗语云:种瓜得瓜,种豆得豆。我种下了技术的种子,吸收着评论的养料,最后收获的却是现在这本呈现在读者面前的《软件设计精要与模式》,连我自己也要感到莫名惊诧了。书的出版缘起偶然。在我做完了一个长达一年多的项目之后,又参加了另外一个大型项目最后阶段的开发与测试,最后拒绝了一个周期可能长达几年的项目安排,结束了在北京的漂泊回到故乡。我开始了悠闲自得的放假生涯。一次偶然与博客园站长杜勇先生的闲谈,结束了我的休假状态,开始了数月的写书生涯。对于杜勇先生,我想把感谢的话放在本文的末尾,此时只想表达我的“愤慨”,是你,谋杀了我的闲适生活☺。
好在我这本书成不了指引人们走出迷宫的地图,所以我可以“没有责任心”地回过头来欣赏自己在迷宫墙上的涂鸦,即使是一个人的艺术,对于自己而言,也是一种美。萨特说:“存在即合理”,我相信本书能够体现这种逻辑的合理性。
本书有些什么
既然本书的内容源自于博客的内容,就必然继承了一些散漫自由的风格。我甚至建议读者把书中各章看作是一篇篇技术随笔,因为我实在厌倦了那种捧着高文大册时的战战兢兢了。于是,我努力营造一种华丽的风格,以此来冲击那些习惯了枯燥与平实的读者口味。
散文的要旨是“形散而神不散”,本书基本符合散文的精神。虽然每一章都可以看作是一个独立的部分,然而其中蕴涵的思想却是一致的,因而又可以合为一个整体。如果观察整体的局部,我们又可以将其割裂开来,成为一个单独存在的系列。如此安排,固然与博客的风格有关,却能够兼得一个好处,就是读者可以有选择地根据自身情况进行阅读,而不必囿于整部书的约束。
全书围绕着软件设计的核心内容,结合大量的实例与代码,使其告别了纯理论研究的空泛,将软件设计理论与项目实践完美地结合起来。遵循这样的原则,是因为我们在设计现实的软件,而不是在乌托邦中空想。软件开发,根本就没有乌托邦的乐园。
根据表述内容的不同,全书以渐进但绝非进阶的形式分成了5大篇。
第1篇为“设计之要”,相当于本书的总纲,列举了软件设计的要素,包括设计模式、重构、测试驱动开发、极限编程等。从涵盖面来看,“设计之要”一篇包容了软件设计中最重要的“流行元素”,它们是程序员向设计师“涅”的基石,是从小工到专家的修炼法门。受篇幅所限,我在阐述这些重要的设计要素时,只能是浅尝辄止,颇有几分意犹未尽的感觉。然而,我并不希望自己的论述浮于表面,而是希望能够结合具体的实例,深入浅出地解析它们的本质与精神。我希望读者在阅读完这些章节之后,有一种饥饿的感觉,不停地叩问与质疑,然后再“上穷碧落下黄泉”地去搜寻相关的资料,以求获得更加深入的理解。
在第2篇“.NET Framework与设计模式”中,我偷懒地从.NET Framework的现有设计中寻找到有关设计模式的实践,包括Factory Method模式、Composite模式、Decorator模式、Iterator模式与Strategy模式。为了帮助读者更好地理解这些模式,在每一章中,我都采用了“抛砖引玉”的方式,首先分析相关模式的实质,并结合具体实例详解模式的目的与应用场景。最后,.NET Framework粉墨登场,尽显自己主角的魅力。由于.NET Framework是一个庞大的框架,包含的代码浩如烟海,不可能在短短的一章中穷尽其详细的设计。因而,我在引入.NET Framework实例时,力求简洁,删去了与文章主旨无关的代码,并辅以UML图体现设计思想。对于.NET Framework在实现相关模式时独具匠心的一面,我则不遗余力地给予详尽的介绍,力求深入透彻。
.NET Framework是一道主食大餐,虽然美味可口,却未免有几分油腻。所以,我在第3篇中又上了一道清炒时蔬,希望能够压一压油荤。“媒体播放器的设计之旅”一篇,引入了一个虚构的媒体播放器项目。之所以称为“设计之旅”,是因为媒体播放器的最后完善,确实是一段艰难的征程,设置重重路障的是为我们提供需求的客户。本篇的独有之处是各章既相互独立,又有着藕断丝连般的联系。虽然是项目实践,但无疑更带了几分对相关设计模式探讨与研究的意味。本篇论述的设计模式包括Factory Method模式、Adapter模式、Decorator模式与Visitor模式。
或许第4篇“设计模式应用实践”最能体现本书的价值。它仍然是设计模式项目实践的延续,凸现其价值的原因在于这些项目实践,均是我亲自参与设计与开发的项目。无疑,它们的实现更具有现实的指导意义。我常常在想,究竟是什么阻碍了程序员对设计模式的理解?他们的普遍意见是不患设计模式之艰深难懂,而患设计模式运用之困难重重。怎么将设计模式运用到实际的项目开发中,是他们面对的最大问题。所谓“读书百遍,其义自现”,我想,唯一的解决之道就是实践,实践,再实践。西方有谚语说“Practice make perfect”,翻译为中文就是“熟能生巧”。然而,盲目的实践终归是不成的,我们需要向他们展示一些已经实现了的示例。在对设计模式进行讲解时,我们固然需要示例的精巧、生动与形象,但最重要的特质还是要与实际的开发结合,否则,再贴切的示例都会成为“空中楼阁”。如果仅仅追求比喻的“一鸣惊人”,或许能够加深程序员对设计模式的理解,但对于如何在项目开发中实际运用它们,仍然是一片茫然。本篇论述的设计模式包括Abstract Factory模式、Builder模式、Command模式、Chain Of Responsibility模式、Observer模式、Proxy模式、Strategy模式、Template Method模式以及Bridge模式。
第5篇“.NET体系架构设计”不再围绕设计模式,而是以更高的层次来叙述体系架构设计的诸多模式与原则。有关架构设计的分析最容易流于空泛,而本篇则以PetShop电子商务系统作为体系架构分析的示例,使得内容能够秉承本书一直坚持的项目实践准则。囿于本人所识,本篇无法对体系架构设计的方方面面做出全局的描述,主要介绍了分层式架构设计思想,并论述了相关模式的运用。其中,论述的设计模式包括Abstract Factory模式、Strategy模式、Facade模式、Proxy模式;企业应用架构模式中的Data Mapper模式、Domain Model模式、Page Controller模式以及MVC模式。
本书面对的读者
本书并非一本软件设计的入门指导书,对于程序设计的初学者,本书的内容未免艰深了些。虽然我一直力求讲解浅显明白,试图以最简单的话与最清晰的逻辑阐述设计思想,并佐以丰富的实例展现设计的现实一面;然而,内容的自身特质决定了它不可能成为“从入门到精通”的大百科式全书。
对于一名即将迈入软件设计门槛,或者有强烈渴求希望提高自身设计能力的程序员而言,本书或许能够满足你的这种诉求。假定你已经具备了一定的面向对象设计思想,且具备设计模式的基本知识,那么本书就不会拒你于千里之外了。如果你已经成为了一名合格的软件设计师,那么本书能够带给你的价值,更多的是参考与借鉴的意义。向优秀的软件设计师进发,是一条荆棘之路,本书或许能够磨利你的刀刃,助你披荆斩棘。
王国维在《人间词话》一书中写到:古今之成大事业大学问者,必经过三种境界。“昨夜西风凋碧树,独上高楼,望尽天涯路。”此第一境界也。“衣带渐宽终不悔,为伊消得人憔悴。”此第二境界也。“众里寻他千百度,蓦然回首,那人却在灯火阑珊处。”此第三境界也。当你正在“独上高楼,望尽天涯路”的时候,本书或许能够帮助你迈入“衣带渐宽终不悔”的境界。
本书的示例代码都以C#语言完成。然而本书并非程序语言的教学书,它关注的核心是软件设计思想,因此,略去语法本身所带来的不快,对于.NET平台下的其他语言编程人员,依然具有一定价值。本书关注于.NET平台技术,但并不排斥Java世界。实际上,除去.NET独有的技术以及运行环境,两者之间在软件设计思想上并没有什么不同。
阅读前的准备
本书提供了大量的示例。为了更好地理解本书,阅读代码并运行这些示例是非常必要的。因此,你需要准备好正确的软件环境。首先是.NET Framework 2.0及Visual Studio 2005。由于部分示例采用了测试驱动开发的模式,引入了单元测试代码,所以你需要下载并安装NUnit 2.0,下载地址是http://www.nunit.org。
本书第5篇以PetShop 4.0作为体系架构设计的分析案例,因此,你有必要安装PetShop 4.0版本,下载地址为http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnbda/ html/bdasamppet4.asp。此外,你还需要安装SQL Server或者Oracle,因为PetShop需要访问数据库。实际上,数据库的设计也是软件体系架构设计中的重要一环。
如何阅读本书
你可以顺序地阅读本书,虽然“设计之要”一篇涵盖的内容可能会制造一些阅读上的困难,但它可以帮助你确立软件设计的基本思想与原则。然而,鉴于书中各章近似于“松散耦合”的关系,你也可以根据阅读兴趣选择相关的章节。事实上,本书并没有强迫性的阅读定式,你甚至可以像阅读博客文章或者技术随笔那样,带着轻松的心情浏览全书。
如果你只对设计模式感兴趣,那么我希望你首先阅读第2章,它会告诉你设计模式最核心的思想——“封装变化”。几乎可以这样说,全书中有关设计模式的章节,都是围绕着“封装变化”的思想来阐述与实现的。
如果你对面向对象设计思想还有些许疑虑,我建议你首先阅读第11章,然后,跟随着媒体播放器的设计之旅,到达你期望的设计目标。如果你受困于设计模式的项目运用,那么直接阅读第2篇与第4篇,会是一个不错的选择。如果你只是希望深入了解某一种设计模式,那么就看看目录吧,然后根据章节名直奔主题,或许可以为你节约宝贵的时间。
关于重构、测试驱动开发与极限编程,你可以在第1篇中找到对应的章节。但如果希望全面地了解它们,那么还是抛开本书吧,它并不是你真正想要的。但如果你愿意认真地阅读第1章,或许它可以为你部分地解决有关软件设计的疑惑。
如果你希望了解体系架构设计方面的内容,毋庸置疑,第5篇才是你需要关注的内容。但如果你希望扎实地走好软件设计之路,千万不要好高骛远,还是从第1章开始阅读吧。
致谢
除了被致谢的人之外,这一节内容往往被读者忽略,以至于漠视了所有支持我、帮助我的家人与朋友。所以,我希望将这一部分变得更有价值一些。为了不背离本书的核心精神,我引入设计的原则来阐述。
致谢的方式有很多种,例如口头致谢,为对方献上鲜花或者赠送礼金,这相当于一种致谢策略。从软件设计的角度来看,我们可以定义如下接口。
public interface IAcknowledge
{
void Acknowledge();
}
如果要实现口头致谢,可以定义如下的类,并实现IAcknowledge接口。
public class Appreciation:IAcknowledge
{
public void Acknowledge()
{
Console.WriteLine("Thank you!");
}
}
献花与赠送礼金的实现则如下所示。
public class PresentFlowers:IAcknowledge
{
public void Acknowledge()
{
Console.WriteLine("To present flowers.");
}
}
public class Reward:IAcknowledge
{
public void Acknowledge()
{
Console.WriteLine("Reward payment.");
}
}
以上是典型的Strategy模式的实现,我们可以在文本文件中列出需要致谢的人,并给出致谢方式所对应的程序集名与具体类名。在读取了文本文件中的每一行值后,利用反射技术创建实现了IAcknowledge接口的具体类对象,然后调用Acknowledge方法,向对方致以我最诚挚的谢意。
然而,致谢并不仅仅是单纯的某一种方式,我可能希望向妻子口头表示感谢的同时,还想献上一束新采的鲜花。此时,Decorator模式就可以派上大用场。如何实现呢?我们可以在阅读了第8章与第13章后,再回过头来思考这个命题。
用生活的例子来阐述软件设计的原理,是吕震宇先生给予我的启迪。他的设计模式随笔系列向我展示了另外一种叙述的魅力。在我撰写本书期间,每当我的设计思路出现了纠缠不清的困惑,我会尝试去阅读他的文章,希望能得到一些启示。我必须感谢徐宁(idior)先生,他对于问题的探讨总有一种近乎于偏执的狂热,在与他的讨论中,我受益良多。卿子成先生曾经为本书提供了一些项目实例,虽然最后因为种种原因没有能够引入,使得本书失色不少,但他给予的帮助使我一直心怀感激。
最不能忽略的一个重要人物是杜勇先生。可以说,没有杜勇先生,这本书根本就没有机会能够面世。最重要的是,在博客园安家的这几年时间,使我有机会结识更多在技术上志同道合的朋友。博客园促进了我技术的成长,作为博客园站长的杜勇先生居功至伟。感谢你给我以及数万博客园居民带来的免费的优质服务。
必须感谢博客园出书团队成员,李会军(Terry Lee)、徐宁(idior)、Dflying Chen、Allen Lee……你们的热情给了我极大的鼓舞。还要感谢双鱼座,你对本书的书名提出了中肯的意见。感谢Anders小明,使我确定了本书的书名。特别感谢许多一直关注本书进展的网友们,你们的褒奖我愧不敢当,你们的批评我虚心接受。
一本书的出版,绝不仅仅是作者付出辛苦的劳动就能实现的,幕后工作者的名字不应该被埋没。感谢博文视点的编辑胡辛征、葛娜以及为本书顺利出版做过贡献的人。
我想借这个机会感谢我的家人。父母生我养我,您们的恩情我即使是衔草结环也难以回报。您们对我无微不至的关心与照顾,使得我能够心无旁骛,专心写作。特别要感谢我的父亲,如果没有您每日的催促,本书不可能如期完稿。感谢我新婚的妻子漆茜,在我忙碌于写书的时候,总是忽略了你默默在我身边。即使是在阳光明媚的秋日,我也无法陪伴你去南山郊游。但你没有丝毫怨言,反而常常在我夜深写稿的时候,悄悄地给我端上一杯温暖的茶水。家人的关怀始终是这么温暖,我希望能将本书献给我亲爱的家人!
张逸
2007年1月