分类:
2009-11-22 14:59:54
摘要
在开发者中间,设计模式是思考设计问题的很受欢迎的一种方法,但是怎样才是思考设计模式的正确方法呢?在这次采访中,Erich Gamma(里程碑式的书籍《设计模式》的作者之一)和Bill Venners谈论了关于如何思考和使用设计模式的正确方法。
Erich Gamma是在1995年作为畅销书籍《设计模式:可复用面向对象软件的基础》 (Addison-Wesley, 1995) [see Resources] 的合著者而跃上软件业界舞台的。这项具有里程碑意义的工作,经常被援引为四人帮(GoF)的书,该书针对通常的设计问题分类整理出了23种特定的解决方案。1998年,他和Kent Beck组成团队开发JUnit[see Resources],这成为Java社区事实上的单元测试工具。Gamma现在是IBM的一名杰出工程师,他在位于瑞士苏黎世的IBM Object Technology International(OTI)实验室工作。他担任Eclipse社区的领导工作,负责Eclipse平台[see Resources]上与Java开发相关的事务。
2004年10月27号,Bill Venners 在加拿大温哥华举行的OOPSLA会议上遇到了Erich Gamma。在这次采访中(这次采访的内容将分多次在Artima Developer的Leading-Edge Java频道刊登出来),Gamma讲述了软件设计中深层次的东西。在这个系列的第一篇文章中,Gamma给出了他对于如何正确思考和使用设计模式的看法,并且描述了不同模式库之间的差异,比如GoF和Alexandrian的模式语言。
设计模式的真正价值
Bill Venners: Bruce Eckel 和我在教授设计模式课程,我们发现大家非常想了解四人帮(GoF)总结的那些模式。模式让讲座买的更火。围绕设计模式有很多关于市场的噱头可做。
Erich Gamma: 现在仍然这样么,已经过了10年了?
Bill Venners: 是的。大家想了解设计模式,而且我怀疑这很大程度上是因为“模式”仍然是一个时髦的词汇。我想绕过那些噱头问问你人们到底该如何对待设计模式。他们对于设计模式的态度应该是个什么样子?人们如何才能使用模式把工作做的更出色?它真正的价值到底是什么?
Erich Gamma: 我想模式作为一个整体可以帮助人们学习面向对象的思想:你如何才能用好多态(polymorphism),设计好组合(composition)和委托(delegation),平衡职责,并且提供可插拔的行为(pluggable behavior)。相对于把对象应用到绘画图形(graphical shape)的例子(有一个关于图形的类层次和一些多态的draw方法),模式要更深入一些。当你理解了模式以后,你就真正学会了多态。所以通常来说模式对于学习OO和设计是有好处的。
在此基础上,每个单独的模式都有不同的特性可以在某些地方为你提供帮助,比如你需要更大的灵活性或者需要封装一个抽象或者需要让你的代码耦合的更松散的时候。这在大系统里确实是个大问题。如何保持你的分层?如何避免向上的调用(up calls)或者循环依赖?GoF模式几乎没有提供什么工具帮助你解决这些问题。他们提供的帮助不是给你一个完美的解决方案而是解释如何权衡利弊。尽管模式是从具体应用中抽象出来的,他们仍然提供给你关于实现的有价值的线索。以我的观点来看,正是因为模式都是可实现的才使它们如此有价值。
模式都是从牛人的经验里提取出来的。它们使你可以重复别人做过的成功的设计。这样一来你就可以站在牛人的肩膀上,用不着重新发明轮子了。但是,因为模式可以有许多实现上的变体,所以你仍然需要保持清醒的头脑。最后,由于模式提供给你用于设计的积木块的名称,所以它们也提供给你用于描述和讨论特定设计的一个词汇表。
另一个问题是我们该如何教授模式。我并不确切知道你该如何做,但是我想你不该做的一件事是用一个类来列举23种模式。这么做并不能带来任何好处。你必须得体会到一个有问题的设计所带来的痛苦。我猜想只有感受到这种设计上的痛苦你才会认识到某个模式的重要性。
Bill Venners: 什么痛苦?
Erich Gamma: 比如说意识到你的设计不够灵活,一个小小的改动会波及到整个系统,你必须复制代码,或者代码变的越来越复杂。如果你在这样一个棘手的情况下应用某个模式,有可能会使这种痛苦消失并且后来还很舒服。当你意识到,哦,实际上这个模式,工厂模式(factory)或者策略模式(strategy),是解决我的问题的方法,你会有一种恍然大悟的感觉。我想这才是教授设计模式真正有趣的方法。
我一开始教授设计模式的时候是非常单调乏味的,因为我只是列举那些模式。我发现试着通过实际例子激发如何应用模式会有趣的多。换句话说,你确实需要在现实背景中展示一个实际的例子——炮制出来的例子不管用。在OOPSLA的会议上我收到一本《Heads First Design Patterns》[see Resources]。这是一本很棒的书,不仅仅是因为它读起来很有趣,而且还因为它们能够通过一种类似于小说的高度可视化的方法来传达设计模式的本质。
Bill Venners: 那么,是不是说模式的价值就是,当我在现实世界中感受到某种特定的痛苦,我可以求助于某个已知的解决方案?
Erich Gamma: 毫无疑问,这是我推荐大家使用模式的方法。不要一开始就马上把模式套进某个设计,而是当你一边深入并且对问题理解更多的时候才使用它们。因为这个原因,我更愿意在犯错误之后使用模式,朝着模式的方向重构(refactoring to patterns)。在模式刚刚开始变的更加流行的时候,我在一个新闻组里看到一条评论说,有人声称他们要在一个特定的程序里尝试使用所有23种GoF模式。他们说他们失败了,因为只能用到20种。他们希望客户会再叫他们回去,这样他们可能就可以把剩下3种也加进去。
试图使用所有的模式是不好的做法,因为你最终得到的是人工臆想出来的设计——过于深思熟虑的设计,有灵活性但却没有人需要用到。现今的软件都太复杂了。我们没功夫推测它额外要做的事情。我们需要真正聚焦在它必需做的事情上。这就是为什么我喜欢朝着模式的方向重构。人们应该学会在碰到某个特定类型的问题或者代码味道(现在大家都这么说)的时候,去他们的模式工具箱里寻找一个解决方案。
Bill Venners: 真滑稽,因为我的第二个问题是,我发现人们经常觉得使用模式最多的设计是最好的设计。在我们讲授设计的讲座上,我让参与者做一个设计项目,在讲座结束的时候大家要把自己的设计讲给别人听。几乎毫无例外,那些讲述的人总是想炫耀他们在自己的设计里使用了多少模式,尽管我试图告诉他们目标是要一个干净的,易于理解的API,而不是要他们赢得一个“看谁模式用的多”的比赛。我刚刚听你说了同样的事情,也就是说,这不是思考模式的正确方法。如果这种方法不对,那么在设计里使用模式的正当理由是什么呢?
Erich Gamma: 很多模式是关于扩展性和重用性的。当你确实需要扩展性的时候,模式提供给你某种方法让你实现它,而且这很酷。但是当不需要它的时候,你应该让你的设计保持简单并且不要添加不需要的抽象层。我们Eclipse的一条原则就是,我们只在要紧的地方需要扩展性。实际上,如果你对我们在Eclipse中是如何使用模式感兴趣的话,我曾经尝试把模式的应用提取出来放到《Contributing to Eclipse》[see Resources]这本书中的一个章节里。在这一章里,我使用设计模式解释了Eclipse架构的一些片断。
Bill Venners: 说到扩展性,您是什么意思呢?
Erich Gamma: 就是说你可以定制行为,而不用触及已经存在的代码——这是OO里面很经典的一个主题。你可以重用针对特定问题调整过的某些东西。
被设计模式所包围的核心抽象
Bill Venners: 在您与Kent Beck合写的一篇叫做“JUnit: A Cook's Tour” [see Resources] 的文章中,你引领读者沿着JUnit的设计路线,如您自己所写的,“从零开始一个接一个的应用模式,直到得到系统的架构”。我想这种方法可能是受了Christopher Alexander的启发,他在建筑学上有关模式的工作激发了软件模式的运动。您觉得把某个模式放置在另外一个模式的层次之上,直到最后完成设计,这是一种有效的设计方法么?
Erich Gamma: “The Cook's tour”那篇文章多少有点人为炮制的痕迹。我们修整了JUnit原先的设计。然而,我们并不是以这么一种模式驱动的方法开发JUnit的,事实上,我们是严格按照测试驱动的方法来开发的。对于JUnit来说,它确实有一个针对测试的核心抽象,围绕着这个核心抽象你会看到其它几个设计要点显现出来,这几个设计要点又依次被模式实例具体化。这些东西是你在成熟设计里经常能看到的。有一些关键抽象,你会经常把他们看作一个设计的中心,围绕这些关键抽象你希望完成各种各样的事情。于是你就会看到模式从这样一个中心不断长出来。但是我不会把这个作为评判(软件)质量的标准。
Bill Venners: 这就是当您说“模式密度(pattern density)”的时候所指的东西么?
Erich Gamma: 是的,确实如此,模式围绕着某些中心抽象浮现出来的。
Bill Venners: 您说过测试用例(TestCase)是JUnit的核心抽象。
Erich Gamma: 准确的说是由测试用例实现的测试接口,但是我们确实是从测试用例开始并且以它为基础扩展的。
Bill Venners: 那么,您能给密度(density)下个定义么? 是指围绕它的模式的个数么?您说过“JUnit Cook's Tour”那篇文章多少有点人为炮制的意思。
Erich Gamma: 炮制在某种意义上是说“the cooks tour”那篇文章是去掉在我们的测试驱动开发过程中所产生的所有测试活动所剩下的东西。所以它是一个高度压缩的陈述。密度从围绕Test固定下来的那些模式中显现出来。
设计JUnit的时候,我们不是仅仅把模式捆绑在一起。我们用测试驱动来做,从一个我们想要成功完成的测试开始,一旦它通过,我们会仔细看看如何能够改善那些代码。用测试驱动的方法开发一个测试框架并不是说没有它的挑战性,但是一旦基本的东西跑起来以后,会变得出奇的顺利。你知道,Kent和我在开发JUnit的时候对模式非常熟练。所以很自然,我们会说诸如,“嘿,那是一个嵌套模式(composite)。” Composite是JUnit用到的一个模式。我们还使用了模板方法(template method)。这是很基本的一个模式。我们用过Command模式。这当然是主要的一个。我们从测试开始并且说,“哦,这是一个command。哦,这是一个template。” 因为我们对模式都很熟练,我们的交谈进行的非常的快,这使我们可以以高速度完成一个设计。
这实际上很好的说明了模式是如何为我们提供设计语汇的。当你看一个UML图的时候,情况也是类似的,你会看到方形和箭头,但他们没有真正告诉你在这些关系后面的含义。但是一旦你了解到那个模式,它就为你解释了这些关系是到底是干吗的。如果它是个观察者模式(observer),那在两个类之间为什么会有一个连线就很清楚了。那是因为一个家伙要观察另外一个家伙。你确切知道所发生的是什么。我猜这就是关键点。模式给了我们一种用于讨论设计的语言。实际上,JUnit之旅并没有结束,Kent和我现在还在忙于JUnit 4。我们仍然通过测试驱动的方法不断完善JUnit。我们有个想法就是要降低入门的门槛。为了达到这个目的,我们正在采用J2SE 5中的某些功能,比如annotations,把JUnit弄得更加易于使用。
模式语言
Bill Venners: 就Alexandrian的本意来说,什么是模式语言?
Erich Gamma: Alexander有一个非常远大的目标, 他想创造出可以提升生活质量的建筑。为了完成这个目标,Alexander发展出了模式语言。这是一系列互相构建于彼此之上的模式。模式语言指导设计者把单个的模式应用到整个设计。我们开始弄设计模式的时候并没有那么远大的理想。我们使用一种以微观架构(micro-architectures)为基础的,更倾向于自底向上的方法。
Bill Venners: 您说的自底向上是什么意思?
Erich Gamma: 让我往回一点说说我是如何进入模式领域的。我想这可以回答你的问题。我曾经和Andre Weinand一道开发ET++,这是一个庞大的C++类库和框架。当我深入思考ET++的时候,一个成熟的框架显现了出来,它包括一些反复出现的设计结构,这些设计结构能够给你很多特性,诸如扩展性,解藕,还有很重要的是——优雅。这些结构可以被认为是为整个系统架构做出贡献的微观架构。最后我在自己的论文里以书面形式整理了十几个这种微观架构。这种针对模式的方法有别于模式语言:它并不是以自顶向下的方法弄出一系列相互交织的模式,微观架构更倾向于是相互独立而最终又以自底向上的方式相互关联的模式。模式语言为你提供的指导贯穿整个设计,而我们有的只是这些碎片式的,零星的工程学知识。我承认,这种做法目标不够远大,但是它仍然很重要并且很有用。
Bill Venners: 模式语言是不是就像持有一个上下文无关的语法,你可以用它来产生一大堆程序?
Erich Gamma: 从抽象的层次上来说,它们之间有某些相似性。用一个语法可以定义一大堆程序,同样的,模式语言可以产生一堆解决方案。Christopher Alexander是这么描述的,他说他的模式描述了某种解决方案,所以它就可以被多次应用而没必要每次都保持不变。但是我认为到这个层次共同性就消失了。
Bill Venners: 说到产生,他(Alexander)是什么意思? 如果我有一个上下文无关的语法,它并不能产生程序。我仍然得把它们写出来。
Erich Gamma: 你仍然需要做决定,但是模式语言提供给你更多的指导而且它有一定的流程。比方说你想设计一个自己住着舒服的房间。他(Alexander)说,先在两边都放上灯。好,现在你在两边都放上了灯,下一步做什么?你如何安放窗户?针对这个问题会有其它的模式描述一个解决方案。他基本上指导你布置整个空间。这种联系正是一个模式库(比如我们在GoF那本书里描述的那些)和模式语言的区别所在。我们发现,这些微观架构实际上也不是孤岛。它们之间也发生关系。我们在那本书的封面的内页画出了它们之间的关系,而且这是Alexander的拥护者认为我们这本书唯一有价值的地方。
Bill Venners: 听起来几乎像是一个设计的方法论。你沿着这条路,一步步做下去,最后得到可以坐在里面的,漂亮而又舒服的房间。
Erich Gamma: 是的,当你遵循Alexander的模式方法的时候,你就是在按照一定顺序使用这些模式。我们并没有限制一个特定的顺序。如果你有问题,我们有相应的解决方案,但是我们没有下个步骤。我们不会给你暗示说下一步该干什么。就这种意义来说,Alexander的方法是更彻底的。JUnit采用了一点这种模式语言的方法,因为它可以帮你写一个测试用例。在JUnit的文档里,Kent和我写了一个关于如何实现一个测试的迷你型的模式语言。以一个测试作为开始,接下来你希望提取出公共的初始化代码,然后你会希望给测试分组,等等。
重用的重要性
Bill Venners: GoF那本书的第一个句子是,“设计面向对象的软件是困难的,设计可重用的面向对象的软件更为困难。”重用到底有多重要?
Erich Gamma: 今天已经没有人会发布程序而不重复使用系统级的类库。我们的环境太复杂了,不使用它们就没法构建程序。很显然,重用是很重要的,大家就是这么做的。有个有趣的问题是,是否存在比系统级的类库更大规模的重用。多年前,当我们开始研究框架的时候,我们对此给予厚望。我们以为生产软件的方法应该是构建高层次的(high-level),专注于特定领域的框架,然后你只是定制它们并且重用所有已经被它们系统化的设计。这是重用之涅盘(nirvana)。从那时开始,我变得更加实际了,因为我认识到创建高度可重用的框架是很难的。它们会变得复杂,难以学习,而且更加难以维护。我曾经是框架的使用者,也同时是框架的创建者,从任一角度看这都很难。
框架开发所面临的一个重要挑战是如何随着时间的推移保持稳定性。一个框架走的越远,你就越发明白一开始你该如何构建它。于是,你会试着调整和改善它。然而,因为你的框架已经被大量使用,所以你所能的改变的东西就受到很大的限制。从这一点来说,至关重要的是,拥有定义完善的(well defined)API并且让用户知道什么是已发布的API,什么是内部代码。对于已发布的API你应该严格遵从稳定性,而对于内部代码你拥有改动它们的自由。
关于我在工作中是如何看待重用的,Eclipse就是一个好例子。它是由我们称之为插件(plug-in)的一堆组件搭建起来的。插件把你的代码扎成捆,还有一个清单,你可以通过它定义你扩展了哪些其它插件以及你的插件提供哪些扩展点(extension points)。插件遵循明确的约定提供可重用的代码把API和内部代码分离开来。Eclipse的组件模型是简单并且牢靠的。它有以下核心特征。Eclipse有一个小的内核,所有的事情都是通过扩展点以同样的方法完成的。组件模型和聚焦于API的这种结合是Eclipse的主要组成部分之一。受控的扩展性(controlled extensibility)是另一个重要的东西。
Bill Venners: 受控的扩展性是什么意思?
Erich Gamma: 人们告诉我们关于面向对象的第一件事情就是:“OO是很酷的方法。你可以子类化(subclass)并且定制任何东西!” 现在我认为这是对象裸露癖者的态度。你可以这么做然后暴露所有东西,而且别人也能改动任何东西。最初的Smalltalk就有点这种味道。但当下个版本出来的时候,问题就来了。如果你暴露了所有东西,你就不能够改动任何东西,否则你就打断了所有的使用者。这就是为什么在谈到重用的时候,组件模型,API聚焦以及设计好内部的和需要发布的东西,变得如此至关重要。当研究Eclipse API的时候你还会发现我们更进了一步,我们并不是仅仅指定哪些类是用于发布的API。Eclipse API还会指出某个类是否打算被子类化。这里有一个重要的教训就是,API并不仅仅是一个文档化的类。而且, API 并不是自然而然产生的; 它们需要大量的投资。
关于我们所谓受控的扩展性在Eclipse插件这个上下文中,还有另外一个重要的方面就是,扩展和扩展点。扩展点定义了你可以在哪里促成(contribute to)以及扩展某个插件。扩展点有一个名字,并且伴有一个说明,这个说明定义了当你做出贡献的时候必须遵循的接口。作为插件作者,直到考虑了你的插件可能提供的扩展点,你的工作才能算完成。
Bill Venners: 请给框架下个定义。
Erich Gamma: 我认为有三个层次上的重用。在最低层次上,你对类进行重用:类库,容器,可能还有一些类似于容器/迭代器的类“团伙”。框架属于最高的层次。它们确实是试图提取设计的精华。它们确定出用以解决某个问题的关键抽象。它们用类来表示这些关键抽象并且定义它们之间的关系。比如说,JUnit就是一个小型框架。它是框架中的“Hello, world”。我们定义了Test,TestCase,TestSuite以及它们之间的关系。典型地,框架比一个单独的类具有更大的粒度。此外,你通过在某个地方使用子类化来以挂钩的方式进入(hook into)框架。它们使用所谓的好莱坞原则“不要找我们,我们会找你。” 框架允许你定义个性化的行为,当轮到你来干些事情的时候它们会叫你。JUnit也同样如此,对么?当它想要为你执行一个测试的时候它会叫你,但是剩下的就在框架内完成了。
Bill Venners: 您说过有三种层次上的重用?
Erich Gamma: 是的,还有中间一个层次。设计模式比框架小而且抽象性更强。它们实际上是针对几个类如何能够彼此联系和相互作用的一个描述。当你从类到模式,最后到框架的时候,重用的层次就提升了。
这个中间层次很好的一点是,模式以一种比框架风险性更小的方式提供重用。构建一个框架风险很高,而且是一个很大的投资。模式允许你独立于具体代码,重用设计思想和概念。
推测(speculating)的风险性
Bill Venners: GoF那本书说,“对于最大限度的重用来说,关键在于预见到新的需求和现有需求的变更,还在于设计你的系统让它们能够相应的演化。为了设计一个针对这些变更足够健壮的系统,你必须考虑这个系统在它的生命周期中可能会需要的变动。一个没有把变化考虑在内的设计要冒着将来主要部分重新设计的风险。” 这种说法看起来和XP的理念是矛盾的。
Erich Gamma: 它确实和XP是矛盾的。它说的是要你未雨绸缪。你应该推测。你应该推测灵活性。好吧,是的,我也成熟了,而且XP提醒我们推测灵活性是划不来的,所以我可能不再会完全以这种方式书写这个问题。要添加灵活性,你必须得能够通过某个需求来证明它是正当的。如果你眼下没有这个需求,那么我不会在现在的系统里为了灵活性加入一个钩子(hook)。
但是我不认为XP和模式是冲突的。看你怎么使用模式了。XP的那些家伙们把模式放入他们的工具箱,他们只是在需要灵活性的时候朝着模式的方向重构。而我们10年前在那本书里说的是,不,你也可以推测。开始你的设计然后马上就使用它们。在最初的设计里你就使用模式,但是XP的那些家伙们不是这么做的。
Bill Venners: 如果他们不使用模式,那么XP的那些人一开始干吗?只是写代码么?
Erich Gamma: 他们写测试。
Bill Venners: 是这样,他们把测试写成代码。然后当要实现的时候,他们只是实现那些代码,让测试能够通过。接下来当他们回头再看的时候,他们重构,而且可能会实现某个模式?
Erich Gamma: 或者是当有新的需求的时候。我真正喜欢的是由需求驱动的灵活性。我们在Eclipse也是这么做的。如果需要暴露更多的API,我们在有这个要求的时候及时完成。我们是逐步的暴露API。当用户告诉我们,“哦,我必须得使用或者复制所有这些内部类。我真不愿意这么干,” 我们看到有这种需要,然后我们说,好的,我们会花时间把它发布成一个API,然后把这个当成一个承诺。所以实际上我以更小步骤考虑这个问题,我们不想在一个API被需要之前忙于把它弄出来。
Bill Venners: 但是,会存在推测出来的需求。所以如果你说你会一直等待,直到有需求出现,这难道不是在转移问题吗?因为有人可能会说,“哦,我们接下来会需要这个。” 他们说这是一个需求。但是另外有人可能会说,“噢,我们真的现在就需要这个吗?” 我不是很确定自己是否理解了什么时候该推测什么时候不该推测。谁来决定一个需求什么时候是真正的需求?作为一个程序员我该如何决断?
Erich Gamma: 嗯,你必须得有针对你生产出来的东西的客户或者消费者。对于Eclipse来说,我们的客户是写插件的这个社区。所以我们和他们交互,而且经常和他们保持同步,根据我们所知道的自己的具体应用,(推测)他们可能想要用的东西。因此我对程序员的建议是,当你必须要推测的时候,一定至少要让你的一个客户参加进来,最好是多个。如果我手头已经有了一些东西会更好一点。
Bill Venners: 您说手头有一些东西是指什么?
Erich Gamma: 我有了一个具体的实例。然后我开始推测如何才能把它弄的更加抽象,而不是其它什么方法(and not the other way around)。理想情况下,一旦我推测某些东西我立刻就会问问我潜在的用户们的反馈。
Bill Venners: 所以你会先构建一个具体的实例。
Erich Gamma: 可能是两个或者三个,直到它让我觉着不爽的时候。然后,哇,我必须再复制一次代码。这时候抽象的过程开始了。于是,我说,如果这个类能够让我以插件的形式定制行为而不必重复剩下部分,那可太棒了。
框架症(frameworkitis)的问题
Bill Venners: 框架,平台和工具箱之间的区别是什么呢?它们需要哪些不同的灵活性呢?
Erich Gamma: 对于平台来说,我把长期的稳定性和它联系在一起。在平台之上搭建东西是安全的。一个平台必须保证兼容性。框架通常没有这个品质,而且我看到过许多有关框架的缺陷都与稳定性有关。如果你看看Eclipse,是的,它包括框架,工具箱,而且还提供平台API。所有这些都捆绑成插件的形式。框架通过抽象提供较高层次的默认功能。为了达到这个目的,框架需要在我们的控制之下。如果失去这种控制可能会导致有时候所谓的框架症(frameworkitis)。
Bill Venners: 您的意思是指把所有东西都弄成框架这种病症?
Erich Gamma: 框架症是一种病症,它是指框架想要为你做太多事情或者它以某种你不想要而又无法改变的方式来做某件事情。能够自动完成所有这些功能当然很开心,但是当自动完成的这些功能妨碍到你的时候就不爽了。但是现在你已经陷入框架。为了得到期望的行为你得开始和框架作斗争。这时候通常你就开始失败了,因为很难让框架朝着并非它所期望的方向弯曲。工具箱并不试图取代你的控制权,因此它不会受到框架症的困扰。
Bill Venners: 工具箱不会受到框架症的困扰是因为。。。。。。
Erich Gamma: 使用工具箱,你创建并且调用工具箱对象,然后注册侦听者以便对事件做出响应。你自己控制这一切。框架试图得到控制权并且告诉你什么时候该干什么事情。工具箱给你积木块,但是如何控制取决于你。
Bill Venners: 也就是说框架可能有点像EJB容器,因为我是通过子类化来创建一个EJB的。
Erich Gamma: 对的。而且,如果要写框架,我们试图把它们弄成小型的框架。相对于重量级的框架,我们更倾向于多个小型的框架。
Bill Venners: 为什么?
Erich Gamma: 因为框架越大,它想越俎代庖的机会就越高,学习曲线就越陡峭,而且维护它也会更加困难。如果你真的想冒险使用框架,你应该试试小型的、焦点集中的并且有可能弄成可选的那些框架。假如你确实想用,你可以使用框架,但是你也可以使用工具箱。这种情形很好的避免了框架症所带来的问题,发生框架症的时候你会因为必须得使用框架而懊恼不已。理想情况下,我希望有一个装满了可供挑挑拣拣的小型框架的工具箱,这样在干活的时候我就能用的起框架了。