扫一扫
分享文章到微信
扫一扫
关注官方公众号
至顶头条
本书后面的章节用一个例子描述了设计演化的过程,从最初的解决方案开始,按照合理的推理和步骤,最终演化成更为合理的设计。这个例子程序(一个模拟trash sorting 的例子程序)随着时间不断演化,你可以把它看作是一个原型,你自己做设计的时候也是从一个适用于某一特定问题的解决方案开始,逐渐演化成一个灵活的能够解决某一类问题的方法。
什么是模式?
一开始,你可以把模式想象成一种特别巧妙和敏锐的用以解决某类特定问题的方法。更确切地说,许多人从不同角度解决了某个问题,最终大家提出了最通用和灵活的解决办法。这个问题可能是你以前见过并解决过的,但是你的方法可能比不上你将看到的模式所体现的方法来的完整。
尽管它们被称作“设计模式”,实际上它们没有仅仅限于设计的范畴。模式看起来似乎跟传统的分析,设计和实现相去甚远;恰恰相反,模式体现的是程序整体的构思,所以有时候它也会出现在分析或者是概要设计阶段。这是个有趣的现象,因为模式可以由代码直接实现,所以你可能不希望在详细设计或编码以前使用模式,(实际上在详细设计和编码之前你可能都不会意识到你需要某个特定的模式)。
模式的基本概念也可以看作是设计的基本概念:即增加一个抽象层。无论什么时候,当你想把某些东西抽象出来的时候,实际上你是在分离特定的细节,这么做的一个有说服力的动机就是把变化的东西从那些不变的东西里分离出来。这个问题的另一种说法是,当你发现程序的某一部分由于某种原因有可能会变化的话,你会希望将这些变化不会传播给程序其它部分的代码。这么做不但使程序更容易维护,而且它通常使程序更容易理解(这将降低成本)。
很多情况下,对于能否设计出优雅和容易维护的系统来说,最难的就是找到“一系列变化的东西。”(这里,“vector”指最大梯度――变化最大的部分-―而不是指容器类。)这就意味着最重要的是找出你的系统里变化的部分,或者说是找到成本最高的部分。一旦你找出了这一系列变化,你就可以以之为焦点来构造你的设计。
设计模式的目的就是为了把代码里变化的那一部分分离出来。如果你这么看待设计模式,你会发现本书实际上已经讲了一些设计模式了。比如说,你可以认为继承(inheritance)就是一种设计模式(只不过他是由编译器来实现罢了)。通过继承你可以使拥有相同接口(这些接口是不变的)的对象具有不通的行为(这就是变化的部分),组合(composition)也可以被认为使一种模式,它可以静态或者动态的改变你用以实现某个类的对象,从而改变这个类的行为。
《设计模式》这本书里讲的另外一个模式:迭代器(iterator),你也已经见到过了。(Java1.0和1.1自以为是的把它叫做枚举器(Enumeration);而Java 2 的容器类用了“迭代器(iterator)这个术语”)。这种方法在你需要遍历和选定某些元素的时候隐藏了容器类的特定实现。通过使用迭代器,你可以写出针对某一序列元素的泛型(generic)代码,而不用管这一序列元素是如何生成的。这样,这些泛型代码就可以用于所有能产生迭代器的容器类了。
模式范畴 (pattern taxonomy)
设计模式兴起以后,有人担心“设计模式”这个词被滥用,因为人们开始把所有它们觉得好的东西都说成是设计模式。慎重考虑之后,我把这一系列相近的东西区分为一下几个范畴:
我觉得这么区分可以帮助我们以更全面的眼光来看待这些范畴,从而找到它们适用的地方。但是,这并不意味着某一个范畴就比另外一个好。试图把每一个解决方案都泛化(generalize)成设计模式是没有意义的,这么做纯粹是浪费时间而且也不大可能发现(新的)模式。模式通常是随着时间的推移在不经意间产生的。
上面那些范畴的分类里还可以再加入分析模式(Analysis Pattern)和架构模式(Architectural Pattern)。
设计原则
(从幻灯片里搬到这儿来了)
当我通过自己的电子通讯(newsletter)征集观点的时候,很多人会给我许多有用的建议,但是它们不同于上面讨论的那些范畴。我意识到,一个有关设计原则的列表至少是和设计构造同等重要的。但是,还有另外一个原因,有了这些设计原则你就可以用它们来对自己所建议的设计提出质疑,并检验这些设计的好坏。
最初思考这些设计原则的的时候,我只是希望总结出一两条,以便你在分析一个问题的时候直接就能用上。然而,你可以用上面列表里的其它准则作为对照表来检查和分析你的设计。
模式分类
《设计模式》讨论了23种不同的模式,按照目的把它们分成三类(这些目的围绕某一可变化的特定情况)。这3种分类分别是:
《设计模式》专门有一部分用一个或多个例子来说明这23个模式,这些例子基本上都是用C++写的(rather restricted C++, at that),有时候也用Smalltalk。(你会发现这并不是个大问题,因为你可以好不费力的把那些概念从C++或Smalltalk转换到Java)。本书将用面向Java的思想重新讲解《设计模式》里的许多模式,因为更换语言带来了对模式表示方法和理解的变化。但是,这里不会重复GOF的那些例子,因为我相信通过努力,我们可以设计出更容易让人理解的例子。我的目标是使你知道模式是干什么用的并且为什么它们这么重要。
多年来接触这些东西,我逐渐意识到模式本身用到的其实都是很基本的组织结构(structure),而不是像《设计模式》里所描述的那么复杂(比那些要简单简单的多)。我之所以这么说是因为,大多说设计模式(不限于《设计模式》所总结的那些),就其实现(implementation)所应用的结构(structure)而言,是存在很大的相似性的。尽管我们总是试图避免使用实现(implementation),取而代之以接口(interface),但在这里,我认为用“结构要素”(structural principles)来讲解,会使大家更容易理解这些模式。我试图以这些模式结构上的相似性来给它们重新归类,而不采用《设计模式》里的那些分类。
然而,后来我发现,根据这些模式所解决的问题来给他们归类会更有用。我期望,这种归类方法与Metsker在《Java设计模式手册》( Design Patterns Java Workshop (Addison-Wesley 2002))里所提出的根据目的(intent)来归类的方法会有所不同,这种不同虽然微妙但却是重大的。如果说模式都是用我说的这种方法归类的的话,我希望读者(在学完本书后)能够辨别出问题所在并找到一个解决方案。
在不断“重构本书(book refactoring)”的过程中,我意识到如果我更改过某个地方,那么很有可能以后我还会改那个地方(当然会有一个次数限制),所以我去掉了所有的章节号索引,这样便于以后的改动(这就是鲜为人知的“无章节号” 模式(numberless chapter pattern)):
开发所面临的挑战(challenge)
关于程序开发,UML过程,极限编程(Extreme Programming)。
评估有价值么(Is evaluation valuable)?能力成熟度模型(The Capability Immaturity Model):
Wiki Page: http://c2.com/cgi-bin/wiki?CapabilityImMaturityModel
Article: http://www.embedded.com/98/9807br.htm
关于成对编程的研究(Pair programming research):
http://collaboration.csc.ncsu.edu/laurie/
单元测试
在本书的早些时候的一个版本里,我认定单元测试是必不可少的(和我其它所有的书一样),但是那时候的JUnit用起来实在是太不爽了。当时,我自己写了一套单元测试的框架,它使用Java的反射(reflection)技术使单元测试所必需的一些语法得以简化。
Thinking in Java的第三版里,我开发了另外一套单元测试的框架用以测试那本书中例子的输出结果。
在此期间,JUnit 增加了支持单元测试的语法,这些语法跟我在本书以前版本里用的那些如出一辙。我不知道自己对(JUnit)那些改动起到了多大的影响,但是我很高兴JUnit作了这些改动,这样我就不用维护我自己的测试框架了(但你还是可以在这儿找到它<some URL here>),我所需要做的只是向大家推荐这个既成事实的标准(指JUnit)。
我在Thinking in Java 第三版第十五章里介绍和描述了JUnit的编程风格,我认为那些代码是“最好的实践”(最主要是因为它们很简单)。与本书有关的单元测试,那一章里提供了足够多的介绍(但是,本书通常不会在正文里包含单元测试的代码)。当你下载本书配套代码的时候,你会发现,绝大多数情况下那些代码都是包含单元测试的。
测试代码的位置
(From Bill)这些都是啥玩艺?是不是还没写完?
公有的(public):在“test”子目录里;不通的package(不要包含在jar里)。
享有Package 权限(Package access)的:同一个package,一个库里的子目录(不要包含在jar里)。
私有权限(Private access)的:(白盒测试)。嵌套类,strip out,或者JUnit插件
如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。
现场直击|2021世界人工智能大会
直击5G创新地带,就在2021MWC上海
5G已至 转型当时——服务提供商如何把握转型的绝佳时机
寻找自己的Flag
华为开发者大会2020(Cloud)- 科技行者