介绍
???? 本书是关于设计的,多年来我一直从事这项工作。基本上说,从我第一次试着阅读“设计模式”(Gamma, Helm,Johnson & Vlissides, Addison-Wesley, 1995,通常被称作“四人帮“或者GOF)这本书开始。
???? 在Thinking in C++第一版里有一章是专门讲设计模式的,后来我把这一章放到了Thinking in C++第二版的卷二。而且在Thinking in Java 的第一版里你也能找到一章是关于模式的(第二版里我把它拿掉了,因为这本书篇幅太长了,更主要的原因是我打算写现在这本书)。
???? 本书不是一本介绍性的书籍。我假定你在阅读本书之前通读过Thinking in Java 或者与之相当的其它教材。此外,我还假定你对Java的语法有一定程度的了解。你应该对“对象”(object)及其内涵有深刻的理解,包括多态。此外,这些东西在Thinking in Java里都有讲解。
???? 从另一方面讲,阅读本书的过程中你可以通过研究“对象”在不同情形下的应用学到很多关于面向对象编程的知识。如果你对“对象”只有一些初步的认识,在理解本书讲到的这些设计方法的过程中,你的这些认识会不断加深。
?
Y2K 综合症
???? 本书的副标题是“解决问题的技术”,所以在这里有必要提一下编程领域里的一个大陷阱:过早优化。每次我提出这个看法的时候,大家实际上都是同意的。与此同时,每个人似乎都在心里保留自己的special case “只是我这次碰巧遇到的是一个特例”。
电脑对很多人来说是神秘的。所以当有人宣称那些愚蠢的程序员忘了使用足够长的数字来保存1999年以后的日期的时候,一下子大家都变成电脑专家了――“这些东西本来一点都不难,如果我早注意到这个浅显的问题的话。”拿我自己来说吧,我的背景最初是计算机工程,一开始我是搞嵌入式系统编程的。因此,我知道许多嵌入市系统根本没有日期或者时间的概念,即便是有,它们也不会被用于任何重要的运算场合。我被明确告知所有的嵌入式系统都会在2000年一月一号荡机。但是据我所知,那天只有那些预言灾难必将降临的家伙们的大脑内存丢失了――似乎他们自己从来就没有说过那些话。
???? 我的意思是说,我们非常容易陷入一种思维定式,即认为我们部分或完全理解的某一算法或者代码段会成为整个系统的瓶颈,而(做出这种判断)仅仅因为我们通过对这部分已知代码段的想象就认定它肯定比我们不知道的那些代码段效率低。但是除非你作过实际测试,通常是profiler?,否则你不可能真正知道实际的运行情况。即使你已经知道某个代码段效率非常低,但是别忘了大部分程序90%的时间运行的都只是占整个程序10%不到的代码。所以除非你认为效率低下的这部分代码属于那10%,否则对它们的优化是无关紧要的。
???? “97%的情况下我们不需要考虑细微的运行效率问题,“过早优化”是所有麻烦的根源”-Donald Kruth
上下文和组合 Context and composition
???? 你将会看到,设计模式文献里用的最多的一个术语就是“上下文”(context)。实际上,“设计模式”通常的一个定义就是“针对某个上下文特定问题的解决方案”。GOF 总结的这些模式经常会包含一个“上下文对象”,使用这些模式的程序员直接与这个上下文对象打交道。我曾经一度觉得这些对象似乎在许多模式中居于主导地位,于是我就想,它们到底是用来干吗的。
“上下文对象”经常充当“外观(facade)”的角色,用以掩盖模式其它部分的复杂性。此外,它还经常是控制整个模式运转的控制器。最初,我以为这些东西对于实现,运用和理解模式并不是十分重要的,但是,我记得GOF书里有一句话给人印象很深:“组合优于继承(prefer composition to inheritance).”上下文对象使你可以在组合(composition)里使用模式。或许,这才是它最大的好处。
?
关于“已检测异常”checked exceptions
???? 1)?异常的价值在于它统一的错误报告机制:这是一种标准的错误报告机制,而不是像C里面的那些非强制性的大杂烩似的错误报告方法。(而C++只是把异常加入C的混合错误报告机制,而没有使异常成为唯一的错误报告方法)Java相对与C++而言有一个巨大的优势就是, 异常是报告错误的唯一方法。
???? 2)?上一段的“非强制性”这个词是说另外一个问题。从理论上说,如果编译器强制程序员处理或者按照规范传递异常,那么,程序员的注意力总是会被带到可能出错的地方,进而用适当的方法来处理这些错误。我认为,问题是这只是一个未被验证过的假设,而我们是把自己当成语言的设计者凭主管臆断来做这个假设的。我的理论是,当有人想做一件事情,而你却不停的用令人厌烦的手段催促他,他就会用取巧的方法掩盖那些令人厌烦的东西,天知道以后他们会不会回头重新拿掉那些取巧的东西。我发现我在Thinking in Java第一版里就是这么干的。
???? ...
???? } catch (SomeKindOfException e) {}
????? (我写了上面那些代码,)然后多多少少就把它忘掉了,直到重写的时候才想起来。有多少人会认为这是一个好例子而效仿它呢?Martin Fowler 也注意到了这个问题,他发现人们屏蔽掉这些异常后就再也不管了。Checked exceptions 所带来的负面问题使它背离了设计它的初衷,当你自己试验的时候你就会发现这些问题。(现在我相信checked exceptions 是个试验性的东西,某些人认为它是个好主意,直到不久以前我还相信它确实是个不错的想法)。
???? 当我开始使用Python以后,所有的异常都会显现,没有一个会突然消失。你可以扑获你所关心的异常,但是你不会被强制要求写一大坨代码仅仅为了让异常能够传递下去。它们(异常)会在你想要扑获他们的地方出现。如果碰到最糟糕的情况,你忘了扑获异常,他们会暂时躲起来(以后会提示你)而不会彻底消失。现在我相信 Checked exceptions 是在鼓励人们让异常彻底消失。而且它使得代码可读性更差。
???? 最后,我想我们必须意识到异常本身就是试验性的东西,在假定所有Java的异常机制都是真确之前,我们必须更谨慎的审视它。我相信,只采用一种统一的错误处理机制是非常棒的,我还相信通过采用一个分离的通道(异常处理机制)来传递异常也是好的。我记得最初关于C++的异常处理机制的一个主张,就是它可以使程序员把工作代码和错误处理代码区分开来。但是,我觉得checked exceptions似乎没有这么做;恰恰相反,它总是想往你的工作代码里硬塞一些东西,这不能不说是一个倒退。我使用Python的经验更加坚定了我的这个看法,在必须要面对这个问题的时候,我倾向于在我的Java代码里用更多的Runtime Exceptions (而不是checked exceptions)。