├── .gitignore ├── LICENSE ├── README.md ├── big-refactorings └── index.html ├── composing-methods └── index.html ├── css ├── base.css ├── bootstrap-custom.min.css ├── font-awesome-4.0.3.css └── prettify-1.0.css ├── custom_theme ├── readable │ ├── base.html │ ├── content.html │ ├── css │ │ ├── base.css │ │ ├── bootstrap-custom.min.css │ │ ├── font-awesome-4.0.3.css │ │ └── prettify-1.0.css │ ├── fonts │ │ ├── fontawesome-webfont.eot │ │ ├── fontawesome-webfont.svg │ │ ├── fontawesome-webfont.ttf │ │ └── fontawesome-webfont.woff │ ├── img │ │ └── favicon.ico │ ├── js │ │ ├── base.js │ │ ├── bootstrap-3.0.3.min.js │ │ └── prettify-1.0.min.js │ ├── nav.html │ └── toc.html └── simplex │ ├── base.html │ ├── content.html │ ├── css │ ├── base.css │ ├── bootstrap-custom.min.css │ ├── font-awesome-4.0.3.css │ └── prettify-1.0.css │ ├── fonts │ ├── fontawesome-webfont.eot │ ├── fontawesome-webfont.svg │ ├── fontawesome-webfont.ttf │ └── fontawesome-webfont.woff │ ├── img │ └── favicon.ico │ ├── js │ ├── base.js │ ├── bootstrap-3.0.3.min.js │ └── prettify-1.0.min.js │ ├── nav.html │ └── toc.html ├── dealing-with-generalization └── index.html ├── docs ├── big-refactorings.md ├── composing-methods.md ├── dealing-with-generalization.md ├── images │ ├── 06fig01.gif │ ├── 07fig01.gif │ ├── 07fig02.gif │ ├── 07fig03.gif │ ├── 07fig04.gif │ ├── 07fig05.gif │ ├── 07fig06.gif │ ├── 07fig07.gif │ ├── 07fig08.gif │ ├── 08fig01.gif │ ├── 08fig02.gif │ ├── 08fig03.gif │ ├── 08fig04.gif │ ├── 08fig05.gif │ ├── 08fig06.gif │ ├── 08fig07.gif │ ├── 08fig08.gif │ ├── 08fig09.gif │ ├── 08fig10.gif │ ├── 08fig11.gif │ ├── 08fig12.gif │ ├── 09fig01.gif │ ├── 09fig01a.gif │ ├── 09fig01b.gif │ ├── 10fig01.gif │ ├── 10fig02.gif │ ├── 10fig03.gif │ ├── 10fig04.gif │ ├── 10fig05.gif │ ├── 10fig06.gif │ ├── 10fig07.gif │ ├── 10fig08.gif │ ├── 11fig01.gif │ ├── 11fig02.gif │ ├── 11fig03-1.gif │ ├── 11fig03.gif │ ├── 11fig04.gif │ ├── 11fig05.gif │ ├── 11fig06.gif │ ├── 11fig07.gif │ ├── 11fig08.gif │ ├── 11fig09.gif │ ├── 11fig10.gif │ ├── 11fig11.gif │ ├── 11fig12.gif │ ├── 11fig13.gif │ ├── 11fig14.gif │ ├── 11fig15.gif │ ├── 12fig01.gif │ ├── 12fig02.png │ ├── 12fig03.gif │ ├── 12fig04.gif │ ├── 12fig05.gif │ ├── 12fig06.gif │ ├── 12fig07.gif │ ├── 12fig08.gif │ ├── 12fig09.gif │ ├── 12fig10.gif │ ├── 12fig11.gif │ ├── 12fig12.gif │ ├── 12fig13.gif │ ├── 12fig14.gif │ ├── 12fig15.gif │ ├── 12fig16.gif │ └── arrow.gif ├── index.md ├── making-method-calls-simpler.md ├── moving-features-between-objects.md ├── organizing-data.md └── simplifying-conditional-expressions.md ├── fonts ├── fontawesome-webfont.eot ├── fontawesome-webfont.svg ├── fontawesome-webfont.ttf └── fontawesome-webfont.woff ├── images ├── 06fig01.gif ├── 07fig01.gif ├── 07fig02.gif ├── 07fig03.gif ├── 07fig04.gif ├── 07fig05.gif ├── 07fig06.gif ├── 07fig07.gif ├── 07fig08.gif ├── 08fig01.gif ├── 08fig02.gif ├── 08fig03.gif ├── 08fig04.gif ├── 08fig05.gif ├── 08fig06.gif ├── 08fig07.gif ├── 08fig08.gif ├── 08fig09.gif ├── 08fig10.gif ├── 08fig11.gif ├── 08fig12.gif ├── 09fig01.gif ├── 09fig01a.gif ├── 09fig01b.gif ├── 10fig01.gif ├── 10fig02.gif ├── 10fig03.gif ├── 10fig04.gif ├── 10fig05.gif ├── 10fig06.gif ├── 10fig07.gif ├── 10fig08.gif ├── 11fig01.gif ├── 11fig02.gif ├── 11fig03-1.gif ├── 11fig03.gif ├── 11fig04.gif ├── 11fig05.gif ├── 11fig06.gif ├── 11fig07.gif ├── 11fig08.gif ├── 11fig09.gif ├── 11fig10.gif ├── 11fig11.gif ├── 11fig12.gif ├── 11fig13.gif ├── 11fig14.gif ├── 11fig15.gif ├── 12fig01.gif ├── 12fig02.png ├── 12fig03.gif ├── 12fig04.gif ├── 12fig05.gif ├── 12fig06.gif ├── 12fig07.gif ├── 12fig08.gif ├── 12fig09.gif ├── 12fig10.gif ├── 12fig11.gif ├── 12fig12.gif ├── 12fig13.gif ├── 12fig14.gif ├── 12fig15.gif ├── 12fig16.gif └── arrow.gif ├── img └── favicon.ico ├── index.html ├── js ├── base.js ├── bootstrap-3.0.3.min.js └── prettify-1.0.min.js ├── making-method-calls-simpler └── index.html ├── mkdocs.yml ├── moving-features-between-objects └── index.html ├── organizing-data └── index.html └── simplifying-conditional-expressions └── index.html /.gitignore: -------------------------------------------------------------------------------- 1 | site 2 | *.swp 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/LICENSE -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | refactoring-cheat-sheet 2 | ======================= 3 | 4 | 重构速查表,基于《重构--改善既有代码的设计》 5 | 6 | http://wangvsa.github.io/refactoring-cheat-sheet/ 7 | 8 | #### 关于 9 | --- 10 | 11 | 拜读《重构》一书后受益匪浅,网络上有pdf、chm版本,但是对于代码的高亮以及各个章节的跳转不是十分方便, 12 | 于是基于《重构》一书的6~12章制作了这个网页,目的在于迅速的浏览重构方法,也可用于新手入门。 13 | 14 | 网页使用[MkDocs](http://mkdocs.org)创建,主题改造自simplex。 15 | -------------------------------------------------------------------------------- /big-refactorings/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 大型重构 - Refactoring Cheat Sheet 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 24 | 25 | 26 | 27 | 28 | 128 | 129 |
130 |
147 |
148 | 149 |

梳理并分解继承体系

150 |
151 |

某个继承体系(inheritance hierarchy )同时承担两项责任。

152 |

建立两个继承体系,并通过委托关系(delegation)让其中一个可以调用另一个。

153 |

154 |

动机(Motivation)

155 |

继承是个好东西,它使你得以在subclass 中写出明显「压缩过」(compressed)的 代码。函数的重要性可能并不和它的大小成比例——在继承体系之中尤然。

156 |

不过,先别急着为这个强大的工具欢呼雀跃,因为继承也很容易被误用,并且这种误用还很容易在开发人员之间蔓延。今天你为了一项小小任务而加入一个小小的subclass ;明天又为同样任务在继承体系的另一个地方加入另一个subclass 。一个星期(或者一个月或者一年)之后,你就会发现自己身陷泥淖,而且连一根拐杖都没有。

157 |

混乱的继承体系是一个严重的问题,因为它会导致重复代码,而后者正是程序员生涯的致命毒药。它还会使修改变得困难,因为「特定种类」的问题的解决策略被分散到了整个继承体系。最终,你的代码将非常难以理解。你无法简单地说:『这就 是我的继承体系,它能计算结果』,而必须说:『它会计算出结果……呃,这些是 用以表现不同表格形式的subclasses ,每个subclass 又有一些subclasses 针对不同的 国家。』

158 |

要指出「某个继承体系承担了两项不同的责任」并不困难:如果继承体系中的某一特定层级上的所有classes,其subclass 名称都以相同的形容词开始,那么这个体系很可能就是承担着两项不同的责任。

159 |

作法(Mechanics)

160 | 169 |

范例:(Example)

170 |

让我们来看一个混乱的继承体系(如图12.1)。

171 |

172 |

图12.1 一个混乱的继承体系

173 |

这个继承体系之所以混乱,因为一开始Deal class 只被用来显示单笔交易。后来,某个人突发奇想地用它来显示一张交易表格。只需飞快建立一个ActiveDeal subclass 再加上一点点经验,不必做太多工作就可以显示一张表格了。哦,还要「被动交易(PassiveDeal)」表格是吗?没问题,再加一个subclass 就行了。

174 |

两个月过去,表格相关代码变得愈来愈复杂,你却没有一个好地方可以放它们,因为时间太紧了。咳,老戏码!现在你将很难向系统加入新种交易,因为「交易处理逻辑」与「数据显示逻辑」已经「你中有我,我中有你」了。

175 |

按照本重构提出的处方笺,第一步工作是识别出这个继承体系所承担的各项责任。 这个继承体系的职责之一是捕捉不同交易种类间的变化(差异〕,职责之二是捕捉 不同显示风格之间的变化(差异〕。因此,我们可以得到下列表格:

176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 |
DealActive DealPassive Deal
Tabular
192 |

下一步要判断哪一项职责更重要。很明显「交易种类」比「显示风格」重要,因此我们把「交易种类」留在原地,把「显示风格」提炼到另一个继承体系中。不过,实际工作中,我们可能需要将「代码较多」的职责留在原地,这样一来需要搬移的 代码数量会比较少。

193 |

然后,我们应该使用提炼类 提炼出一个单独的PresentationStyle class 用以表示「显示风格」(如图12.2)。

194 |

195 |

图12.2 添加PresentationStyle ,用以表示「显示风格」

196 |

接下来我们需要针对原继承体系中的每个subclass ,建立PresentationStyle 的一个个subclasses (如图 12.3),并将Deal class之中用来保存PresentationStyle 实体的那个instance 变量初始化为适当的subclass 实体:

197 |

198 |

图12.3 为PresentationStyle 添加subclasses

199 |
ActiveDeal constructor
200 |   ...presentation= new SingleActivePresentationStyle();...
201 | 
202 | 203 |

你可能会说:『这不是比原先的classes 数量还多了吗?难道这还能让我的生活更舒服?』生活往往如此:以退为进,走得更远。对一个纠结成团的继承体系来说,被提炼出来的另一个继承体系几乎总是可以再戏剧性地大量简化。不过,比较安全的态度是一次一小步,不要过于躁进。

204 |

现在,我们要使用 搬移函数搬移值域,将Deal subclass 中[与显示逻辑相关」的函数和变量搬移到PresentationStyle 相应的subclasses 去。我们想不出什么好办法来模拟这个过程,只好请你自己想像。总之,这个步骤完成后,TabularActiveDeal 和 TabularPassiveDeal不再有任何代码,因此我们将它们移除(如图12.4)。

205 |

206 |

图12.4 与表格相关的 Deal subclass 都移除了

207 |

两项职责被分割之后,我们可以分别简化两个继承体系。一旦本重构完成,我们总是能够大大简化被提炼出来的新继承体系,而且通常还可以简化原继承体系。

208 |

下一步,我们将摆脱「显示风格」中的主动(active)与被动(passive)区别,如图 12.5。

209 |

210 |

图12.5 继承体系被分割了

211 |

就连「单一显示」和「表格显示」之间的区别,都可以运用若干变量值来捕捉,根本不需要为它们建立subclasses (如图12.6〕。

212 |

213 |

图12.6「显示风格」(presentation style)之间的差异可以使用一些变量来表现

214 |

将过程化设计转化为对象设计

215 |
216 |

你手上有一些代码,以传统的过程化风格(procedural style)写就。

217 |

将数据记录(data records)9 变成对象,将行为分开,并将行为移入相关对象之中。

218 |

9译注:这里所谓的记录(record),是指像C struct 那样的结构。

219 |

220 |

动机(Motivation)

221 |

有一次,我们的一位客户,在项目开始时,给开发者提出了两条必须遵守的条件:(1)必须使用Java;(2)不能使用对象。

222 |

这让我们忍俊不禁。尽管是面向对象语言,「使用对象」可远不仅仅是「调用构造函数」而已。对象的使用也需要花时间去学习。往往你会面对一些过程化风格(procedural style)的代码所带来的问题,并因而希望它们变得更面向对象一些。 典型的情况是:class 中有着长长的过程化函数和极少的数据,以及所谓的「哑数据 对象」——除了数据访问函数(accessors)外没有其他任何函数。如果你要转换的是一个纯粹的过程化程序(procedural program),可能连这些东西都没有。

223 |

我们并不是说绝对不应该出现「只有行为而几乎没有数据」的对象。在Strategy 模式中,我们常常使用一些小型的strategy 对象来改变宿主对象的行为,这些小型的strategy 对象就是「只有行为而没有数据」。但是这样的对象通常比较小,而且只有在我们特规需要灵活性的时候,才会使用它们。

224 |

作法(Mechanics) 225 | - 针对每一个记录型别(record type),将它转变为「只含访问函数」的「哑数据对象』(dump data object )。 226 | - 如果你的数据来自关系式数据库(relational database),就把数据库中的每个表(table)变成一个「哑数据对象」。 227 | - 针对每一处过程化风格,将该处的代码提炼到一个独立class 中。 228 | - 你可以把提炼所得的class 做成一个Singleton(单件;为了方便重新初始化),或是把提炼所得的函数声明为static。 229 | - 针对每一个长长的程序(procedure),实施提炼函数及其他相关重构,将它分解。再以搬移函数 将分解后的函数分别移到它所相关的哑数据类(dump data class)中。 230 | - 重复上述步骤,直到原始中的所有函数都被移除。如果原始是一个完全过程化(purely procedural〕的class ,将它拿掉将大快人心。

231 |

范例:(Example)

232 |

第1章的范例很好地展示了将过程化设计转化为对象设计,尤其是第一阶段(对statement() 函数的分解和安置)。完成这项重构之后,你就拥有了一个「聪明的」数据对象,可以对它进行其他种重构了。

233 |

将领域和表述/显示分离

234 |
235 |

(译注:本节保留domain-,presentation-logic,UI,class,object等英文词)

236 |

某些GUI class 之中包含了domain logic(领域逻辑)。

237 |

将domain logic(领域逻辑)分离出来,为它们建立独立的domain class。

238 |

239 |

动机(Motivation)

240 |

提到面向对象,就不能不提model-view-controller (MVC,模型-视图-控制器) 模式。在Smalltalk-80环境中,人们以此模式维护GUI(图形用户界面)和domain object (领域对象)间的关系。

241 |

MVC模式的最核心价值在于:它将用户界面代码(即所谓view,视图;亦即现今常说的presentation,表述)和领域逻辑(即所谓model,模型)分离了。presentation class 只含用以处理用户界面的逻辑;domain class不含任何与程序外观相关的代码,只含业务逻辑(business logic)相关代码。将程序中这两块复杂的部分加以分离,程序未来的修改将变得更加容易,同时也使同一业务逻辑(business logic)的多种表述(显示)方式成为可能。那些熟稔面向对象技术的程序员会毫不犹豫地在他们的程序中进行这种分离,并且这种作法也的确证实了它自身的价值。

242 |

但是,大多数人并没有在设计中采用这种方式来处理GUI。大多数带有client-server GUIs 的环境都釆用双层(two-tier)逻辑设计:数据保存在数据库中,业务逻辑(business logic)放在presentation class 中。这样的环境往往迫使你也倾向这种风格的设计,使你很难把业务逻辑放在其他地方。

243 |

Java 是一个真正意义上的面向对象环境,因此你可以创建内含业务逻辑的非视觉性领域对象(nonvisual domain objects )。但你却还是会经常遇到上述双层风格写就的程序。

244 |

作法(Mechanics)

245 | 252 |

范例:(Example)

253 |

下面是一个商品订购程序。其GUI 如图12.7所示,其presentation class 与图12.8 所示的关系式数据库(relational database)互动。

254 |

255 |

图12.7 启动程序的用户界面

256 |

257 |

图12.8 订单程序所用的数据库

258 | 261 |

所有行为(包括GUI 和定单处理)都由OrderWindow class 处理。

262 |

首先建立一个Order class 表示「定单」。然后把Order 和OrderWindow 联系起来, 如图12.9。由于窗口中有一个用以显示定单的表格(grid),所以我们还得建立一个OrderLine,用以表示表格中的每一行(rows)。

263 |

264 |

图12.9 OrderWindow class 和Order class

265 |

我们将从窗口这边而不是从数据库那边开始重构。当然,一开始就把domain model 建立在数据库基础上,也是一种合理策略,但我们最大的风险源于presentation logic 和domain logic之间的混淆,因此我们首先基于窗口将这些分离出来,然后再考虑对其他地方进行重构。

266 |

面对这一类程序,在窗口中寻找内嵌的SQL (结构化查询语言)语句,会对你有所帮助,因为SQL 语句获取的数据一定是domain data 。

267 |

最容易处理的domain data 就是那些不直接显示于GUI 者。本例数据库的Customer table 中有一个Codes 值域,它并不直接显示于GUI,而是被转换为一个更容易被人理解的短语之后再显示。程序中以简单型别(而非AWT组件)如String 保存这个值域值。我们可以安全地使用搬移值域 将这个值域移到domain class。

268 |

对于其他值域,我们就没有这么幸运了,因为它们内含AWT 组件,既显示于窗口, 也被domain object 使用。面对这些值域,我们需要使用复制被监视数据,把一个domain field 放进Order class,又把一个相应的AWT field 放进OrderWindow class。

269 |

这是一个缓慢的过程,但最终我们还是可以把所有domain logic field 都搬到domain class。进行这一步骤时,你可以试着把所有SQL calls 都移到domain class,这样你就是同时移动了database logic 和domain data。最后,你可以在OrderWindow 中移除import java.sql 之类的语句,这就表示我们的重构告一段落了。在此阶段中 你可能需要大量运用 提炼函数搬移函数

270 |

现在,我们拥有的三个classes,如图12.10所示,它们离「组织良好」还有很大的距离。不过这个模型的确已经很好地分离了presentation logic 和domain logic (business logic)。本项重构的进行过程中,你必须时刻留心你的风险来自何方。 如果「presentation logic 和domain logic 混淆」是最大风险,那么就先把它们完全分开,然后才做其他工作;如果其他方面的事情(例如产品定价策略〉更重要,那么就先把那一部分的logic 从窗口提炼出来,并围绕着这个高风险部分进行重构,为它建立合适的结构。反正domain logic 早晚都必须从窗口移出,如果你在处理高风险部分的重构时会遗留某些logic 于窗口之中,没关系,就放手去做吧。

271 |

272 |

图12.10 将数据安置(分散)于domain classes 中

273 |

提炼继承体系

274 |
275 |

你有某个class 做了太多(过多〕工作,其中一部分工作是以大量条件式完成的。

276 |

建立继承体系,以一个subclass 表示一种特殊情况。

277 |

278 |

动机(Motivation)

279 |

在渐进式设计过程中,常常会有这样的情况:一开始设计者只想「以一个class 实 现一个概念」;但随着设计方案的演化,最后却可能「一个class 实现了两个、三 个乃至十个不同的概念」。一开始,你建立了这个简单的class ;数天或数周之后, 你可能发现:只要加入一个标记(flag)和一两个测试,就可以在另一个环境下使用这个class ;一个月之后你又发现了另一个这样的机会;一年之后,这个class 就完全一团糟了:标记变量和条件式遍布各处。

280 |

当你遇到这种「瑞士小刀般」的class ,不但能够开瓶开罐、砍小树枝、在演示文稿会上打出激光强调重点……(简直无所不能),你就需要一个好策略(亦即本项重构、将它的各个功能梳理并分开。不过,请注意,只有当条件逻辑(conditional logic)在对象的整个生命期间保持不变,本重构所导入的策略才适用。否则你可能必须在分离各种状况之前先使用提炼类

281 |

提炼继承体系是一项大型重构,如果你一天之内不足以完成它,不要因此失去勇气。将一个极度混乱的设计方案梳理出来,可能需要数周甚至数月的时间。 你可以先进行本重构中的一些简易步骤,稍微休息一下,再花数天编写一些明显有生产力的代码。当你领悟到更多东西,再回来继续本项重构的其他步骤——这些步骤将因为你的领悟而显得更加简单明了。

282 |

作法(Mechanics)

283 |

我们为你准备了两组重构作法。第一种情况是:你无法确定变异(variations)应该是些什么(也就是说你无法确定原始class 中该有哪些条件逻辑〕。这时候你希望每次一小步地前进:

284 | 293 |

如果你非常清楚原始class 会有哪些变异(variations),可以使用另一种作法:

294 | 306 |

范例:(Example)

307 |

这里所举的例子是「变异并不明朗」的情况。你可以在 以子类取代型别码以State/Strategy取代型别码以多态取代条件式 等重构结果之上,验证「变异已经明朗」的情况下如何使用本项重构。

308 |

我们以一个电费计算程序为例。这个程序有两个classes :表示「消费者」的Customer 和表示「计费方案」的BillingScheme,如图12.11。

309 |

310 |

图12.11 Customer 和BillingScheme

311 |

BillingScheme 使用大量条件逻辑来计算不同情况(变异)下的费用:冬季和夏季的电价不同,私宅用电、小型企业用电、社会救济(包括残障人士)用电的价格也不同。这些复杂的逻辑导致BillingScheme 变得复杂。

312 |

第一个步骤是,提炼出条件逻辑中经常出现的某种变异性。本例之中可能是「视用户是否为残障人士」而发生的变化。用于标示这种情况的可能是Customer、 BillingScheme 或其他地方的一个标记变量(flag)。

313 |

我们针对这种变异建立一个subclass 。为了使用这个subclass ,我们需要确保它被建立并且被使用。因此我们需要处理BillingScheme 构造函数:首先对它实施 以工厂函数取代构造函数,然后在所得的factory method 中为残障 人士增加一个条件子句,使它在适当时候返回一个DisablityBillingScheme对象。

314 |

然后,我们需要观察BillingScheme 的其他函数,寻找那些随着「用户是否为残障人士」而变化的行为。createBill() 就是这样一个函数,因此我们将它拷贝到subclass (图 12,12)。

315 |

316 |

图12.12 为「残障人士」添加一个subclass

317 |

现在,我们需要检查subclass 中的createBill() 函数。由于现在我们可以肯定该 消费者是残障人士,因此可以简化这个函数。所以下列代码:

318 |
if (disabilityScheme()) doSomething
319 | 
320 | 321 |

可以变成:

322 |
doSomething
323 | 
324 | 325 |

如果规定在「残障人士用电」和「企业用电」之间只能择一,那么我们的方案就可以避免在BusinessBillingScheme 中出现任何条件代码。

326 |

实施本项重构时,我们希望将「可能变化」和「始终不变」的部分分开,为此我们可以使用 提炼函数分解条件式。本例将对BillingScheme 各函数实施这两项重构,直到「是否为残障人士」的所有判断都得到了适当处理。然后我们再以相同过程处理他种变异(例如「社会救济用电」)。

327 |

然而,当我们处理第二种变异时,我们应该观察「社会救济用电」与「残障人士用 电」有何不同。我们希望能够为不同的变异(特殊情况)建立起这般函数:有着相同意图,但针对不同的变异性(特殊情况)釆取不同的实际作为(译注:这就是Template Method)。例如上述两种变异情况下的税额计算可能不同。我们希望确保两个subclasses 中的相应函数有相同的签名式(signature)。这可能意味我们必须修改DisablityBillingScheme,使得以将subclasses 整理一番。通常我们发现,面对更多变异时,这种「相仿之中略带变化」的函数(similar and varying methods patterns)会使整个系统结构趋于稳定,使我们更容易添加后续更多变异。

328 |
329 |
330 | 331 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | -------------------------------------------------------------------------------- /css/base.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 70px; 3 | } 4 | 5 | ul.nav li.main { 6 | font-weight: bold; 7 | } 8 | 9 | div.col-md-3 { 10 | padding-left: 0; 11 | } 12 | 13 | div.source-links { 14 | float: right; 15 | } 16 | 17 | footer { 18 | margin-top: 30px; 19 | margin-bottom: 10px; 20 | text-align: center; 21 | font-weight: 200; 22 | } 23 | 24 | /* 25 | * Side navigation 26 | * 27 | * Scrollspy and affixed enhanced navigation to highlight sections and secondary 28 | * sections of docs content. 29 | */ 30 | 31 | /* By default it's not affixed in mobile views, so undo that */ 32 | .bs-sidebar.affix { 33 | position: static; 34 | margin-top: 40px; 35 | } 36 | 37 | .bs-sidebar.well { 38 | padding: 0; 39 | } 40 | 41 | /* First level of nav */ 42 | .bs-sidenav { 43 | padding-top: 10px; 44 | padding-bottom: 10px; 45 | border-radius: 5px; 46 | } 47 | 48 | /* All levels of nav */ 49 | .bs-sidebar .nav > li > a { 50 | display: block; 51 | padding: 5px 20px; 52 | } 53 | .bs-sidebar .nav > li > a:hover, 54 | .bs-sidebar .nav > li > a:focus { 55 | text-decoration: none; 56 | border-right: 1px solid; 57 | } 58 | .bs-sidebar .nav > .active > a, 59 | .bs-sidebar .nav > .active:hover > a, 60 | .bs-sidebar .nav > .active:focus > a { 61 | font-weight: bold; 62 | background-color: transparent; 63 | border-right: 1px solid; 64 | } 65 | 66 | /* Nav: second level (shown on .active) */ 67 | .bs-sidebar .nav .nav { 68 | display: none; /* Hide by default, but at >768px, show it */ 69 | margin-bottom: 8px; 70 | } 71 | .bs-sidebar .nav .nav > li > a { 72 | padding-top: 3px; 73 | padding-bottom: 3px; 74 | padding-left: 30px; 75 | font-size: 90%; 76 | } 77 | 78 | /* Show and affix the side nav when space allows it */ 79 | @media (min-width: 992px) { 80 | .bs-sidebar .nav > .active > ul { 81 | display: block; 82 | } 83 | /* Widen the fixed sidebar */ 84 | .bs-sidebar.affix, 85 | .bs-sidebar.affix-bottom { 86 | width: 213px; 87 | } 88 | .bs-sidebar.affix { 89 | position: fixed; /* Undo the static from mobile first approach */ 90 | top: 80px; 91 | } 92 | .bs-sidebar.affix-bottom { 93 | position: absolute; /* Undo the static from mobile first approach */ 94 | } 95 | .bs-sidebar.affix-bottom .bs-sidenav, 96 | .bs-sidebar.affix .bs-sidenav { 97 | margin-bottom: 0; 98 | } 99 | } 100 | @media (min-width: 1200px) { 101 | /* Widen the fixed sidebar again */ 102 | .bs-sidebar.affix-bottom, 103 | .bs-sidebar.affix { 104 | width: 263px; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /css/prettify-1.0.css: -------------------------------------------------------------------------------- 1 | .com { color: #93a1a1; } 2 | .lit { color: #195f91; } 3 | .pun, .opn, .clo { color: #93a1a1; } 4 | .fun { color: #dc322f; } 5 | .str, .atv { color: #D14; } 6 | .kwd, .prettyprint .tag { color: #1e347b; } 7 | .typ, .atn, .dec, .var { color: teal; } 8 | .pln { color: #48484c; } 9 | 10 | .prettyprint { 11 | padding: 8px; 12 | } 13 | .prettyprint.linenums { 14 | -webkit-box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0; 15 | -moz-box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0; 16 | box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0; 17 | } 18 | 19 | /* Specify class=linenums on a pre to get line numbering */ 20 | ol.linenums { 21 | margin: 0 0 0 33px; /* IE indents via margin-left */ 22 | } 23 | ol.linenums li { 24 | padding-left: 12px; 25 | color: #bebec5; 26 | line-height: 20px; 27 | text-shadow: 0 1px 0 #fff; 28 | } 29 | -------------------------------------------------------------------------------- /custom_theme/readable/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | {% if page_description %}{% endif %} 8 | {% if site_author %}{% endif %} 9 | {% if canonical_url %}{% endif %} 10 | {% if favicon %} 11 | {% else %}{% endif %} 12 | 13 | {{ page_title }} 14 | 15 | 16 | 17 | 18 | 19 | {%- for path in extra_css %} 20 | 21 | {%- endfor %} 22 | 23 | 24 | 28 | 29 | 30 | 31 | 32 | {% include "nav.html" %} 33 | 34 |
35 |
{% include "toc.html" %}
36 |
{% include "content.html" %}
37 |
38 | 39 | {% if include_search %}{% include "search.html" %}{% endif %} 40 | 41 | 42 | 43 | 44 | 45 | {%- for path in extra_javascript %} 46 | 47 | {%- endfor %} 48 | 49 | 50 | -------------------------------------------------------------------------------- /custom_theme/readable/content.html: -------------------------------------------------------------------------------- 1 | {% if meta.source %} 2 | 7 | {% endif %} 8 | 9 | {{ content }} 10 | -------------------------------------------------------------------------------- /custom_theme/readable/css/base.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 70px; 3 | } 4 | 5 | ul.nav li.main { 6 | font-weight: bold; 7 | } 8 | 9 | div.col-md-3 { 10 | padding-left: 0; 11 | } 12 | 13 | div.source-links { 14 | float: right; 15 | } 16 | 17 | /* 18 | * Side navigation 19 | * 20 | * Scrollspy and affixed enhanced navigation to highlight sections and secondary 21 | * sections of docs content. 22 | */ 23 | 24 | /* By default it's not affixed in mobile views, so undo that */ 25 | .bs-sidebar.affix { 26 | position: static; 27 | } 28 | 29 | .bs-sidebar.well { 30 | padding: 0; 31 | } 32 | 33 | /* First level of nav */ 34 | .bs-sidenav { 35 | margin-top: 30px; 36 | margin-bottom: 30px; 37 | padding-top: 10px; 38 | padding-bottom: 10px; 39 | border-radius: 5px; 40 | } 41 | 42 | /* All levels of nav */ 43 | .bs-sidebar .nav > li > a { 44 | display: block; 45 | padding: 5px 20px; 46 | } 47 | .bs-sidebar .nav > li > a:hover, 48 | .bs-sidebar .nav > li > a:focus { 49 | text-decoration: none; 50 | border-right: 1px solid; 51 | } 52 | .bs-sidebar .nav > .active > a, 53 | .bs-sidebar .nav > .active:hover > a, 54 | .bs-sidebar .nav > .active:focus > a { 55 | font-weight: bold; 56 | background-color: transparent; 57 | border-right: 1px solid; 58 | } 59 | 60 | /* Nav: second level (shown on .active) */ 61 | .bs-sidebar .nav .nav { 62 | display: none; /* Hide by default, but at >768px, show it */ 63 | margin-bottom: 8px; 64 | } 65 | .bs-sidebar .nav .nav > li > a { 66 | padding-top: 3px; 67 | padding-bottom: 3px; 68 | padding-left: 30px; 69 | font-size: 90%; 70 | } 71 | 72 | /* Show and affix the side nav when space allows it */ 73 | @media (min-width: 992px) { 74 | .bs-sidebar .nav > .active > ul { 75 | display: block; 76 | } 77 | /* Widen the fixed sidebar */ 78 | .bs-sidebar.affix, 79 | .bs-sidebar.affix-bottom { 80 | width: 213px; 81 | } 82 | .bs-sidebar.affix { 83 | position: fixed; /* Undo the static from mobile first approach */ 84 | top: 80px; 85 | } 86 | .bs-sidebar.affix-bottom { 87 | position: absolute; /* Undo the static from mobile first approach */ 88 | } 89 | .bs-sidebar.affix-bottom .bs-sidenav, 90 | .bs-sidebar.affix .bs-sidenav { 91 | margin-top: 0; 92 | margin-bottom: 0; 93 | } 94 | } 95 | @media (min-width: 1200px) { 96 | /* Widen the fixed sidebar again */ 97 | .bs-sidebar.affix-bottom, 98 | .bs-sidebar.affix { 99 | width: 263px; 100 | } 101 | } -------------------------------------------------------------------------------- /custom_theme/readable/css/prettify-1.0.css: -------------------------------------------------------------------------------- 1 | .com { color: #93a1a1; } 2 | .lit { color: #195f91; } 3 | .pun, .opn, .clo { color: #93a1a1; } 4 | .fun { color: #dc322f; } 5 | .str, .atv { color: #D14; } 6 | .kwd, .prettyprint .tag { color: #1e347b; } 7 | .typ, .atn, .dec, .var { color: teal; } 8 | .pln { color: #48484c; } 9 | 10 | .prettyprint { 11 | padding: 8px; 12 | } 13 | .prettyprint.linenums { 14 | -webkit-box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0; 15 | -moz-box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0; 16 | box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0; 17 | } 18 | 19 | /* Specify class=linenums on a pre to get line numbering */ 20 | ol.linenums { 21 | margin: 0 0 0 33px; /* IE indents via margin-left */ 22 | } 23 | ol.linenums li { 24 | padding-left: 12px; 25 | color: #bebec5; 26 | line-height: 20px; 27 | text-shadow: 0 1px 0 #fff; 28 | } 29 | -------------------------------------------------------------------------------- /custom_theme/readable/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/custom_theme/readable/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /custom_theme/readable/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/custom_theme/readable/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /custom_theme/readable/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/custom_theme/readable/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /custom_theme/readable/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/custom_theme/readable/img/favicon.ico -------------------------------------------------------------------------------- /custom_theme/readable/js/base.js: -------------------------------------------------------------------------------- 1 | 2 | /* Prettyify */ 3 | $( document ).ready(function() { 4 | prettyPrint(); 5 | }); 6 | 7 | 8 | /* Scrollspy */ 9 | var navHeight = $('.navbar').outerHeight(true) + 10 10 | 11 | $('body').scrollspy({ 12 | target: '.bs-sidebar', 13 | offset: navHeight 14 | }) 15 | 16 | 17 | /* Prevent disabled links from causing a page reload */ 18 | $("li.disabled a").click(function() { 19 | event.preventDefault(); 20 | }); 21 | 22 | 23 | /* Adjust the scroll height of anchors to compensate for the fixed navbar */ 24 | window.disableShift = false; 25 | var shiftWindow = function() { 26 | if (window.disableShift) { 27 | window.disableShift = false; 28 | } else { 29 | /* If we're at the bottom of the page, don't erronously scroll up */ 30 | var scrolledToBottomOfPage = ( 31 | (window.innerHeight + window.scrollY) >= document.body.offsetHeight 32 | ); 33 | if (!scrolledToBottomOfPage) { 34 | scrollBy(0, -60); 35 | }; 36 | }; 37 | }; 38 | if (location.hash) {shiftWindow();} 39 | window.addEventListener("hashchange", shiftWindow); 40 | 41 | 42 | /* Deal with clicks on nav links that do not change the current anchor link. */ 43 | $("ul.nav a" ).click(function() { 44 | var href = this.href; 45 | var suffix = location.hash; 46 | var matchesCurrentHash = (href.indexOf(suffix, href.length - suffix.length) !== -1); 47 | if (location.hash && matchesCurrentHash) { 48 | /* Force a single 'hashchange' event to occur after the click event */ 49 | window.disableShift = true; 50 | location.hash=''; 51 | }; 52 | }); 53 | -------------------------------------------------------------------------------- /custom_theme/readable/js/bootstrap-3.0.3.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.0.3 (http://getbootstrap.com) 3 | * Copyright 2013 Twitter, Inc. 4 | * Licensed under http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | 7 | if("undefined"==typeof jQuery)throw new Error("Bootstrap requires jQuery");+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]}}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one(a.support.transition.end,function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b()})}(jQuery),+function(a){"use strict";var b='[data-dismiss="alert"]',c=function(c){a(c).on("click",b,this.close)};c.prototype.close=function(b){function c(){f.trigger("closed.bs.alert").remove()}var d=a(this),e=d.attr("data-target");e||(e=d.attr("href"),e=e&&e.replace(/.*(?=#[^\s]*$)/,""));var f=a(e);b&&b.preventDefault(),f.length||(f=d.hasClass("alert")?d:d.parent()),f.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(f.removeClass("in"),a.support.transition&&f.hasClass("fade")?f.one(a.support.transition.end,c).emulateTransitionEnd(150):c())};var d=a.fn.alert;a.fn.alert=function(b){return this.each(function(){var d=a(this),e=d.data("bs.alert");e||d.data("bs.alert",e=new c(this)),"string"==typeof b&&e[b].call(d)})},a.fn.alert.Constructor=c,a.fn.alert.noConflict=function(){return a.fn.alert=d,this},a(document).on("click.bs.alert.data-api",b,c.prototype.close)}(jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d)};b.DEFAULTS={loadingText:"loading..."},b.prototype.setState=function(a){var b="disabled",c=this.$element,d=c.is("input")?"val":"html",e=c.data();a+="Text",e.resetText||c.data("resetText",c[d]()),c[d](e[a]||this.options[a]),setTimeout(function(){"loadingText"==a?c.addClass(b).attr(b,b):c.removeClass(b).removeAttr(b)},0)},b.prototype.toggle=function(){var a=this.$element.closest('[data-toggle="buttons"]'),b=!0;if(a.length){var c=this.$element.find("input");"radio"===c.prop("type")&&(c.prop("checked")&&this.$element.hasClass("active")?b=!1:a.find(".active").removeClass("active")),b&&c.prop("checked",!this.$element.hasClass("active")).trigger("change")}b&&this.$element.toggleClass("active")};var c=a.fn.button;a.fn.button=function(c){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof c&&c;e||d.data("bs.button",e=new b(this,f)),"toggle"==c?e.toggle():c&&e.setState(c)})},a.fn.button.Constructor=b,a.fn.button.noConflict=function(){return a.fn.button=c,this},a(document).on("click.bs.button.data-api","[data-toggle^=button]",function(b){var c=a(b.target);c.hasClass("btn")||(c=c.closest(".btn")),c.button("toggle"),b.preventDefault()})}(jQuery),+function(a){"use strict";var b=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=this.sliding=this.interval=this.$active=this.$items=null,"hover"==this.options.pause&&this.$element.on("mouseenter",a.proxy(this.pause,this)).on("mouseleave",a.proxy(this.cycle,this))};b.DEFAULTS={interval:5e3,pause:"hover",wrap:!0},b.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},b.prototype.getActiveIndex=function(){return this.$active=this.$element.find(".item.active"),this.$items=this.$active.parent().children(),this.$items.index(this.$active)},b.prototype.to=function(b){var c=this,d=this.getActiveIndex();return b>this.$items.length-1||0>b?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){c.to(b)}):d==b?this.pause().cycle():this.slide(b>d?"next":"prev",a(this.$items[b]))},b.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition.end&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},b.prototype.next=function(){return this.sliding?void 0:this.slide("next")},b.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},b.prototype.slide=function(b,c){var d=this.$element.find(".item.active"),e=c||d[b](),f=this.interval,g="next"==b?"left":"right",h="next"==b?"first":"last",i=this;if(!e.length){if(!this.options.wrap)return;e=this.$element.find(".item")[h]()}this.sliding=!0,f&&this.pause();var j=a.Event("slide.bs.carousel",{relatedTarget:e[0],direction:g});if(!e.hasClass("active")){if(this.$indicators.length&&(this.$indicators.find(".active").removeClass("active"),this.$element.one("slid.bs.carousel",function(){var b=a(i.$indicators.children()[i.getActiveIndex()]);b&&b.addClass("active")})),a.support.transition&&this.$element.hasClass("slide")){if(this.$element.trigger(j),j.isDefaultPrevented())return;e.addClass(b),e[0].offsetWidth,d.addClass(g),e.addClass(g),d.one(a.support.transition.end,function(){e.removeClass([b,g].join(" ")).addClass("active"),d.removeClass(["active",g].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger("slid.bs.carousel")},0)}).emulateTransitionEnd(600)}else{if(this.$element.trigger(j),j.isDefaultPrevented())return;d.removeClass("active"),e.addClass("active"),this.sliding=!1,this.$element.trigger("slid.bs.carousel")}return f&&this.cycle(),this}};var c=a.fn.carousel;a.fn.carousel=function(c){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c),g="string"==typeof c?c:f.slide;e||d.data("bs.carousel",e=new b(this,f)),"number"==typeof c?e.to(c):g?e[g]():f.interval&&e.pause().cycle()})},a.fn.carousel.Constructor=b,a.fn.carousel.noConflict=function(){return a.fn.carousel=c,this},a(document).on("click.bs.carousel.data-api","[data-slide], [data-slide-to]",function(b){var c,d=a(this),e=a(d.attr("data-target")||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"")),f=a.extend({},e.data(),d.data()),g=d.attr("data-slide-to");g&&(f.interval=!1),e.carousel(f),(g=d.attr("data-slide-to"))&&e.data("bs.carousel").to(g),b.preventDefault()}),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var b=a(this);b.carousel(b.data())})})}(jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d),this.transitioning=null,this.options.parent&&(this.$parent=a(this.options.parent)),this.options.toggle&&this.toggle()};b.DEFAULTS={toggle:!0},b.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},b.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b=a.Event("show.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.$parent&&this.$parent.find("> .panel > .in");if(c&&c.length){var d=c.data("bs.collapse");if(d&&d.transitioning)return;c.collapse("hide"),d||c.data("bs.collapse",null)}var e=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[e](0),this.transitioning=1;var f=function(){this.$element.removeClass("collapsing").addClass("in")[e]("auto"),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return f.call(this);var g=a.camelCase(["scroll",e].join("-"));this.$element.one(a.support.transition.end,a.proxy(f,this)).emulateTransitionEnd(350)[e](this.$element[0][g])}}},b.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse").removeClass("in"),this.transitioning=1;var d=function(){this.transitioning=0,this.$element.trigger("hidden.bs.collapse").removeClass("collapsing").addClass("collapse")};return a.support.transition?(this.$element[c](0).one(a.support.transition.end,a.proxy(d,this)).emulateTransitionEnd(350),void 0):d.call(this)}}},b.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()};var c=a.fn.collapse;a.fn.collapse=function(c){return this.each(function(){var d=a(this),e=d.data("bs.collapse"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c);e||d.data("bs.collapse",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.collapse.Constructor=b,a.fn.collapse.noConflict=function(){return a.fn.collapse=c,this},a(document).on("click.bs.collapse.data-api","[data-toggle=collapse]",function(b){var c,d=a(this),e=d.attr("data-target")||b.preventDefault()||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,""),f=a(e),g=f.data("bs.collapse"),h=g?"toggle":d.data(),i=d.attr("data-parent"),j=i&&a(i);g&&g.transitioning||(j&&j.find('[data-toggle=collapse][data-parent="'+i+'"]').not(d).addClass("collapsed"),d[f.hasClass("in")?"addClass":"removeClass"]("collapsed")),f.collapse(h)})}(jQuery),+function(a){"use strict";function b(){a(d).remove(),a(e).each(function(b){var d=c(a(this));d.hasClass("open")&&(d.trigger(b=a.Event("hide.bs.dropdown")),b.isDefaultPrevented()||d.removeClass("open").trigger("hidden.bs.dropdown"))})}function c(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}var d=".dropdown-backdrop",e="[data-toggle=dropdown]",f=function(b){a(b).on("click.bs.dropdown",this.toggle)};f.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=c(e),g=f.hasClass("open");if(b(),!g){if("ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(''}),b.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),b.prototype.constructor=b,b.prototype.getDefaults=function(){return b.DEFAULTS},b.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content")[this.options.html?"html":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},b.prototype.hasContent=function(){return this.getTitle()||this.getContent()},b.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},b.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")},b.prototype.tip=function(){return this.$tip||(this.$tip=a(this.options.template)),this.$tip};var c=a.fn.popover;a.fn.popover=function(c){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof c&&c;e||d.data("bs.popover",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.popover.Constructor=b,a.fn.popover.noConflict=function(){return a.fn.popover=c,this}}(jQuery),+function(a){"use strict";function b(c,d){var e,f=a.proxy(this.process,this);this.$element=a(c).is("body")?a(window):a(c),this.$body=a("body"),this.$scrollElement=this.$element.on("scroll.bs.scroll-spy.data-api",f),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||(e=a(c).attr("href"))&&e.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.offsets=a([]),this.targets=a([]),this.activeTarget=null,this.refresh(),this.process()}b.DEFAULTS={offset:10},b.prototype.refresh=function(){var b=this.$element[0]==window?"offset":"position";this.offsets=a([]),this.targets=a([]);var c=this;this.$body.find(this.selector).map(function(){var d=a(this),e=d.data("target")||d.attr("href"),f=/^#\w/.test(e)&&a(e);return f&&f.length&&[[f[b]().top+(!a.isWindow(c.$scrollElement.get(0))&&c.$scrollElement.scrollTop()),e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){c.offsets.push(this[0]),c.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,d=c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(b>=d)return g!=(a=f.last()[0])&&this.activate(a);for(a=e.length;a--;)g!=f[a]&&b>=e[a]&&(!e[a+1]||b<=e[a+1])&&this.activate(f[a])},b.prototype.activate=function(b){this.activeTarget=b,a(this.selector).parents(".active").removeClass("active");var c=this.selector+'[data-target="'+b+'"],'+this.selector+'[href="'+b+'"]',d=a(c).parents("li").addClass("active");d.parent(".dropdown-menu").length&&(d=d.closest("li.dropdown").addClass("active")),d.trigger("activate.bs.scrollspy")};var c=a.fn.scrollspy;a.fn.scrollspy=function(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.scrollspy.Constructor=b,a.fn.scrollspy.noConflict=function(){return a.fn.scrollspy=c,this},a(window).on("load",function(){a('[data-spy="scroll"]').each(function(){var b=a(this);b.scrollspy(b.data())})})}(jQuery),+function(a){"use strict";var b=function(b){this.element=a(b)};b.prototype.show=function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.data("target");if(d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),!b.parent("li").hasClass("active")){var e=c.find(".active:last a")[0],f=a.Event("show.bs.tab",{relatedTarget:e});if(b.trigger(f),!f.isDefaultPrevented()){var g=a(d);this.activate(b.parent("li"),c),this.activate(g,g.parent(),function(){b.trigger({type:"shown.bs.tab",relatedTarget:e})})}}},b.prototype.activate=function(b,c,d){function e(){f.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),b.addClass("active"),g?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu")&&b.closest("li.dropdown").addClass("active"),d&&d()}var f=c.find("> .active"),g=d&&a.support.transition&&f.hasClass("fade");g?f.one(a.support.transition.end,e).emulateTransitionEnd(150):e(),f.removeClass("in")};var c=a.fn.tab;a.fn.tab=function(c){return this.each(function(){var d=a(this),e=d.data("bs.tab");e||d.data("bs.tab",e=new b(this)),"string"==typeof c&&e[c]()})},a.fn.tab.Constructor=b,a.fn.tab.noConflict=function(){return a.fn.tab=c,this},a(document).on("click.bs.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(b){b.preventDefault(),a(this).tab("show")})}(jQuery),+function(a){"use strict";var b=function(c,d){this.options=a.extend({},b.DEFAULTS,d),this.$window=a(window).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(c),this.affixed=this.unpin=null,this.checkPosition()};b.RESET="affix affix-top affix-bottom",b.DEFAULTS={offset:0},b.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},b.prototype.checkPosition=function(){if(this.$element.is(":visible")){var c=a(document).height(),d=this.$window.scrollTop(),e=this.$element.offset(),f=this.options.offset,g=f.top,h=f.bottom;"object"!=typeof f&&(h=g=f),"function"==typeof g&&(g=f.top()),"function"==typeof h&&(h=f.bottom());var i=null!=this.unpin&&d+this.unpin<=e.top?!1:null!=h&&e.top+this.$element.height()>=c-h?"bottom":null!=g&&g>=d?"top":!1;this.affixed!==i&&(this.unpin&&this.$element.css("top",""),this.affixed=i,this.unpin="bottom"==i?e.top-d:null,this.$element.removeClass(b.RESET).addClass("affix"+(i?"-"+i:"")),"bottom"==i&&this.$element.offset({top:document.body.offsetHeight-h-this.$element.height()}))}};var c=a.fn.affix;a.fn.affix=function(c){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof c&&c;e||d.data("bs.affix",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.affix.Constructor=b,a.fn.affix.noConflict=function(){return a.fn.affix=c,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var b=a(this),c=b.data();c.offset=c.offset||{},c.offsetBottom&&(c.offset.bottom=c.offsetBottom),c.offsetTop&&(c.offset.top=c.offsetTop),b.affix(c)})})}(jQuery); -------------------------------------------------------------------------------- /custom_theme/readable/js/prettify-1.0.min.js: -------------------------------------------------------------------------------- 1 | var q=null;window.PR_SHOULD_USE_CONTINUATION=!0; 2 | (function(){function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92)return f;var b=a.charAt(1);return(f=r[b])?f:"0"<=b&&b<="7"?parseInt(a.substring(1),8):b==="u"||b==="x"?parseInt(a.substring(2),16):a.charCodeAt(1)}function e(a){if(a<32)return(a<16?"\\x0":"\\x")+a.toString(16);a=String.fromCharCode(a);if(a==="\\"||a==="-"||a==="["||a==="]")a="\\"+a;return a}function h(a){for(var f=a.substring(1,a.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),a= 3 | [],b=[],o=f[0]==="^",c=o?1:0,i=f.length;c122||(d<65||j>90||b.push([Math.max(65,j)|32,Math.min(d,90)|32]),d<97||j>122||b.push([Math.max(97,j)&-33,Math.min(d,122)&-33]))}}b.sort(function(a,f){return a[0]-f[0]||f[1]-a[1]});f=[];j=[NaN,NaN];for(c=0;ci[0]&&(i[1]+1>i[0]&&b.push("-"),b.push(e(i[1])));b.push("]");return b.join("")}function y(a){for(var f=a.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=f.length,d=[],c=0,i=0;c=2&&a==="["?f[c]=h(j):a!=="\\"&&(f[c]=j.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return f.join("")}for(var t=0,s=!1,l=!1,p=0,d=a.length;p=5&&"lang-"===b.substring(0,5))&&!(o&&typeof o[1]==="string"))c=!1,b="src";c||(r[f]=b)}i=d;d+=f.length;if(c){c=o[1];var j=f.indexOf(c),k=j+c.length;o[2]&&(k=f.length-o[2].length,j=k-c.length);b=b.substring(5);B(l+i,f.substring(0,j),e,p);B(l+i+j,c,C(b,c),p);B(l+i+k,f.substring(k),e,p)}else p.push(l+i,b)}a.e=p}var h={},y;(function(){for(var e=a.concat(m), 9 | l=[],p={},d=0,g=e.length;d=0;)h[n.charAt(k)]=r;r=r[1];n=""+r;p.hasOwnProperty(n)||(l.push(r),p[n]=q)}l.push(/[\S\s]/);y=L(l)})();var t=m.length;return e}function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?m.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/, 10 | q,"'\"`"]):m.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&e.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var h=a.hashComments;h&&(a.cStyleComments?(h>1?m.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):m.push(["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),e.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,q])):m.push(["com",/^#[^\n\r]*/, 11 | q,"#"]));a.cStyleComments&&(e.push(["com",/^\/\/[^\n\r]*/,q]),e.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));a.regexLiterals&&e.push(["lang-regex",/^(?:^^\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\(|\*|\*=|\+=|,|-=|->|\/|\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\^=|\^\^|\^\^=|{|\||\|=|\|\||\|\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(h=a.types)&&e.push(["typ",h]);a=(""+a.keywords).replace(/^ | $/g, 12 | "");a.length&&e.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),q]);m.push(["pln",/^\s+/,q," \r\n\t\xa0"]);e.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,q],["pun",/^.[^\s\w"-$'./@\\`]*/,q]);return x(m,e)}function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.className))break;if("BR"===a.nodeName)h(a), 13 | a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)e(a);break;case 3:case 4:if(p){var b=a.nodeValue,d=b.match(t);if(d){var c=b.substring(0,d.index);a.nodeValue=c;(b=b.substring(d.index+d[0].length))&&a.parentNode.insertBefore(s.createTextNode(b),a.nextSibling);h(a);c||a.parentNode.removeChild(a)}}}}function h(a){function b(a,d){var e=d?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),g=a.nextSibling;f.appendChild(e);for(var h=g;h;h=g)g=h.nextSibling,f.appendChild(h)}return e} 14 | for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),e;(e=a.parentNode)&&e.nodeType===1;)a=e;d.push(a)}var k=/(?:^|\s)nocode(?:\s|$)/,t=/\r\n?|\n/,s=a.ownerDocument,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=s.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);for(l=s.createElement("LI");a.firstChild;)l.appendChild(a.firstChild);for(var d=[l],g=0;g=0;){var h=m[e];A.hasOwnProperty(h)?window.console&&console.warn("cannot override language handler %s",h):A[h]=a}}function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*=o&&(h+=2);e>=c&&(a+=2)}}catch(w){"console"in window&&console.log(w&&w.stack?w.stack:w)}}var v=["break,continue,do,else,for,if,return,while"],w=[[v,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"], 18 | "catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],F=[w,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],G=[w,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"], 19 | H=[G,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"],w=[w,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],I=[v,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"], 20 | J=[v,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],v=[v,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],K=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/,N=/\S/,O=u({keywords:[F,H,w,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+ 21 | I,J,v],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};k(O,["default-code"]);k(x([],[["pln",/^[^]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]), 22 | ["default-markup","htm","html","mxml","xhtml","xml","xsl"]);k(x([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css", 23 | /^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);k(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);k(u({keywords:F,hashComments:!0,cStyleComments:!0,types:K}),["c","cc","cpp","cxx","cyc","m"]);k(u({keywords:"null,true,false"}),["json"]);k(u({keywords:H,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:K}),["cs"]);k(u({keywords:G,cStyleComments:!0}),["java"]);k(u({keywords:v,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);k(u({keywords:I,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}), 24 | ["cv","py"]);k(u({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);k(u({keywords:J,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb"]);k(u({keywords:w,cStyleComments:!0,regexLiterals:!0}),["js"]);k(u({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes", 25 | hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);k(x([],[["str",/^[\S\s]+/]]),["regex"]);window.prettyPrintOne=function(a,m,e){var h=document.createElement("PRE");h.innerHTML=a;e&&D(h,e);E({g:m,i:e,h:h});return h.innerHTML};window.prettyPrint=function(a){function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;p=0){var k=k.match(g),f,b;if(b= 26 | !k){b=n;for(var o=void 0,c=b.firstChild;c;c=c.nextSibling)var i=c.nodeType,o=i===1?o?b:c:i===3?N.test(c.nodeValue)?b:o:o;b=(f=o===b?void 0:o)&&"CODE"===f.tagName}b&&(k=f.className.match(g));k&&(k=k[1]);b=!1;for(o=n.parentNode;o;o=o.parentNode)if((o.tagName==="pre"||o.tagName==="code"||o.tagName==="xmp")&&o.className&&o.className.indexOf("prettyprint")>=0){b=!0;break}b||((b=(b=n.className.match(/\blinenums\b(?::(\d+))?/))?b[1]&&b[1].length?+b[1]:!0:!1)&&D(n,b),d={g:k,h:n,i:b},E(d))}}p 2 |
3 | 4 | 5 | 17 | 18 | 19 | 73 |
74 | 75 | -------------------------------------------------------------------------------- /custom_theme/readable/toc.html: -------------------------------------------------------------------------------- 1 | 11 | -------------------------------------------------------------------------------- /custom_theme/simplex/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | {% if page_description %}{% endif %} 8 | {% if site_author %}{% endif %} 9 | {% if canonical_url %}{% endif %} 10 | {% if favicon %} 11 | {% else %}{% endif %} 12 | 13 | {{ page_title }} 14 | 15 | 16 | 17 | 18 | 19 | {%- for path in extra_css %} 20 | 21 | {%- endfor %} 22 | 23 | 24 | 28 | 29 | 30 | 31 | 32 | {% include "nav.html" %} 33 | 34 |
35 |
{% include "toc.html" %}
36 |
{% include "content.html" %}
37 |
38 | 39 |
40 |
41 |

Copyright © 2014, Wang Chen.

42 |

Documentation built with MkDocs.

43 |
44 | 45 | {% if include_search %}{% include "search.html" %}{% endif %} 46 | 47 | 48 | 49 | 50 | 51 | {%- for path in extra_javascript %} 52 | 53 | {%- endfor %} 54 | 55 | 56 | -------------------------------------------------------------------------------- /custom_theme/simplex/content.html: -------------------------------------------------------------------------------- 1 | {% if meta.source %} 2 | 7 | {% endif %} 8 | 9 | {{ content }} 10 | -------------------------------------------------------------------------------- /custom_theme/simplex/css/base.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 70px; 3 | } 4 | 5 | ul.nav li.main { 6 | font-weight: bold; 7 | } 8 | 9 | div.col-md-3 { 10 | padding-left: 0; 11 | } 12 | 13 | div.source-links { 14 | float: right; 15 | } 16 | 17 | footer { 18 | margin-top: 30px; 19 | margin-bottom: 10px; 20 | text-align: center; 21 | font-weight: 200; 22 | } 23 | 24 | /* 25 | * Side navigation 26 | * 27 | * Scrollspy and affixed enhanced navigation to highlight sections and secondary 28 | * sections of docs content. 29 | */ 30 | 31 | /* By default it's not affixed in mobile views, so undo that */ 32 | .bs-sidebar.affix { 33 | position: static; 34 | margin-top: 40px; 35 | } 36 | 37 | .bs-sidebar.well { 38 | padding: 0; 39 | } 40 | 41 | /* First level of nav */ 42 | .bs-sidenav { 43 | padding-top: 10px; 44 | padding-bottom: 10px; 45 | border-radius: 5px; 46 | } 47 | 48 | /* All levels of nav */ 49 | .bs-sidebar .nav > li > a { 50 | display: block; 51 | padding: 5px 20px; 52 | } 53 | .bs-sidebar .nav > li > a:hover, 54 | .bs-sidebar .nav > li > a:focus { 55 | text-decoration: none; 56 | border-right: 1px solid; 57 | } 58 | .bs-sidebar .nav > .active > a, 59 | .bs-sidebar .nav > .active:hover > a, 60 | .bs-sidebar .nav > .active:focus > a { 61 | font-weight: bold; 62 | background-color: transparent; 63 | border-right: 1px solid; 64 | } 65 | 66 | /* Nav: second level (shown on .active) */ 67 | .bs-sidebar .nav .nav { 68 | display: none; /* Hide by default, but at >768px, show it */ 69 | margin-bottom: 8px; 70 | } 71 | .bs-sidebar .nav .nav > li > a { 72 | padding-top: 3px; 73 | padding-bottom: 3px; 74 | padding-left: 30px; 75 | font-size: 90%; 76 | } 77 | 78 | /* Show and affix the side nav when space allows it */ 79 | @media (min-width: 992px) { 80 | .bs-sidebar .nav > .active > ul { 81 | display: block; 82 | } 83 | /* Widen the fixed sidebar */ 84 | .bs-sidebar.affix, 85 | .bs-sidebar.affix-bottom { 86 | width: 213px; 87 | } 88 | .bs-sidebar.affix { 89 | position: fixed; /* Undo the static from mobile first approach */ 90 | top: 80px; 91 | } 92 | .bs-sidebar.affix-bottom { 93 | position: absolute; /* Undo the static from mobile first approach */ 94 | } 95 | .bs-sidebar.affix-bottom .bs-sidenav, 96 | .bs-sidebar.affix .bs-sidenav { 97 | margin-bottom: 0; 98 | } 99 | } 100 | @media (min-width: 1200px) { 101 | /* Widen the fixed sidebar again */ 102 | .bs-sidebar.affix-bottom, 103 | .bs-sidebar.affix { 104 | width: 263px; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /custom_theme/simplex/css/prettify-1.0.css: -------------------------------------------------------------------------------- 1 | .com { color: #93a1a1; } 2 | .lit { color: #195f91; } 3 | .pun, .opn, .clo { color: #93a1a1; } 4 | .fun { color: #dc322f; } 5 | .str, .atv { color: #D14; } 6 | .kwd, .prettyprint .tag { color: #1e347b; } 7 | .typ, .atn, .dec, .var { color: teal; } 8 | .pln { color: #48484c; } 9 | 10 | .prettyprint { 11 | padding: 8px; 12 | } 13 | .prettyprint.linenums { 14 | -webkit-box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0; 15 | -moz-box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0; 16 | box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0; 17 | } 18 | 19 | /* Specify class=linenums on a pre to get line numbering */ 20 | ol.linenums { 21 | margin: 0 0 0 33px; /* IE indents via margin-left */ 22 | } 23 | ol.linenums li { 24 | padding-left: 12px; 25 | color: #bebec5; 26 | line-height: 20px; 27 | text-shadow: 0 1px 0 #fff; 28 | } 29 | -------------------------------------------------------------------------------- /custom_theme/simplex/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/custom_theme/simplex/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /custom_theme/simplex/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/custom_theme/simplex/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /custom_theme/simplex/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/custom_theme/simplex/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /custom_theme/simplex/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/custom_theme/simplex/img/favicon.ico -------------------------------------------------------------------------------- /custom_theme/simplex/js/base.js: -------------------------------------------------------------------------------- 1 | /* center the image */ 2 | $("img").parent().css("text-align", "center"); 3 | 4 | /* Prettyify */ 5 | $( document ).ready(function() { 6 | prettyPrint(); 7 | }); 8 | 9 | 10 | /* Scrollspy */ 11 | var navHeight = $('.navbar').outerHeight(true) + 10 12 | 13 | $('body').scrollspy({ 14 | target: '.bs-sidebar', 15 | offset: navHeight 16 | }) 17 | 18 | 19 | /* Prevent disabled links from causing a page reload */ 20 | $("li.disabled a").click(function() { 21 | event.preventDefault(); 22 | }); 23 | 24 | 25 | /* Adjust the scroll height of anchors to compensate for the fixed navbar */ 26 | window.disableShift = false; 27 | var shiftWindow = function() { 28 | if (window.disableShift) { 29 | window.disableShift = false; 30 | } else { 31 | /* If we're at the bottom of the page, don't erronously scroll up */ 32 | var scrolledToBottomOfPage = ( 33 | (window.innerHeight + window.scrollY) >= document.body.offsetHeight 34 | ); 35 | if (!scrolledToBottomOfPage) { 36 | scrollBy(0, -60); 37 | }; 38 | }; 39 | }; 40 | if (location.hash) {shiftWindow();} 41 | window.addEventListener("hashchange", shiftWindow); 42 | 43 | 44 | /* Deal with clicks on nav links that do not change the current anchor link. */ 45 | $("ul.nav a" ).click(function() { 46 | var href = this.href; 47 | var suffix = location.hash; 48 | var matchesCurrentHash = (href.indexOf(suffix, href.length - suffix.length) !== -1); 49 | if (location.hash && matchesCurrentHash) { 50 | /* Force a single 'hashchange' event to occur after the click event */ 51 | window.disableShift = true; 52 | location.hash=''; 53 | }; 54 | }); 55 | -------------------------------------------------------------------------------- /custom_theme/simplex/js/prettify-1.0.min.js: -------------------------------------------------------------------------------- 1 | var q=null;window.PR_SHOULD_USE_CONTINUATION=!0; 2 | (function(){function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92)return f;var b=a.charAt(1);return(f=r[b])?f:"0"<=b&&b<="7"?parseInt(a.substring(1),8):b==="u"||b==="x"?parseInt(a.substring(2),16):a.charCodeAt(1)}function e(a){if(a<32)return(a<16?"\\x0":"\\x")+a.toString(16);a=String.fromCharCode(a);if(a==="\\"||a==="-"||a==="["||a==="]")a="\\"+a;return a}function h(a){for(var f=a.substring(1,a.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),a= 3 | [],b=[],o=f[0]==="^",c=o?1:0,i=f.length;c122||(d<65||j>90||b.push([Math.max(65,j)|32,Math.min(d,90)|32]),d<97||j>122||b.push([Math.max(97,j)&-33,Math.min(d,122)&-33]))}}b.sort(function(a,f){return a[0]-f[0]||f[1]-a[1]});f=[];j=[NaN,NaN];for(c=0;ci[0]&&(i[1]+1>i[0]&&b.push("-"),b.push(e(i[1])));b.push("]");return b.join("")}function y(a){for(var f=a.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=f.length,d=[],c=0,i=0;c=2&&a==="["?f[c]=h(j):a!=="\\"&&(f[c]=j.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return f.join("")}for(var t=0,s=!1,l=!1,p=0,d=a.length;p=5&&"lang-"===b.substring(0,5))&&!(o&&typeof o[1]==="string"))c=!1,b="src";c||(r[f]=b)}i=d;d+=f.length;if(c){c=o[1];var j=f.indexOf(c),k=j+c.length;o[2]&&(k=f.length-o[2].length,j=k-c.length);b=b.substring(5);B(l+i,f.substring(0,j),e,p);B(l+i+j,c,C(b,c),p);B(l+i+k,f.substring(k),e,p)}else p.push(l+i,b)}a.e=p}var h={},y;(function(){for(var e=a.concat(m), 9 | l=[],p={},d=0,g=e.length;d=0;)h[n.charAt(k)]=r;r=r[1];n=""+r;p.hasOwnProperty(n)||(l.push(r),p[n]=q)}l.push(/[\S\s]/);y=L(l)})();var t=m.length;return e}function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?m.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/, 10 | q,"'\"`"]):m.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&e.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var h=a.hashComments;h&&(a.cStyleComments?(h>1?m.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):m.push(["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),e.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,q])):m.push(["com",/^#[^\n\r]*/, 11 | q,"#"]));a.cStyleComments&&(e.push(["com",/^\/\/[^\n\r]*/,q]),e.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));a.regexLiterals&&e.push(["lang-regex",/^(?:^^\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\(|\*|\*=|\+=|,|-=|->|\/|\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\^=|\^\^|\^\^=|{|\||\|=|\|\||\|\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(h=a.types)&&e.push(["typ",h]);a=(""+a.keywords).replace(/^ | $/g, 12 | "");a.length&&e.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),q]);m.push(["pln",/^\s+/,q," \r\n\t\xa0"]);e.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,q],["pun",/^.[^\s\w"-$'./@\\`]*/,q]);return x(m,e)}function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.className))break;if("BR"===a.nodeName)h(a), 13 | a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)e(a);break;case 3:case 4:if(p){var b=a.nodeValue,d=b.match(t);if(d){var c=b.substring(0,d.index);a.nodeValue=c;(b=b.substring(d.index+d[0].length))&&a.parentNode.insertBefore(s.createTextNode(b),a.nextSibling);h(a);c||a.parentNode.removeChild(a)}}}}function h(a){function b(a,d){var e=d?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),g=a.nextSibling;f.appendChild(e);for(var h=g;h;h=g)g=h.nextSibling,f.appendChild(h)}return e} 14 | for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),e;(e=a.parentNode)&&e.nodeType===1;)a=e;d.push(a)}var k=/(?:^|\s)nocode(?:\s|$)/,t=/\r\n?|\n/,s=a.ownerDocument,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=s.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);for(l=s.createElement("LI");a.firstChild;)l.appendChild(a.firstChild);for(var d=[l],g=0;g=0;){var h=m[e];A.hasOwnProperty(h)?window.console&&console.warn("cannot override language handler %s",h):A[h]=a}}function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*=o&&(h+=2);e>=c&&(a+=2)}}catch(w){"console"in window&&console.log(w&&w.stack?w.stack:w)}}var v=["break,continue,do,else,for,if,return,while"],w=[[v,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"], 18 | "catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],F=[w,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],G=[w,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"], 19 | H=[G,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"],w=[w,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],I=[v,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"], 20 | J=[v,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],v=[v,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],K=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/,N=/\S/,O=u({keywords:[F,H,w,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+ 21 | I,J,v],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};k(O,["default-code"]);k(x([],[["pln",/^[^]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]), 22 | ["default-markup","htm","html","mxml","xhtml","xml","xsl"]);k(x([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css", 23 | /^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);k(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);k(u({keywords:F,hashComments:!0,cStyleComments:!0,types:K}),["c","cc","cpp","cxx","cyc","m"]);k(u({keywords:"null,true,false"}),["json"]);k(u({keywords:H,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:K}),["cs"]);k(u({keywords:G,cStyleComments:!0}),["java"]);k(u({keywords:v,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);k(u({keywords:I,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}), 24 | ["cv","py"]);k(u({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);k(u({keywords:J,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb"]);k(u({keywords:w,cStyleComments:!0,regexLiterals:!0}),["js"]);k(u({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes", 25 | hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);k(x([],[["str",/^[\S\s]+/]]),["regex"]);window.prettyPrintOne=function(a,m,e){var h=document.createElement("PRE");h.innerHTML=a;e&&D(h,e);E({g:m,i:e,h:h});return h.innerHTML};window.prettyPrint=function(a){function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;p=0){var k=k.match(g),f,b;if(b= 26 | !k){b=n;for(var o=void 0,c=b.firstChild;c;c=c.nextSibling)var i=c.nodeType,o=i===1?o?b:c:i===3?N.test(c.nodeValue)?b:o:o;b=(f=o===b?void 0:o)&&"CODE"===f.tagName}b&&(k=f.className.match(g));k&&(k=k[1]);b=!1;for(o=n.parentNode;o;o=o.parentNode)if((o.tagName==="pre"||o.tagName==="code"||o.tagName==="xmp")&&o.className&&o.className.indexOf("prettyprint")>=0){b=!0;break}b||((b=(b=n.className.match(/\blinenums\b(?::(\d+))?/))?b[1]&&b[1].length?+b[1]:!0:!1)&&D(n,b),d={g:k,h:n,i:b},E(d))}}p 2 |
3 | 4 | 5 | 17 | 18 | 19 | 74 |
75 | 76 | -------------------------------------------------------------------------------- /custom_theme/simplex/toc.html: -------------------------------------------------------------------------------- 1 | 11 | -------------------------------------------------------------------------------- /docs/big-refactorings.md: -------------------------------------------------------------------------------- 1 | ## 梳理并分解继承体系 2 | -------------------- 3 | 4 | 某个继承体系(inheritance hierarchy )同时承担两项责任。 5 | 6 | ***建立两个继承体系,并通过委托关系(delegation)让其中一个可以调用另一个。*** 7 | 8 | ![](../images/12fig01.gif) 9 | 10 | **动机(Motivation)** 11 | 12 | 继承是个好东西,它使你得以在subclass 中写出明显「压缩过」(compressed)的 代码。函数的重要性可能并不和它的大小成比例——在继承体系之中尤然。 13 | 14 | 不过,先别急着为这个强大的工具欢呼雀跃,因为继承也很容易被误用,并且这种误用还很容易在开发人员之间蔓延。今天你为了一项小小任务而加入一个小小的subclass ;明天又为同样任务在继承体系的另一个地方加入另一个subclass 。一个星期(或者一个月或者一年)之后,你就会发现自己身陷泥淖,而且连一根拐杖都没有。 15 | 16 | 混乱的继承体系是一个严重的问题,因为它会导致重复代码,而后者正是程序员生涯的致命毒药。它还会使修改变得困难,因为「特定种类」的问题的解决策略被分散到了整个继承体系。最终,你的代码将非常难以理解。你无法简单地说:『这就 是我的继承体系,它能计算结果』,而必须说:『它会计算出结果……呃,这些是 用以表现不同表格形式的subclasses ,每个subclass 又有一些subclasses 针对不同的 国家。』 17 | 18 | 要指出「某个继承体系承担了两项不同的责任」并不困难:如果继承体系中的某一特定层级上的所有classes,其subclass 名称都以相同的形容词开始,那么这个体系很可能就是承担着两项不同的责任。 19 | 20 | **作法(Mechanics)** 21 | 22 | - 首先识别出继承体系所承担的不同责任,然后建立一个二维表格(或者三维乃至四维表格,如果你的继承体系够混乱而你的绘图工具够酷的话),并以坐标轴标示出不同的任务。我们将重复运用本重构,处理两个或两个以上的维度〔当然,每次只处理一个维度〉。 23 | - 判断哪一项责任更重要些,并准备将它留在当前的继承体系中。准备将另一项责任移到另一个继承体系中。 24 | - 使用[提炼类](moving-features-between-objects.md#_1) 从当前的superclass 提炼出一个新class ,用以表示重要性稍低的责任,并在原superclass 中添加一个instance 变量(不是static 变量〕,用以保存新建class 的实体。 25 | - 对应于原继承体系中的每个subclass ,创建上述新class 的一个个subclasses 。在原继承体系的subclasses 中,将前一步骤所添加的instance 变量初始化为新建subclass 的实体。 26 | - 针对原继承体系中每个subclass ,使用[搬移函数](moving-features-between-objects.md#_3) 将其中的行为搬移到与之对应的新建subclass 中。 27 | - 当原继承体系中的某个subclass 不再有任何代码时,就将它去除。 28 | - 重复以上步骤,直到原继承体系中的所有subclass 都被处理过为止。观察新继承体系,看看是否有可能对它实施其他重构手法,例如[函数上移](dealing-with-generalization.md#_8)或[值域上移](dealing-with-generalization.md#_7)。 29 | 30 | 31 | **范例:(Example)** 32 | 33 | 让我们来看一个混乱的继承体系(如图12.1)。 34 | 35 | ![](../images/12fig02.png) 36 | 37 | 图12.1 一个混乱的继承体系 38 | 39 | 40 | 41 | 这个继承体系之所以混乱,因为一开始Deal class 只被用来显示单笔交易。后来,某个人突发奇想地用它来显示一张交易表格。只需飞快建立一个ActiveDeal subclass 再加上一点点经验,不必做太多工作就可以显示一张表格了。哦,还要「被动交易(PassiveDeal)」表格是吗?没问题,再加一个subclass 就行了。 42 | 43 | 两个月过去,表格相关代码变得愈来愈复杂,你却没有一个好地方可以放它们,因为时间太紧了。咳,老戏码!现在你将很难向系统加入新种交易,因为「交易处理逻辑」与「数据显示逻辑」已经「你中有我,我中有你」了。 44 | 45 | 按照本重构提出的处方笺,第一步工作是识别出这个继承体系所承担的各项责任。 这个继承体系的职责之一是捕捉不同交易种类间的变化(差异〕,职责之二是捕捉 不同显示风格之间的变化(差异〕。因此,我们可以得到下列表格: 46 | 47 | Deal | Active Deal | Passive Deal 48 | ---- | ----------- | ------------ 49 | Tabular 50 | 51 | 52 | 下一步要判断哪一项职责更重要。很明显「交易种类」比「显示风格」重要,因此我们把「交易种类」留在原地,把「显示风格」提炼到另一个继承体系中。不过,实际工作中,我们可能需要将「代码较多」的职责留在原地,这样一来需要搬移的 代码数量会比较少。 53 | 54 | 然后,我们应该使用[提炼类](moving-features-between-objects.md#_1) 提炼出一个单独的PresentationStyle class 用以表示「显示风格」(如图12.2)。 55 | 56 | ![](../images/12fig03.gif) 57 | 58 | 图12.2 添加PresentationStyle ,用以表示「显示风格」 59 | 60 | 接下来我们需要针对原继承体系中的每个subclass ,建立PresentationStyle 的一个个subclasses (如图 12.3),并将Deal class之中用来保存PresentationStyle 实体的那个instance 变量初始化为适当的subclass 实体: 61 | 62 | ![](../images/12fig04.gif) 63 | 64 | 图12.3 为PresentationStyle 添加subclasses 65 | 66 | 67 | ```java 68 | ActiveDeal constructor 69 | ...presentation= new SingleActivePresentationStyle();... 70 | ``` 71 | 72 | 73 | 你可能会说:『这不是比原先的classes 数量还多了吗?难道这还能让我的生活更舒服?』生活往往如此:以退为进,走得更远。对一个纠结成团的继承体系来说,被提炼出来的另一个继承体系几乎总是可以再戏剧性地大量简化。不过,比较安全的态度是一次一小步,不要过于躁进。 74 | 75 | 现在,我们要使用 [搬移函数](moving-features-between-objects.md#_3) 和 [搬移值域](moving-features-between-objects.md#_2),将Deal subclass 中[与显示逻辑相关」的函数和变量搬移到PresentationStyle 相应的subclasses 去。我们想不出什么好办法来模拟这个过程,只好请你自己想像。总之,这个步骤完成后,TabularActiveDeal 和 TabularPassiveDeal不再有任何代码,因此我们将它们移除(如图12.4)。 76 | 77 | 78 | ![](../images/12fig05.gif) 79 | 80 | 图12.4 与表格相关的 Deal subclass 都移除了 81 | 82 | 83 | 两项职责被分割之后,我们可以分别简化两个继承体系。一旦本重构完成,我们总是能够大大简化被提炼出来的新继承体系,而且通常还可以简化原继承体系。 84 | 85 | 下一步,我们将摆脱「显示风格」中的主动(active)与被动(passive)区别,如图 12.5。 86 | 87 | 88 | ![](../images/12fig06.gif) 89 | 90 | 图12.5 继承体系被分割了 91 | 92 | 93 | 就连「单一显示」和「表格显示」之间的区别,都可以运用若干变量值来捕捉,根本不需要为它们建立subclasses (如图12.6〕。 94 | 95 | 96 | ![](../images/12fig07.gif) 97 | 98 | 图12.6「显示风格」(presentation style)之间的差异可以使用一些变量来表现 99 | 100 | ## 将过程化设计转化为对象设计 101 | ------------------------- 102 | 103 | 104 | 你手上有一些代码,以传统的过程化风格(procedural style)写就。 105 | 106 | **将数据记录(data records)9 变成对象,将行为分开,并将行为移入相关对象之中。** 107 | 108 | 9译注:这里所谓的记录(record),是指像C struct 那样的结构。 109 | 110 | 111 | ![](../images/12fig08.gif) 112 | 113 | 114 | **动机(Motivation)** 115 | 116 | 有一次,我们的一位客户,在项目开始时,给开发者提出了两条必须遵守的条件:(1)必须使用Java;(2)不能使用对象。 117 | 118 | 这让我们忍俊不禁。尽管是面向对象语言,「使用对象」可远不仅仅是「调用构造函数」而已。对象的使用也需要花时间去学习。往往你会面对一些过程化风格(procedural style)的代码所带来的问题,并因而希望它们变得更面向对象一些。 典型的情况是:class 中有着长长的过程化函数和极少的数据,以及所谓的「哑数据 对象」——除了数据访问函数(accessors)外没有其他任何函数。如果你要转换的是一个纯粹的过程化程序(procedural program),可能连这些东西都没有。 119 | 120 | 我们并不是说绝对不应该出现「只有行为而几乎没有数据」的对象。在Strategy 模式中,我们常常使用一些小型的strategy 对象来改变宿主对象的行为,这些小型的strategy 对象就是「只有行为而没有数据」。但是这样的对象通常比较小,而且只有在我们特规需要灵活性的时候,才会使用它们。 121 | 122 | 123 | 124 | **作法(Mechanics)** 125 | - 针对每一个记录型别(record type),将它转变为「只含访问函数」的「哑数据对象』(dump data object )。 126 | - 如果你的数据来自关系式数据库(relational database),就把数据库中的每个表(table)变成一个「哑数据对象」。 127 | - 针对每一处过程化风格,将该处的代码提炼到一个独立class 中。 128 | - 你可以把提炼所得的class 做成一个Singleton(单件;为了方便重新初始化),或是把提炼所得的函数声明为static。 129 | - 针对每一个长长的程序(procedure),实施[提炼函数](composing-methods.md#_1)及其他相关重构,将它分解。再以[搬移函数](moving-features-between-objects.md#_3) 将分解后的函数分别移到它所相关的哑数据类(dump data class)中。 130 | - 重复上述步骤,直到原始中的所有函数都被移除。如果原始是一个完全过程化(purely procedural〕的class ,将它拿掉将大快人心。 131 | 132 | 133 | **范例:(Example)** 134 | 135 | 第1章的范例很好地展示了[将过程化设计转化为对象设计](big-refactorings.md#_2),尤其是第一阶段(对statement() 函数的分解和安置)。完成这项重构之后,你就拥有了一个「聪明的」数据对象,可以对它进行其他种重构了。 136 | 137 | ## 将领域和表述/显示分离 138 | ----------------------- 139 | 140 | (译注:本节保留domain-,presentation-logic,UI,class,object等英文词) 141 | 142 | 某些GUI class 之中包含了domain logic(领域逻辑)。 143 | 144 | **将domain logic(领域逻辑)分离出来,为它们建立独立的domain class。** 145 | 146 | ![](../images/12fig09.gif) 147 | 148 | 149 | **动机(Motivation)** 150 | 151 | 提到面向对象,就不能不提model-view-controller (MVC,模型-视图-控制器) 模式。在Smalltalk-80环境中,人们以此模式维护GUI(图形用户界面)和domain object (领域对象)间的关系。 152 | 153 | MVC模式的最核心价值在于:它将用户界面代码(即所谓view,视图;亦即现今常说的presentation,表述)和领域逻辑(即所谓model,模型)分离了。presentation class 只含用以处理用户界面的逻辑;domain class不含任何与程序外观相关的代码,只含业务逻辑(business logic)相关代码。将程序中这两块复杂的部分加以分离,程序未来的修改将变得更加容易,同时也使同一业务逻辑(business logic)的多种表述(显示)方式成为可能。那些熟稔面向对象技术的程序员会毫不犹豫地在他们的程序中进行这种分离,并且这种作法也的确证实了它自身的价值。 154 | 155 | 但是,大多数人并没有在设计中采用这种方式来处理GUI。大多数带有client-server GUIs 的环境都釆用双层(two-tier)逻辑设计:数据保存在数据库中,业务逻辑(business logic)放在presentation class 中。这样的环境往往迫使你也倾向这种风格的设计,使你很难把业务逻辑放在其他地方。 156 | 157 | Java 是一个真正意义上的面向对象环境,因此你可以创建内含业务逻辑的非视觉性领域对象(nonvisual domain objects )。但你却还是会经常遇到上述双层风格写就的程序。 158 | 159 | 160 | 161 | **作法(Mechanics)** 162 | 163 | - 为每个窗口(window)建立一个domain class。 164 | - 如果窗口内有一张表格(grid),新建一个class 来表示其中的行(row),再以窗口所对应之domain class 中的一个群集(collection)来容纳所有的row domain objects。 165 | - 检查窗口中的数据。如果数据只被用于UI,就把它留着;如果数据被domain logic使用,而且不显示于窗口上,我们就以Mocve Field 将它搬移到domain class 中;如果数据同时被UI 和domain logic 使用,就对它实施[复制被监视数据](organizing-data.md#_5),使它同时存在于两处,并保持两处之间的同步。 166 | - 检査presentation class 中的逻辑。实施 [提炼函数](composing-methods.md#_1) 将presentation logic 从domain logic 中分开。一旦隔离了domain logic。再运用 [搬移函数](moving-features-between-objects.md#_3) 将它移到domain class。 167 | - 以上步骤完成后,你就拥有了两组彼此分离的classes:presentation classes 用以处理GUI,domain logic 内含所有业务逻辑(business logic)。此时的domain classes 组织可能还不够严谨,更进一步的重构将解决这些问题。 168 | 169 | 170 | 171 | **范例:(Example)** 172 | 173 | 下面是一个商品订购程序。其GUI 如图12.7所示,其presentation class 与图12.8 所示的关系式数据库(relational database)互动。 174 | 175 | ![](../images/12fig10.gif) 176 | 177 | 图12.7 启动程序的用户界面 178 | 179 | ![](../images/12fig11.gif) 180 | 181 | 图12.8 订单程序所用的数据库 182 | 183 | * 所有classes 都是《SQL table》,粗体字表示主键(primary key),《FK》表示外键(foreign keys) 184 | 185 | 186 | 所有行为(包括GUI 和定单处理)都由OrderWindow class 处理。 187 | 188 | 首先建立一个Order class 表示「定单」。然后把Order 和OrderWindow 联系起来, 如图12.9。由于窗口中有一个用以显示定单的表格(grid),所以我们还得建立一个OrderLine,用以表示表格中的每一行(rows)。 189 | 190 | 191 | ![](../images/12fig12.gif) 192 | 193 | 图12.9 OrderWindow class 和Order class 194 | 195 | 196 | 我们将从窗口这边而不是从数据库那边开始重构。当然,一开始就把domain model 建立在数据库基础上,也是一种合理策略,但我们最大的风险源于presentation logic 和domain logic之间的混淆,因此我们首先基于窗口将这些分离出来,然后再考虑对其他地方进行重构。 197 | 198 | 面对这一类程序,在窗口中寻找内嵌的SQL (结构化查询语言)语句,会对你有所帮助,因为SQL 语句获取的数据一定是domain data 。 199 | 200 | 最容易处理的domain data 就是那些不直接显示于GUI 者。本例数据库的Customer table 中有一个Codes 值域,它并不直接显示于GUI,而是被转换为一个更容易被人理解的短语之后再显示。程序中以简单型别(而非AWT组件)如String 保存这个值域值。我们可以安全地使用[搬移值域](moving-features-between-objects.md#_2) 将这个值域移到domain class。 201 | 202 | 对于其他值域,我们就没有这么幸运了,因为它们内含AWT 组件,既显示于窗口, 也被domain object 使用。面对这些值域,我们需要使用[复制被监视数据](organizing-data.md#_5),把一个domain field 放进Order class,又把一个相应的AWT field 放进OrderWindow class。 203 | 204 | 这是一个缓慢的过程,但最终我们还是可以把所有domain logic field 都搬到domain class。进行这一步骤时,你可以试着把所有SQL calls 都移到domain class,这样你就是同时移动了database logic 和domain data。最后,你可以在OrderWindow 中移除import java.sql 之类的语句,这就表示我们的重构告一段落了。在此阶段中 你可能需要大量运用 [提炼函数](composing-methods.md#_1) 和 [搬移函数](moving-features-between-objects.md#_3)。 205 | 206 | 现在,我们拥有的三个classes,如图12.10所示,它们离「组织良好」还有很大的距离。不过这个模型的确已经很好地分离了presentation logic 和domain logic (business logic)。本项重构的进行过程中,你必须时刻留心你的风险来自何方。 如果「presentation logic 和domain logic 混淆」是最大风险,那么就先把它们完全分开,然后才做其他工作;如果其他方面的事情(例如产品定价策略〉更重要,那么就先把那一部分的logic 从窗口提炼出来,并围绕着这个高风险部分进行重构,为它建立合适的结构。反正domain logic 早晚都必须从窗口移出,如果你在处理高风险部分的重构时会遗留某些logic 于窗口之中,没关系,就放手去做吧。 207 | 208 | 209 | ![](../images/12fig13.gif) 210 | 211 | 图12.10 将数据安置(分散)于domain classes 中 212 | 213 | ## 提炼继承体系 214 | -------------- 215 | 216 | 217 | 你有某个class 做了太多(过多〕工作,其中一部分工作是以大量条件式完成的。 218 | 219 | **建立继承体系,以一个subclass 表示一种特殊情况。** 220 | 221 | ![](../images/12fig14.gif) 222 | 223 | **动机(Motivation)** 224 | 225 | 在渐进式设计过程中,常常会有这样的情况:一开始设计者只想「以一个class 实 现一个概念」;但随着设计方案的演化,最后却可能「一个class 实现了两个、三 个乃至十个不同的概念」。一开始,你建立了这个简单的class ;数天或数周之后, 你可能发现:只要加入一个标记(flag)和一两个测试,就可以在另一个环境下使用这个class ;一个月之后你又发现了另一个这样的机会;一年之后,这个class 就完全一团糟了:标记变量和条件式遍布各处。 226 | 227 | 当你遇到这种「瑞士小刀般」的class ,不但能够开瓶开罐、砍小树枝、在演示文稿会上打出激光强调重点……(简直无所不能),你就需要一个好策略(亦即本项重构、将它的各个功能梳理并分开。不过,请注意,只有当条件逻辑(conditional logic)在对象的整个生命期间保持不变,本重构所导入的策略才适用。否则你可能必须在分离各种状况之前先使用[提炼类](moving-features-between-objects.md#_1)。 228 | 229 | [提炼继承体系](big-refactorings.md#_4)是一项大型重构,如果你一天之内不足以完成它,不要因此失去勇气。将一个极度混乱的设计方案梳理出来,可能需要数周甚至数月的时间。 你可以先进行本重构中的一些简易步骤,稍微休息一下,再花数天编写一些明显有生产力的代码。当你领悟到更多东西,再回来继续本项重构的其他步骤——这些步骤将因为你的领悟而显得更加简单明了。 230 | 231 | 232 | 233 | **作法(Mechanics)** 234 | 235 | 我们为你准备了两组重构作法。第一种情况是:你无法确定变异(variations)应该是些什么(也就是说你无法确定原始class 中该有哪些条件逻辑〕。这时候你希望每次一小步地前进: 236 | 237 | 238 | - 鉴别出一种变异(variation)。 239 | - 如果这种变异可能在对象生命期内发生变化,就运用[提炼类](moving-features-between-objects.md#_1) 将它提炼为一个独立的class 。 240 | - 针对这种变异,新建一个subclass ,并对原始class 实施[以工厂函数取代构造函数](making-method-calls-simpler.md#_10)。再修改factory method,令它返回适当的(相应的)subclass 实体。 241 | - 将含有条件逻辑的函数,一次一个,遂一拷贝到subclass ,然后在明确情况下 (注:对subclass 明确,对superclass 不明确!),简化这些函数。 242 | - 如有必要隔离函数中的「条件逻辑」和「非条件逻辑」,可对superclass 实施[提炼函数](composing-methods.md#_1)。 243 | - 重复上述过程,将所有变异(variations;特殊情况)都分离出来,直到可以将superclass 声明为抽象类(abstract class)为止。 244 | - 删除superclass 中的那些「被所有subclasses 覆写」的函数(的本体),并将 它声明为抽象函数(abstract class)。 245 | 246 | 247 | 248 | 如果你非常清楚原始class 会有哪些变异(variations),可以使用另一种作法: 249 | 250 | 251 | - 针对原始class 的每一种变异(variation)建立一个subclass 。 252 | - 使用[以工厂函数取代构造函数](making-method-calls-simpler.md#_10) 将原始过class 的构造函数转变成factory method ,并令它针对每一种变异返回适当的subclass 实体。 253 | 254 | - 如果原始class 中的各种变异是以type code 标示,先使用 [以子类取代型别码](organizing-data.md#_14) ;如果那些变异在对象生命期间会改变, 请使用[以State/Strategy取代型别码](organizing-data.md#statestrategy)。 255 | 256 | - 针对带有条件逻辑的函数,实施 [以多态取代条件式](simplifying-conditional-expressions.md#_6)。如果并非整个函数的行为有所变化,而只是函数一部分有所变化,请先运用 [提炼函数](composing-methods.md#_1) 将变化部分和不变部分隔开来。 257 | 258 | 259 | **范例:(Example)** 260 | 261 | 这里所举的例子是「变异并不明朗」的情况。你可以在 [以子类取代型别码](organizing-data.md#_14)、[以State/Strategy取代型别码](organizing-data.md#statestrategy) 和 [以多态取代条件式](simplifying-conditional-expressions.md#_6) 等重构结果之上,验证「变异已经明朗」的情况下如何使用本项重构。 262 | 263 | 我们以一个电费计算程序为例。这个程序有两个classes :表示「消费者」的Customer 和表示「计费方案」的BillingScheme,如图12.11。 264 | 265 | 266 | ![](../images/12fig15.gif) 267 | 268 | 图12.11 Customer 和BillingScheme 269 | 270 | 271 | BillingScheme 使用大量条件逻辑来计算不同情况(变异)下的费用:冬季和夏季的电价不同,私宅用电、小型企业用电、社会救济(包括残障人士)用电的价格也不同。这些复杂的逻辑导致BillingScheme 变得复杂。 272 | 273 | 第一个步骤是,提炼出条件逻辑中经常出现的某种变异性。本例之中可能是「视用户是否为残障人士」而发生的变化。用于标示这种情况的可能是Customer、 BillingScheme 或其他地方的一个标记变量(flag)。 274 | 275 | 我们针对这种变异建立一个subclass 。为了使用这个subclass ,我们需要确保它被建立并且被使用。因此我们需要处理BillingScheme 构造函数:首先对它实施 [以工厂函数取代构造函数](making-method-calls-simpler.md#_10),然后在所得的factory method 中为残障 人士增加一个条件子句,使它在适当时候返回一个DisablityBillingScheme对象。 276 | 277 | 然后,我们需要观察BillingScheme 的其他函数,寻找那些随着「用户是否为残障人士」而变化的行为。createBill() 就是这样一个函数,因此我们将它拷贝到subclass (图 12,12)。 278 | 279 | 280 | ![](../images/12fig16.gif) 281 | 282 | 图12.12 为「残障人士」添加一个subclass 283 | 284 | 现在,我们需要检查subclass 中的createBill() 函数。由于现在我们可以肯定该 消费者是残障人士,因此可以简化这个函数。所以下列代码: 285 | ```java 286 | if (disabilityScheme()) doSomething 287 | ``` 288 | 289 | 可以变成: 290 | ```java 291 | doSomething 292 | ``` 293 | 294 | 295 | 如果规定在「残障人士用电」和「企业用电」之间只能择一,那么我们的方案就可以避免在BusinessBillingScheme 中出现任何条件代码。 296 | 297 | 实施本项重构时,我们希望将「可能变化」和「始终不变」的部分分开,为此我们可以使用 [提炼函数](composing-methods.md#_1) 和[分解条件式](simplifying-conditional-expressions.md#_3)。本例将对BillingScheme 各函数实施这两项重构,直到「是否为残障人士」的所有判断都得到了适当处理。然后我们再以相同过程处理他种变异(例如「社会救济用电」)。 298 | 299 | 然而,当我们处理第二种变异时,我们应该观察「社会救济用电」与「残障人士用 电」有何不同。我们希望能够为不同的变异(特殊情况)建立起这般函数:有着相同意图,但针对不同的变异性(特殊情况)釆取不同的实际作为(译注:这就是Template Method)。例如上述两种变异情况下的税额计算可能不同。我们希望确保两个subclasses 中的相应函数有相同的签名式(signature)。这可能意味我们必须修改DisablityBillingScheme,使得以将subclasses 整理一番。通常我们发现,面对更多变异时,这种「相仿之中略带变化」的函数(similar and varying methods patterns)会使整个系统结构趋于稳定,使我们更容易添加后续更多变异。 300 | -------------------------------------------------------------------------------- /docs/images/06fig01.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/06fig01.gif -------------------------------------------------------------------------------- /docs/images/07fig01.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/07fig01.gif -------------------------------------------------------------------------------- /docs/images/07fig02.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/07fig02.gif -------------------------------------------------------------------------------- /docs/images/07fig03.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/07fig03.gif -------------------------------------------------------------------------------- /docs/images/07fig04.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/07fig04.gif -------------------------------------------------------------------------------- /docs/images/07fig05.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/07fig05.gif -------------------------------------------------------------------------------- /docs/images/07fig06.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/07fig06.gif -------------------------------------------------------------------------------- /docs/images/07fig07.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/07fig07.gif -------------------------------------------------------------------------------- /docs/images/07fig08.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/07fig08.gif -------------------------------------------------------------------------------- /docs/images/08fig01.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/08fig01.gif -------------------------------------------------------------------------------- /docs/images/08fig02.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/08fig02.gif -------------------------------------------------------------------------------- /docs/images/08fig03.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/08fig03.gif -------------------------------------------------------------------------------- /docs/images/08fig04.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/08fig04.gif -------------------------------------------------------------------------------- /docs/images/08fig05.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/08fig05.gif -------------------------------------------------------------------------------- /docs/images/08fig06.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/08fig06.gif -------------------------------------------------------------------------------- /docs/images/08fig07.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/08fig07.gif -------------------------------------------------------------------------------- /docs/images/08fig08.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/08fig08.gif -------------------------------------------------------------------------------- /docs/images/08fig09.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/08fig09.gif -------------------------------------------------------------------------------- /docs/images/08fig10.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/08fig10.gif -------------------------------------------------------------------------------- /docs/images/08fig11.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/08fig11.gif -------------------------------------------------------------------------------- /docs/images/08fig12.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/08fig12.gif -------------------------------------------------------------------------------- /docs/images/09fig01.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/09fig01.gif -------------------------------------------------------------------------------- /docs/images/09fig01a.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/09fig01a.gif -------------------------------------------------------------------------------- /docs/images/09fig01b.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/09fig01b.gif -------------------------------------------------------------------------------- /docs/images/10fig01.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/10fig01.gif -------------------------------------------------------------------------------- /docs/images/10fig02.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/10fig02.gif -------------------------------------------------------------------------------- /docs/images/10fig03.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/10fig03.gif -------------------------------------------------------------------------------- /docs/images/10fig04.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/10fig04.gif -------------------------------------------------------------------------------- /docs/images/10fig05.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/10fig05.gif -------------------------------------------------------------------------------- /docs/images/10fig06.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/10fig06.gif -------------------------------------------------------------------------------- /docs/images/10fig07.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/10fig07.gif -------------------------------------------------------------------------------- /docs/images/10fig08.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/10fig08.gif -------------------------------------------------------------------------------- /docs/images/11fig01.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/11fig01.gif -------------------------------------------------------------------------------- /docs/images/11fig02.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/11fig02.gif -------------------------------------------------------------------------------- /docs/images/11fig03-1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/11fig03-1.gif -------------------------------------------------------------------------------- /docs/images/11fig03.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/11fig03.gif -------------------------------------------------------------------------------- /docs/images/11fig04.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/11fig04.gif -------------------------------------------------------------------------------- /docs/images/11fig05.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/11fig05.gif -------------------------------------------------------------------------------- /docs/images/11fig06.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/11fig06.gif -------------------------------------------------------------------------------- /docs/images/11fig07.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/11fig07.gif -------------------------------------------------------------------------------- /docs/images/11fig08.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/11fig08.gif -------------------------------------------------------------------------------- /docs/images/11fig09.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/11fig09.gif -------------------------------------------------------------------------------- /docs/images/11fig10.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/11fig10.gif -------------------------------------------------------------------------------- /docs/images/11fig11.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/11fig11.gif -------------------------------------------------------------------------------- /docs/images/11fig12.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/11fig12.gif -------------------------------------------------------------------------------- /docs/images/11fig13.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/11fig13.gif -------------------------------------------------------------------------------- /docs/images/11fig14.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/11fig14.gif -------------------------------------------------------------------------------- /docs/images/11fig15.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/11fig15.gif -------------------------------------------------------------------------------- /docs/images/12fig01.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/12fig01.gif -------------------------------------------------------------------------------- /docs/images/12fig02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/12fig02.png -------------------------------------------------------------------------------- /docs/images/12fig03.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/12fig03.gif -------------------------------------------------------------------------------- /docs/images/12fig04.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/12fig04.gif -------------------------------------------------------------------------------- /docs/images/12fig05.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/12fig05.gif -------------------------------------------------------------------------------- /docs/images/12fig06.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/12fig06.gif -------------------------------------------------------------------------------- /docs/images/12fig07.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/12fig07.gif -------------------------------------------------------------------------------- /docs/images/12fig08.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/12fig08.gif -------------------------------------------------------------------------------- /docs/images/12fig09.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/12fig09.gif -------------------------------------------------------------------------------- /docs/images/12fig10.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/12fig10.gif -------------------------------------------------------------------------------- /docs/images/12fig11.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/12fig11.gif -------------------------------------------------------------------------------- /docs/images/12fig12.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/12fig12.gif -------------------------------------------------------------------------------- /docs/images/12fig13.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/12fig13.gif -------------------------------------------------------------------------------- /docs/images/12fig14.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/12fig14.gif -------------------------------------------------------------------------------- /docs/images/12fig15.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/12fig15.gif -------------------------------------------------------------------------------- /docs/images/12fig16.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/12fig16.gif -------------------------------------------------------------------------------- /docs/images/arrow.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/docs/images/arrow.gif -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | ## 代码的坏味道 2 | --------------- 3 | 4 | ***重复的代码(Duplicated Code)*** 5 | 6 | 7 | 臭味行列中首当其冲的就是Duplicated Code。如果你在一个以上的地点看到相同的程序结构,那么当可肯定:设法将它们合而为一,程序会变得更好。 8 | 9 | 最单纯的Duplicated Code就是「同一个class内的两个函数含有相同表达式(expression)」。这时候你需要做的就是采用[提炼函数](composing-methods.md#_1)提炼出重复的代码,然后让这两个地点都调用被提炼出来的那一段代码。 10 | 11 | 另一种常见情况就是「两个互为兄弟〔sibling)的subclasses内含相同表达式」。要避免这种情况,只需对两个classes都使用[提炼函数](composing-methods.md#_1),然后再对被提炼出来的代码使用[值域上移](dealing-with-generalization.md#_7),将它推入superclass内。如果代码之间只是类似,并非完全相同,那么就得运用[提炼函数](composing-methods.md#_1)将相似部分和差异部分割开,构成单独一个函数。然后你可能发现或许可以运用[塑造模板函数](dealing-with-generalization.md#_5)获得一个Template Method设计模式。如果有些函数以不同的算法做相同的事,你可以择定其中较清晰的一个,并使用[替换你的算法](composing-methods#_10)将其他函数的算法替换掉。 12 | 13 | 如果两个毫不相关的classes内出现Duplicated Code,你应该考虑对其中一个使用[提炼类](moving-features-between-objects.md#_1),将重复代码提炼到一个独立class中,然后在另一个class内 使用这个新class。但是,重复代码所在的函数也可能的确只应该属于某个class, 另一个class只能调用它,抑或这个函数可能属于第三个class,而另两个classes应该引用这第三个class。你必须决定这个函数放在哪儿最合适,并确保它被安置后就不会再在其他任何地方出现。 14 | 15 | ***过长函数(Long Method)*** 16 | 17 | 拥有[短函数」(short methods)的对象会活得比较好、比较长。不熟悉面向对象技术的人,常常觉得对象程序中只有无穷无尽的delegation(委托),根本没有进行任何计算。和此类程序共同生活数年之后,你才会知道,这些小小函数有多大价值。「间接层」所能带来的全部利益――解释能力、共享能力、选择能力――都是由小型函数支持的(请看p.61的「闯接层和重构」〕。 18 | 19 | 很久以前程序员就巳认识到:程序愈长愈难理解。早期的编程语言中,「子程序调用动作」需要额外开销,这使得人们不太乐意使用small method。现代OO语言几乎已经完全免除了进程(process)内的「函数调用动作额外开销」。不过代码阅读者还是得多费力气,因为他必须经常转换上下文去看看子程序做了什么。某些开发环境允许用户同时看到两个函数,这可以帮助你省去部分麻烦,但是让small method容易理解的真正关键在于一个好名字。如果你能给函数起个好名字,读者就可以通过名字了解函数的作用,根本不必去看其中写了些什么。 20 | 21 | 最终的效果是:你应该更积极进取地分解函数。我们遵循这样一条原则:每当感觉需要以注释来说明点什么的时候,我们就把需要说明的东西写进一个独立函数中,并以其用途(而非实现手法)命名。我们可以对一组或甚至短短一行代码做这件事。哪怕替换后的函数调用动作比函数自身还长,只要函数名称能够解释其用途,我们也该毫不犹豫地那么做。关键不在于函数的长度,而在于函数「做什么」和「如何做」之间的语义距离。 22 | 23 | 百分之九十九的场合里,要把函数变小,只需使用[提炼函数](composing-methods.md#_1)。找到函数中适合集在一起的部分,将它们提炼出来形成一个新函数。 24 | 25 | 如果函数内有大量的参数和临时变量,它们会对你的函数提炼形成阻碍。如果你尝试运用[提炼函数](composing-methods.md#_1),最终就会把许多这些参数和临时变量当作参数,传递给被提炼出来的新函数,导致可读性几乎没有任何提升。啊是的,你可以经常运用[以查询取代临时变量](composing-methods#_4)来消除这些暂时元素。[引入参数对象](making-method-calls-simpler.md#_4)和[保持对象完整](making-method-calls-simpler.md#_6)则可以将过长的参数列变得更简洁一些。 26 | 27 | 如果你已经这么做了,仍然有太多临时变量和参数,那就应该使出我们的杀手锏:[以函数对象取代函数](composing-methods#_9)。 28 | 29 | 如何确定该提炼哪一段代码昵? 一个很好的技巧是:寻找注释。它们通常是指出「代码用途和实现手法间的语义距离」的信号。如果代码前方有一行注释,就是在提醒 你:可以将这段代码替换成一个函数,而且可以在注释的基础上给这个函数命名。就算只有一行代码,如果它需要以注释来说明,那也值得将它提炼到独立函数去。 30 | 31 | 条件式和循环常常也是提炼的信号。你可以使用[分解条件式](simplifying-conditional-expressions.md#_3)处理条件式。至于循环,你应该将循环和其内的代码提炼到一个独立函数中。] 32 | 33 | ***过大类(Large Class)*** 34 | 35 | 如果想利用单一class做太多事情,其内往往就会出现太多instance变量。一旦如此,Duplicated Code也就接踵而至了。 36 | 37 | 你可以运用[提炼类](moving-features-between-objects.md#_1)将数个变量一起提炼至新class内。提炼时应该选择class内彼此相关的变量,将它们放在一起。例如"depositAmount"和 "depositCurrency"可能应该隶属同一个class。通常如果class内的数个变量有着相同的前缀或字尾,这就意味有机会把它们提炼到某个组件内。如果这个组件适合作为一个subclass,你会发现[提炼子类](dealing-with-generalization.md#_3)往往比较简单。 38 | 39 | 有时候class并非在所有时刻都使用所有instance变量。果真如此,你或许可以多次使用[提炼类](moving-features-between-objects.md#_1)或[提炼子类](dealing-with-generalization.md#_3)。 40 | 41 | 和「太多instance变量」一样,class内如果有太多代码,也是「代码重复、混乱、死亡」的绝佳滋生地点。最简单的解决方案(还记得吗,我们喜欢简单的解决方案)是把赘余的东西消弭于class内部。如果有五个「百行函数」,它们之中很多代码都相同,那么或许你可以把它们变成五个「十行函数」和十个提炼出来的「双行函 数」。 42 | 43 | 和「拥有太多instance变量」一样,一个class如果拥有太多代码,往往也适合使用[提炼类](moving-features-between-objects.md#_1)和[提炼子类](dealing-with-generalization.md#_3)。这里有个有用技巧:先确定客户端如何使用它们,然后运用[提炼接口](dealing-with-generalization.md#_2)为每一种使用方式提炼出一个接口。这或许可以帮助你看清楚如何分解这个class。 44 | 45 | 如果你的Large class是个GUI class,你可能需要把数据和行为移到一个独立的领域对象(domain objec)去。你可能需要两边各保留一些重复数据,并令这些数据同步(sync.)。[复制被监视数据](organizing-data.md#_5)告诉你该怎么做。这种情况下,特别是如果你使用旧式Abstract Windows Toolkit (AWT)组件,你可以采用这种方式去掉GUI class并代以Swing组件。 46 | 47 | ***过长参数列(Long Parameter List)*** 48 | 49 | 刚开始学习编程的时候,老师教我们:把函数所需的所有东西都以参数传递进去。这可以理解,因为除此之外就只能选择全局数据,而全局数据是邪恶的东西。对象 技术改变了这一情况,因为如果你手上没有你所需要的东西,总可以叫另一个对象给你。因此,有了对象,你就不必把函数需要的所有东西都以参数传递给它了,你只需传给它足够的东西、让函数能从中获得自己需要的所有东西就行了。函数需要的东西多半可以在函数的宿主类(host class)中找到。面向对象程序中的函数,其参数列通常比在传统程序中短得多。 50 | 51 | 这是好现象,因为太长的参数列难以理解,太多参数会造成前后不一致、不易使用,而且一旦你需要更多数据,就不得不修改它。如果将对象传递给函数,大多数修改都将没有必要,因为你很可能只需(在函数内)增加一两条请求(requests),就能得到更多数据。 52 | 53 | 如果「向既有对象发出一条请求」就可以取得原本位于参数列上的一份数据,那么 你应该激活重构准则[以函数取代参数](making-method-calls-simpler.md#_14)。上述的既有对象可能是函数所属class内的一个值域(field),也可能是另一个参数。你还可以运用[保持对象完整](making-method-calls-simpler.md#_6)将来自同一对象的一堆数据收集起来,并以该对象替换它们。如果某些数据缺乏合理的对象归属,可使用[引入参数对象](making-method-calls-simpler.md#_4)为它们制造出一个「参数对象」。 54 | 55 | 此间存在一个重要的例外。有时候你明显不希望造成「被调用之对象」与「较大对 象」间的某种依存关系。这时候将数据从对象中拆解出来单独作为参数,也很合情合理。但是请注意其所引发的代价。如果参数列太长或变化太频繁,你就需要重新考虑自己的依存结构(dependency structure)了。 56 | 57 | ***发散式变化(Divergent Change)*** 58 | 59 | 我们希望软件能够更容易被修改――毕竟软件再怎么说本来就该是「软」的。一旦需要修改,我们希望能够跳到系统的某一点,只在该处做修改。如果不能做到这点,你就嗅出两种紧密相关的刺鼻味道中的一种了。 60 | 61 | 如果某个class经常因为不同的原因在不同的方向上发生变化,Divergent Change就出现了。当你看着一个class说:『呃,如果新加入一个数据库,我必须修改这三个函数;如果新出现一种金融工具,我必须修改这四个函数』,那么此时也许将这个对象分成两个会更好,这么一来每个对象就可以只因一种变化而需要修改。当然,往往只有在加入新数据库或新金融工具后,你才能发现这一点。针对某一外界 变化的所有相应修改,都只应该发生在单一class中,而这个新class内的所有内容都应该反应该外界变化。为此,你应该找出因着某特定原因而造成的所有变化,然后运用[提炼类](moving-features-between-objects.md#_1)将它们提炼到另一个class中。 62 | 63 | ***散弹式修改(Shotgun Surgery)*** 64 | 65 | Shotgun Surgery类似Divergent Change,但恰恰相反。如果每遇到某种变化,你都必须在许多不同的classes内做出许多小修改以响应之,你所面临的坏味道就是Shotgun Surgery。如果需要修改的代码散布四处,你不但很难找到它们,也很容易忘记某个重要的修改。 66 | 67 | 这种情况下你应该使用[搬移函数](moving-features-between-objects.md#_3)和[搬移值域](moving-features-between-objects.md#_2)把所有需要修改的代码放进同一个class。如果眼下没有合适的可以安置这些代码,就创造一 个。通常你可以运用[将类内联化](moving-features-between-objects.md#_4)把一系列相关行为放进同一个class。这可能会造成少量Divergent Change,但你可以轻易处理它。 68 | 69 | Divergent Change是指「一个class受多种变化的影响」,Shotgun Surgery则是指「一种变化引发多个classes相应修改」。这两种情况下你都会希望整理代码,取得「外界变化」与「待改类」呈现一对一关系的理想境地。 70 | 71 | ***依恋情结(Feature Envy)*** 72 | 73 | 对象技术的全部要点在于:这是一种「将数据和加诸其上的操作行为包装在一起」 的技术。有一种经典气味是:函数对某个class的兴趣高过对自己所处之host class的兴趣。这种孺慕之情最通常的焦点便是数据。无数次经验里,我们看到某个函数 为了计算某值,从另一个对象那儿调用几乎半打的取值函数(getting method)。疗法显而易见:把这个函数移至另一个地点。你应该使用[搬移函数](moving-features-between-objects.md#_3)把它 移到它该去的地方。有时候函数中只有一部分受这种依恋之苦,这时候你应该使用 [提炼函数](composing-methods.md#_1) 把这一部分提炼到独立函数中,再使用[搬移函数](moving-features-between-objects.md#_3)带它去它的梦中家园。 74 | 75 | 当然,并非所有情况都这么简单。一个函数往往会用上数个特性,那么它究竟该被置于何处呢?我们的原则是:判断哪个class拥有最多「被此函数使用」的数据,然后就把这个函数和那些数据摆在一起。如果先以[提炼函数](composing-methods.md#_1) 将这个函数分解为数个较小函数并分别置放于不同地点,上述步骤也就比较容易完成了。 76 | 77 | 有数个复杂精巧的模式(patterns)破坏了这个规则。说起这个话题,「四巨头」[Gang of Four]的Strategy 和Visitor立刻跳入我的脑海,Kent Beck 的 Self Delegation [Beck]也在此列。使用这些模式是为了对抗坏味道Divergent Change。最根本的原则是:将总是一起变化的东西放在一块儿。「数据」和「引用这些数据」的行为总是一起变化的,但也有例外。如果例外出现,我们就搬移那些行为,保持「变化只在一地发生」。Strategy 和Visitor『使你得以轻松修改函数行为,因为它们将少量需被覆写〔overridden)的行为隔离开来――当然也付出了「多一层间接性」的 代价。 78 | 79 | ***数据泥团(Data Clumps)*** 80 | 81 | 数据项(data items)就像小孩子:喜欢成群结队地待在一块儿。你常常可以在很多地方看到相同的三或四笔数据项:两个classes内的相同值域(field)、许多函数签名式(signature)中的相同参数。这些「总是绑在一起出现的数据」真应该放进属于它们自己的对象中。首先请找出这些数据的值域形式(field)出现点,运用[提炼类](moving-features-between-objects.md#_1)将它们提炼到一个独立对象中。然后将注意力转移到函数签名式(signature)上头,运用[引入参数对象](making-method-calls-simpler.md#_4)或[保持对象完整](making-method-calls-simpler.md#_6)为它减肥。这么做的直接好处是可以将很多参数列缩短,简化函数调用动作。是的,不必因为Data Clumps只用上新对象的一部分值域而在意,只要你以新对象取代两个(或更多)值域,你就值回票价了。 82 | 83 | 一个好的评断办法是:删掉众多数据中的一笔。其他数据有没有因而失去意义?如果它们不再有意义,这就是个明确信号:你应该为它们产生一个新对象。 84 | 85 | 缩短值域个数和参数个数,当然可以去除一些坏味道,但更重要的是:一旦拥有新对象,你就有机会让程序散发出一种芳香。得到新对象后,你就可以着手寻找Feature Envy,这可以帮你指出「可移至新class」中的种种程序行为。不必太久, 所有classes都将在它们的小小社会中充分发挥自己的生产力。 86 | 87 | ***基本型别偏执(Primitive Obsession)*** 88 | 89 | 大多数编程环境都有两种数据:结构型别(record types)允许你将数据组织成有意义的形式;基本型别(Primitive type)则是构成结构型别的积木块。结构总是会带 来一定的额外开销。它们有点像数据库中的表格,或是那些得不偿失(只为做一两件事而创建,却付出太大额外开销〕的东西。 90 | 91 | 对象的一个极具价值的东西是:它们模糊(甚至打破)了横亘于基本数据和体积较大的classes之间的界限。你可以轻松编写出一些与语言内置(基本〕型别无异的小型classes。例如Java就以基本型别表示数值,而以class表示字符串和日期――这 两个型别在其他许多编程环境中都以基本型别表现。 92 | 93 | 对象技术的新手通常不愿意在小任务上运用小对象――像是结合数值和币别的 money classes 、含一个起始值和一个结束值的range classes、电话号码或邮政编码(ZIP) 等等的特殊strings。你可以运用[以对象取代数据值](organizing-data.md#_9)将原本单独存在的数据值替换为对象,从而走出传统的洞窟,进入炙手可热的对象世界。如果欲替换之数据值是 type code(型别码),而它并不影响行为,你可以运用[以类取代型别码](organizing-data.md#_13)将它换掉。如果你有相依于此 type code的条件式,可运用[以子类取代型别码](organizing-data.md#_14)或[以State/Strategy取代型别码](organizing-data.md#statestrategy)加以处理。 94 | 95 | 如果你有一组应该总是被放在一起的值域(fields),可运用[提炼类](moving-features-between-objects.md#_1)。 如果你在参数列中看到基本型数据,不妨试试[引入参数对象](making-method-calls-simpler.md#_4)。 如果你发现自己正从array中挑选数据,可运用[以对象取代数组](organizing-data.md#_8)。 96 | 97 | ***Switch惊悚现身(Switch Statements)*** 98 | 99 | 面向对象程序的一个最明显特征就是:少用switch (或case)语句。从本质上说, switch语句的问题在于重复(duplication)。你常会发现同样的switch语句散布 于不同地点。如果要为它添加一个新的子句,你必须找到所有switch语句 并修改它们。面向对象中的多态(polymorphism )概念可为此带来优雅的解决办法。 100 | 101 | 大多数时候,一看到switch语句你就应该考虑以「多态」来替换它。问题是态 该出现在哪儿?switch语句常常根据 type code(型别码)进行选择,你要的是「与 该 type code相关的函数或class」。所以你应该使用[提炼函数](composing-methods.md#_1) 将switch语句提炼到一个独立函数中,再以[搬移函数](moving-features-between-objects.md#_3)将它搬移到需要多态性的那个class里头。此时你必须决定是否使用[以子类取代型别码](organizing-data.md#_14)或[以State/Strategy取代型别码](organizing-data.md#statestrategy)。一旦这样完成继承结构之后, 你就可以运用[以多态取代条件式](simplifying-conditional-expressions.md#_6)了。 102 | 103 | 如果你只是在单一函数中有些选择事例,而你并不想改动它们,那么「多态」就有 点杀鸡用牛刀了。这种情况下[以明确函数取代参数](making-method-calls-simpler.md#_13)是个不错的选择。如果你的选择条件之一是null,可以试试Introduce Null Object。) 104 | 105 | ***平行继承体系(Parallel Inheritance Hierarchies)*** 106 | 107 | Parallel Inheritance Hierarchies其实是shotgun surgery的特殊情况。在这种情况下,每当你为某个class增加一个subclass,必须也为另一个class相应增加一个subclass。如果你发现某个继承体系的名称前缀和另一个继承体系的名称前缀完全相同,便是闻到了这种坏味道。 108 | 109 | 消除这种重复性的一般策略是:让一个继承体系的实体(instance)指涉(参考、引用、refer to)另一个继承体系的实体(instances)。如果再接再厉运用[搬移函数](moving-features-between-objects.md#_3)和[搬移值域](moving-features-between-objects.md#_2),就可以将指涉端( referring class )的继承体系消弭于无形。 110 | 111 | ***冗赘类(Lazy Class)*** 112 | 113 | 你所创建的每一个class,都得有人去理解它、维护它,这些工作都是要花钱的。如 果一个class的所得不值其身价,它就应该消失。项目中经常会出现这样的情况: 某个class原本对得起自己的身价,但重构使它身形缩水,不再做那么多工作;或开发者事前规划了某些变化,并添加一个class来应付这些变化,但变化实际上没 有发生。不论上述哪一种原因,请让这个class庄严赴义吧。如果某些subclass没有做满足够工作,试试 [折叠继承关系](dealing-with-generalization.md#_1)。对于几乎没用的组件,你应该以[将类内联化](moving-features-between-objects.md#_4)对付它们。 114 | 115 | ***夸夸其谈未来性(Speculative Generality)*** 116 | 117 | 这个令我们十分敏感的坏味道,命名者是Brian Foote。当有人说『噢,我想我们总有一天需要做这事』并因而企图以各式各样的挂勾(hooks)和特殊情况来处理一 些非必要的事情,这种坏味道就出现了。那么做的结果往往造成系统更难理解和维护。如果所有装置都会被用到,那就值得那么做;如果用不到,就不值得。用不上的装置只会挡你的路,所以,把它搬开吧。 118 | 119 | 如果你的某个abstract class其实没有太大作用,请运用[折叠继承关系](dealing-with-generalization.md#_1)。非必要之delegation (委托)可运用[将类内联化](moving-features-between-objects.md#_4)除掉。如果函数的某些参数未被用上,可对它实施[移除参数](making-method-calls-simpler.md#_7)。如果函数名称带有多余的抽象意味,应该对它实施[重新命名函数](making-method-calls-simpler.md#_9)让它现实一些。 120 | 121 | 如果函数或class的惟一用户是test cases (测试用例),这就飘出了坏味道Speculative Generality。如果你发现这样的函数或class,请把它们连同其test cases都删掉。但如果它们的用途是帮助test cases检测正当功能,当然必须刀下留人。 122 | 123 | ***令人迷惑的暂时值域(Temporary Field)*** 124 | 125 | 有时你会看到这样的对象:其内某个instance变量仅为某种特定情势而设。这样的代码让人不易理解,因为你通常认为对象在所有时候都需要它的所有变量。在变量未被使用的情况下猜测当初其设置目的,会让你发疯。 126 | 127 | 请使用[提炼类](moving-features-between-objects.md#_1)给这个可怜的孤儿创造一个家,然后把所有和这个变 量相关的代码都放进这个新家。也许你还可以使用 Introduce Null Object 在「变量不合法』的情况下创建一个Null对象,从而避免写出『条件式代码」。 128 | 129 | 如果class中有一个复杂算法,需要好几个变量,往往就可能导致坏味道Temporary Field的出现。由于实现者不希望传递一长串参数(想想为什么),所以他把这些 参数都放进值域(field)中。但是这些值域只在使用该算法时才有效,其他情况下只会让人迷惑。这时候你可以利用[提炼类](moving-features-between-objects.md#_1)把这些变量和其相关函数提炼到一个独立class中。提炼后的新对象将是一个method object[Beck](译注:其存在只是为了提供调用函数的途径,class本身并无抽象意味)。 130 | 131 | ***过度耦合的消息链(Message Chains)*** 132 | 133 | 如果你看到用户向一个对象索求(request)另一个对象,然后再向后者索求另一个对象,然后再索求另一个对象……这就是Message Chains。实际代码中你看到的可 能是一长串getThis()或一长串临时变量。采取这种方式,意味客户将与查找过程中的航行结构(structure of the navigation)紧密耦合。一旦对象间的关系发生任何变化,客户端就不得不做出相应修改。 134 | 135 | 这时候你应该使用[隐藏委托关系](moving-features-between-objects.md#_5)。你可以在Message Chains的不同位置进行这种重构手法。理论上你可以重构Message Chains上的任何一个对象,但这么做往往会把所有中介对象(intermediate object )都变成Middle Man。通常更好的选择是:先观察Message Chains最终得到的对象是用来干什么的,看看能否以 [提炼函数](composing-methods.md#_1) 把使用该对象的代码提炼到一个独立函数中,再运用[搬移函数](moving-features-between-objects.md#_3)把这个函数推入Message Chains。如果这条链上的某个对象有多位客户打算航行此航线的剩余部分,就加一个函数来做这件事。 136 | 137 | 有些人把任何函数链(method chain。译注:就是Message Chains;面向对象领域中所谓「发送消息」就是「调用函数」)都视为坏东西,我们不这样想。呵呵,我们的冷静镇定是出了名的,起码在这件事情上是这样。 138 | 139 | ***中间转手人(Middle Man)*** 140 | 141 | 对象的基本特征之一就是封装(encapsulation)――对外部世界隐藏其内部细节。封装往往伴随delegation (委托)。比如说你问主管是否有时间参加一个会议,他就把这个消息委托给他的记事簿,然后才能回答你。很好,你没必要知道这位主管到底使用传统记事簿或电子记事簿抑或秘书来记录自己的约会。 142 | 143 | 但是人们可能过度运用delegation。你也许会看到某个class接口有一半的函数都委托给其他class,这样就是过度运用。这时你应该使用[移除中间人](moving-features-between-objects.md#_6),直接和实责对象打交道。如果这样「不干实事」的函数只有少数几个,可以运用[将函数内联化](composing-methods.md#_2)把它们" Inlining",放进调用端。如果这些Middle Man还有其他行 为,你可以运用 [以继承取代委托](dealing-with-generalization.md#_11) 把它变成实责对象的subclass,这样你既可以扩展原对象的行为,又不必负担那么多的委托动作。 144 | 145 | ***Inappropriate Intimacy(狎昵关系)*** 146 | 147 | 有时你会看到两个classes过于亲密,花费太多时间去探究彼此的private成分。如果这发生在两个「人」之间,我们不必做卫道之士;但对于classes,我们希望它们严守清规。 148 | 149 | 就像古代恋人一样,过份狎昵的classes必须拆散。你可以采用[搬移函数](moving-features-between-objects.md#_3)和[搬移值域](moving-features-between-objects.md#_2)帮它们划清界线,从而减少狎昵行径。你也可以看看是否运用[将双向关联改为单向](organizing-data.md#_1)让其中一个class对另一个斩断情丝。如果两个实在是情投意合,可以运用[提炼类](moving-features-between-objects.md#_1)把两者共同点提炼到一个安全地点,让它们坦荡地使用这个新class。或者也可以尝试运用[隐藏委托关系](moving-features-between-objects.md#_5)让另一个class来为它们传递相思情。 150 | 151 | 继承(inheritance)往往造成过度亲密,因为subclass对superclass的了解总是超过superclass的主观愿望。如果你觉得该让这个孩子独自生活了,请运用[以继承取代委托](dealing-with-generalization.md#_11) 让它离开继承体系。 152 | 153 | ***Alternative Classes with Different Interfaces(异曲同工的类)*** 154 | 155 | 如果两个函数做同一件事,却有着不同的签名式(signatures),请运用[重新命名函数](making-method-calls-simpler.md#_9)根据它们的用途重新命名。但这往往不够,请反复运用[搬移函数](moving-features-between-objects.md#_3)将某些行为移入classes,直到两者的协议(protocols )一致为止。如果你必须重复而赘余地移入代码才能完成这些,或许可运用[提炼超类](dealing-with-generalization.md#_4)为自己赎 点罪。 156 | 157 | ***不完美的程序库类(Incomplete Library Class)*** 158 | 159 | 复用(reuse)常被视为对象的终极目的。我们认为这实在是过度估计了(我们只是使用而己)。但是无可否认,许多编程技术都建立在library classes (程序库类)的基础上,没人敢说是不是我们都把排序算法忘得一干二净了。 160 | 161 | library classes构筑者没有未卜先知的能力,我们不能因此责怪他们。毕竟我们自己也几乎总是在系统快要构筑完成的时候才能弄清楚它的设计,所以library 构筑者的任务真的很艰巨。麻烦的是library的形式(form)往往不够好,往往不可能让我们修改其中的classes使它完成我们希望完成的工作。这是否意味那些经过实践检验的战术如[搬移函数](moving-features-between-objects.md#_3)等等,如今都派不上用场了? 162 | 163 | 幸好我们有两个专门应付这种情况的工具。如果你只想修改library classes内的一两 个函数,可以运用[引入外加函数](moving-features-between-objects.md#_7);如果想要添加一大堆额外行为,就得运用[引入本地扩展](moving-features-between-objects.md#_8)。 164 | 165 | ***纯稚的数据类(Data Class)*** 166 | 167 | 所谓Data Class是指:它们拥有一些值域(fields),以及用于访问(读写〕这些值域的函数,除此之外一无长物。这样的classes只是一种「不会说话的数据容器」,它们几乎一定被其他classes过份细琐地操控着。这些classes早期可能拥有public值域,果真如此你应该在别人注意到它们之前,立刻运用[封装值域](organizing-data.md#_7)将它们封装起来。如果这些classes内含容器类的值域(collection fields),你应该 检査它们是不是得到了恰当的封装;如果没有,就运用[封装群集](organizing-data.md#_6)把它们封装起来。对于那些不该被其他classes修改的值域,请运用[移出设置函数](making-method-calls-simpler.md#_8)。 168 | 169 | 然后,找出这些「取值/设值」函数(getting and setting methods)被其他classes运用的地点。尝试以[搬移函数](moving-features-between-objects.md#_3)把那些调用行为搬移到Data Class来。如果无法搬移整个函数,就运用 [提炼函数](composing-methods.md#_1) 产生一个可被搬移的函数。不久之后你就可以运用[隐藏某个函数](making-method-calls-simpler.md#_3)把这些「取值/设值」函数隐藏起来了。 170 | 171 | Data Class就像小孩子。作为一个起点很好,但若要让它们像「成年(成熟)」的对象那样参与整个系统的工作,它们就必须承担一定责任。 172 | 173 | ***被拒绝的遗赠(Refused Bequest)*** 174 | 175 | Subclasses 应该继承superclasses的函数和数据。但如果它们不想或不需要继承,又该怎么办呢?它们得到所有礼物,却只从中挑选几样来玩! 176 | 177 | 按传统说法,这就意味继承体系设计错误。你需要为这个subclass 新建一个兄弟(sibling class),再运用[函数下移](dealing-with-generalization.md#_10)和[值域下移](dealing-with-generalization.md#_9)把所有用不到的函数下推给那兄弟。这样一来superclass就只持有所有subclasses共享的东西。常常你会听到这样的建议:所有superclasses都应该是抽象的(abstract)。 178 | 179 | 既然使用「传统说法」这个略带贬义的词,你就可以猜到,我们不建议你这么做,起码不建议你每次都这么做。我们经常利用subclassing手法来复用一些行为,并发现这可以很好地应用于日常工作。这也是一种坏味道,我们不否认,但气味通常并不强烈。所以我们说:如果Refused Bequest引起困惑和问题,请遵循传统忠告。但不必认为你每次都得那么做。十有八九这种坏味道很淡,不值得理睬。 180 | 181 | 如果subclass复用了superclass的行为(实现),却又不愿意支持superclass的接口,Refused Bequest的坏味道就会变得浓烈。拒绝继承superclass的实现,这一点我们不介意;但如果拒绝继承superclass的接口,我们不以为然。不过即使你不愿意继承接口,也不要胡乱修改继承体系,你应该运用[以委托取代继承](dealing-with-generalization.md#_12) 来达到目的。 182 | 183 | ***过多的注释(Comments)*** 184 | 185 | 别担心,我们并不是说你不该写注释。从嗅觉上说,Comments不是一种坏味道;事实上它们还是一种香味呢。我们之所以要在这里提到Comments,因为人们常把它当作除臭剂来使用。常常会有这样的情况:你看到一段代码有着长长的注释,然后发现,这些注释之所以存在乃是因为代码很糟糕。这种情况的发生次数之多,实 在令人吃惊。 186 | 187 | Comments可以带我们找到本章先前提到的各种坏味道。找到坏味道后,我们首先应该以各种重构手法把坏味道去除。完成之后我们常常会发现:注释已经变得多余了,因为代码已经清楚说明了一切。 188 | 189 | 如果你需要注释来解释一块代码做了什么,试试 [提炼函数](composing-methods.md#_1);如果method已经提炼出来,但还是需要注释来解释其行为,试试[重新命名函数](making-method-calls-simpler.md#_9);如果你需要注释说明某些系统的需求规格,试试[引入断言](simplifying-conditional-expressions.md#_4)。 190 | 191 | TIP:当你感觉需要撰写注释,请先尝试重构,试着让所有注释都变得多余。 192 | 193 | 如果你不知道该做什么,这才是注释的良好运用时机。除了用来记述将来的打算之外,注释还可以用来标记你并无十足把握的区域。你可以在注释里写下自己「为什 么做某某事」。这类信息可以帮助将来的修改者,尤其是那些健忘的家伙。 194 | 195 | 196 | ## 重新组织你的函数 197 | ------------------- 198 | 199 | 我的重构手法中,很大一部分是对函数进行整理,使之更恰当地包装代码。几乎所有时刻,问题都源于Long Method(过长函数)。这很讨厌,因为它们往往包含太多信息,这些信息又被函数错综复杂的逻辑掩盖,不易鉴别。对付过长函数,一项重要的重构手法就是[提炼函数](composing-methods.md#_1),它把一段代码从原先函数中提取出 来,放进一个单独函数中。[将函数内联化](composing-methods.md#_2)正好相反:将一个函数调用动作替 换为该函数本体。如果在进行多次提炼之后,意识到提炼所得的某些函数并没有做任何实质事情,或如果需要回溯恢复原先函数,我就需要[将函数内联化](composing-methods.md#_2)。 200 | 201 | [Extract Method]最大的困难就是处理局部变量,而临时变量则是其中一个主要的困难源头。处理一个函数时,我喜欢运用[以查询取代临时变量](composing-methods#_4) 去掉所有可去掉的临时变量。如果很多地方使用了某个临时变量,我就会先运用[剖解临时变量](composing-methods.md#_6)将它变得比较容易替换。 202 | 203 | 但有时候临时变量实在太混乱,难以替换。这时候我就需要使用[以函数对象取代函数](composing-methods#_9)。它让我可以分解哪怕最混乱的函数,代价则是引入一个新class。 204 | 205 | 参数带来的问题比临时变量稍微少一些,前提是你不在函数内赋值给它们。如果你已经这样做了,就得使用[移除对参数的赋值](composing-methods.md#_7)。 206 | 207 | 函数分解完毕后,我就可以知道如何让它工作得更好。也许我还会发现算法可以改进,从而使代码更清晰。这时我就使用[替换你的算法](composing-methods#_10)引入更清晰的算法。 208 | 209 | 210 | 211 | ## 在对象之间搬移特性 212 | --- 213 | 214 | 在对象的设计过程中,「决定把责任放在哪儿」即使不是最重要的事,也是最重要的事之一。我使用对象技术已经十多年了,但还是不能一开始就保证做对。这曾经让我很烦恼,但现在我知道,在这种情况下,我可以运用重构(refactoring),改变自己原先的设计。 215 | 216 | 常常我可以只运用[搬移函数](moving-features-between-objects.md#_3)和[搬移值域](moving-features-between-objects.md#_2)简单地移动对象行为,就可以解决这些问题。如果这两个重构手法都需要用到,我会首先使用[搬移值域](moving-features-between-objects.md#_2),再使用[搬移函数](moving-features-between-objects.md#_3)。 217 | 218 | class往往会因为承担过多责任而变得臃肿不堪。这种情况下,我会使用[提炼类](moving-features-between-objects.md#_1)将一部分责任分离出去。如果一个class变得太「不负责任」,我就会使用[将类内联化](moving-features-between-objects.md#_4)将它融入另一个class。如果一个class使用了另一个class,运用[隐藏委托关系](moving-features-between-objects.md#_5)将这种关系隐藏起来通常是有帮助的。有时候隐藏delegate class会导致拥有者的接口经常变化,此时需要使用[移除中间人](moving-features-between-objects.md#_6)。 219 | 220 | 本章的最后两项重构——[引入外加函数](moving-features-between-objects.md#_7)和[引入本地扩展](moving-features-between-objects.md#_8)——比较特殊。只有当我不能访问某个class的源码,却又想把其他责任移进这个不可修改的class时,我才会使用这两个重构手法。如果我想加入的只是一或两个函数,我会使用[引入外加函数](moving-features-between-objects.md#_7);如果不止一两个函数,我就使用[引入本地扩展](moving-features-between-objects.md#_8)。 221 | 222 | ## 组织你的数据 223 | --------------- 224 | 225 | 本章之中,我将讨论数个「能让你更轻松运用数据」的重构手法。很多人或许会认为Self[封装值域](organizing-data.md#_7)有点多余,但是关于「对象应该直接访问其中的数据,抑或应该通过访问函数(accessor)来访问」这一问题,争论的声音从来不曾停止。有时候你确实需要访问函数,此时你就可以通过[封装值域](organizing-data.md#_7)得到它们。通常我会选择「直接访问」方式,因为我发现,只要我想做,任何时候进行这项重构都是很简单的。 226 | 227 | 面向对象语言有一个很有用的特征:除了允许使用传统语言提供的简单数据型别,它们还允许你定义新型别。不过人们往往需要一段时间才能习惯这种编程方式。一开始你常会使用一个简单数值来表示某个概念;随着对系统的深入了解,你可能会明白,以对象表示这个概念,可能更合适。[以对象取代数据值](organizing-data.md#_9)让你可以将「哑」数据(dumb data)变成会说话的对象(articulate objects)。如果你发现程序中有太多地方需要这一类对象,你也可以使用[将实值对象改为引用对象](organizing-data.md#_4)将它们变成reference object。 228 | 229 | 如果你看到一个array的行为方式很像一个数据结构,你可以使用[以对象取代数组](organizing-data.md#_8)把array变成对象,从而使这个数据结构更清晰地显露出来。但这只是第一步,当你使用[搬移函数](moving-features-between-objects.md#_3)为这个新对象加入相应行为时,真正的好处才得以体现。 230 | 231 | 魔法数(magic numbers),也就是带有特殊含义的数字,从来都是个问题。我还清楚记得,一开始学习编程的时候,老师就告诉我不要使用魔法数。但它们还是不时出现。因此,只要弄清楚魔法数的用途,我就运用[以符号常量/字面常量取代魔法数](organizing-data.md#_10)将它们除掉,以绝后患。 232 | 233 | 对象之间的关联(links)可以单向,也可以双向。单向关联比较简单,但有时为了支持一项新功能,你需要以[将单向关联改为双向](organizing-data.md#_3)将它变成双向关联。[将双向关联改为单向](organizing-data.md#_1)则恰恰相反:如果你发现不再需要双向关联,可以使用这项重构将它变成单向关联。 234 | 235 | 我常常遇到这样的情况:GUI classes竟然去处理不该它们处理的业务逻辑(business logic)。为了把这些处理业务逻辑的行为移到合适的domain class去,你需要在domain class中保存这些逻辑的相关数据,并运用[复制被监视数据](organizing-data.md#_5)提供对GUI的支持。一般来说,我不喜欢重复的数据,但这是一个例外,因为这里的重复数据通常是不可避免的。 236 | 237 | 面向对象编程(OOP)的关键原则之一就是封装。如果一个class暴露了任何public数据,你就应该使用[封装值域](organizing-data.md#_7)将它高雅而正派地包装起来。如果被暴露的数据是个群集(collection),你就应该使用[封装群集](organizing-data.md#_6)因为群集有其特殊协议。如果一整笔记录(record)都被裸露在外,你就应该使用[以数据类取代记录](organizing-data.md#_11)。 238 | 239 | 需要特别对待的一种数据是type code〔型别码):这是一种特殊数值,用来指出 「与实体所属之型别相关的某些东西」。Type code通常以枚举(enumeration)形式出现,并且通常以static final整数实现之。如果这些type code用来表现某种信息,并且不会改变所属class的行为,你可以运用[以类取代型别码](organizing-data.md#_13)将它们替换掉,这项重构会为你提供更好的型别检查,以及一个更好的平台,使你可以在未来更方便地将相关行为添加进去。另一方面,如果class的行为受到type code的影响,你就应该尽可能使用[以子类取代型别码](organizing-data.md#_14)。如果做不到,就只好使用更复杂(同时也更灵活)的[以State/Strategy取代型别码](organizing-data.md#statestrategy)。 240 | 241 | ## 简化条件表达式 242 | ----------------- 243 | 244 | 条件逻辑(conditional logic)有可能十分复杂,因此本章提供一些重构手法,专门用来简化它们。其中一项核心重构就是[分解条件式](simplifying-conditional-expressions.md#_3),可将一个复杂的条件逻辑分成若干小块。这项重构很重要,因为它使得「转辙逻辑」(switching logic )和「操作细节」(details)分离。 245 | 246 | 本章的其余重构手法可用以处理另一些重要问题:如果你发现代码中的多处测试有相同结果,应该实施[合并条件式](simplifying-conditional-expressions.md#_1);如果条件代码中有任何重复,可以运用[合并重复的条件片段](simplifying-conditional-expressions.md#_2)将重复成分去掉。 247 | 248 | 如果程序开发者坚持「单一出口(one exit point )」原则,那么为让条件式也遵循这 一原则,他往往会在其中加入控制标记(control flags )。我并不特别在意「一个函数一个出口」原则,所以我使用[以卫语句取代嵌套条件式](simplifying-conditional-expressions.md#_7)标示出那些特殊情况,并使用[移出控制标记](simplifying-conditional-expressions.md#_5)去除那些讨厌的控制标记。 249 | 250 | 较之于过程化(procedural )程序而言,面向对象(object oriented)程序的条件式通常比较少,这是因为很多条件行为都被多态机制(polymorphism)处理掉了。多态之所以更好,是因为调用者无需了解条件行为的细节,因此条件的扩展更为容易。所以面向对象程序中很少出现switch 语句;一旦出现,就应该考虑运用[以多态取代条件式](simplifying-conditional-expressions.md#_6)将它替换为多态。 251 | 252 | 多态还有一种十分有用但鲜为人知的用途:通过[引入Null对象](simplifying-conditional-expressions.md#null)去除对于null value的检验。 253 | 254 | ## 简化函数调用 255 | --------------- 256 | 257 | 在对象技术中,最重要的概念莫过于「接口」(interface)。容易被理解和被使用的接口,是开发良好面向对象软件的关键。本章将介绍「使接口变得更简洁易用」 的重构手法。 258 | 259 | 最简单也最重要的一件事就是修改函数名称。「名称」是程序写作者与阅读者交流的关键工具。只要你能理解一段程序的功能,就应该大胆地使用[重新命名函数](making-method-calls-simpler.md#_9)将你所知道的东西传达给其他人。另外,你也可以(并且应该)在适当时机修改变量名称和class 名称。不过,总体来说,「修改名称」只是相对比较简单 的文本替换功夫,所以我没有为它们提供单独的重构项目。 260 | 261 | 函数参数在「接口」之中扮演十分重要的角色。[添加参数](making-method-calls-simpler.md#_1)和[移除参数](making-method-calls-simpler.md#_7)都是很常见的重构手法。初始接触面向对象技术的程序员往往使用很长的参数列(parameter lists),这在其他开发环境中是很典型的方式。但是, 使用对象技术,你可以保持参数列的简短,以下有一些相关的重构可以帮助你缩短参数列。如果来自同一对象的数个值被当作参数传递,你可以运用[保持对象完整](making-method-calls-simpler.md#_6)将它们替换为单一对象,从而缩短参数列。如果此前并不存在这样一个对象,你可以运用[引入参数对象](making-method-calls-simpler.md#_4)将它创建出来。如果函数参数来自该函数可取用的一个对象,则可以使用[以函数取代参数](making-method-calls-simpler.md#_14)避免传递参数。如果某些参数被用来在条件式中做选择依据,你可以实施[以明确函数取代参数](making-method-calls-simpler.md#_13)。另外,你还可以使用[令函数携带参数](making-method-calls-simpler.md#_5)为数个相似函数添加参数,将它们合并到一起。 262 | 263 | 关于缩减参数列的重构手法,Doug Lea 对我提出了一个警告:并发编程(con-current programming)往往需要使用较长的参数列,因为这样你可以保证传递给函数的参数都是不可被修改的,就像内置型对象和value object 一定地不可变。通常,你可以使用不可变对象(immutable object)取代这样的长参数列,但另一方面你也必须对此类重构保持谨慎。 264 | 265 | 多年来我一直坚守一个很有价值的习惯:明确地将「修改对象状态」的函数(修改函数,modifiers)和「查询对象状态」的函数(查询函数,queries)分开设计。不知道多少次,我因为将这两种函数混在一起而麻烦缠身;不知道多少次,我看到别 人也因为同样的原因而遇到同样的麻烦。因此,如果我看到这两种函数混在一起, 我就使用[将查询函数和修改函数分离](making-method-calls-simpler.md#_15)将它们分开。 266 | 267 | 良好的接口只向用户展现必须展现的东西。如果一个接口暴露了过多细节,你可以将不必要暴露的东西隐藏起来,从而改进接口的质量。毫无疑问,所有数据都应该隐藏起来(希望你不需要我来告诉你这一点),同时,所有可以隐藏的函数都应该被隐藏起来。进行重构时,你往往需要暂时暴露某些东西,最后再以[隐藏某个函数](making-method-calls-simpler.md#_3)和[移出设置函数](making-method-calls-simpler.md#_8)将它们隐藏起来。 268 | 269 | 构造函数(constructors)是Java 和C++ 中特别麻烦的一个东西,因为它强迫你必须知道「待建对象」属于哪一个class ,而你往往并不需要知道这一点。你可以使用[以工厂函数取代构造函数](making-method-calls-simpler.md#_10)避免了解这「被迫了解的一点」。 270 | 271 | 转型(casting)是Java 程序员心中另一处永远的痛。你应该尽量使用[封装向下转型动作](making-method-calls-simpler.md#_2)将「向下转型动作」封装隐藏起来,避免让class 用户做那种动作。 272 | 273 | 和许多现代编程语言一样,Java 也有异常处理(exception-handing)机制,这使得错误处理(error handling)相对容易一些。不习惯使用异常的程序员,往往会以错误代码(error code)表示程序遇到的麻烦。你可以使用[以异常取代错误码](making-method-calls-simpler.md#_11)来运用这些崭新的异常特性。但有时候异常也并不是最合适的选择,你应该实施[以测试取代异常](making-method-calls-simpler.md#_12)先测试一番。 274 | 275 | ## 处理概括关系 276 | --------------- 277 | 278 | 有一批重构手法专门用来处理「概括关系」(generalization ;译注:这里指的是class 继承」这档事),其中主要是将函数(methods)上下移动于继承体系之中。 [值域上移](dealing-with-generalization.md#_7)和[函数上移](dealing-with-generalization.md#_8)都用于将class 特性向继承体系的上端移动,[函数下移](dealing-with-generalization.md#_10)和[值域下移](dealing-with-generalization.md#_9)则将class 特性向继承体系的下端移动。构造函数比较难以向上拉动,因此专门有一个[构造函数本体上移](dealing-with-generalization.md#_6)处理它。我们不会将构造函数往下推,因为[以工厂函数取代构造函数](making-method-calls-simpler.md#_10)通常更管用。 279 | 280 | 如果有若干函数大体上相同,只在细节上有所差异,可以使用[塑造模板函数](dealing-with-generalization.md#_5) 将它们的共同点和不同点分开。 281 | 282 | 除了在继承体系中移动class 特性之外,你还可以建立新class ,改变整个继承体系。[提炼子类](dealing-with-generalization.md#_3)、[提炼超类](dealing-with-generalization.md#_4)和[提炼接口](dealing-with-generalization.md#_2)都是这样的重构手法,它们在继承体系的不同位置构造出新元素。如果你想在型别系统(type system)中标示(mark)一小部分函数,[提炼接口](dealing-with-generalization.md#_2) 特别有用。如果你发现继承体系中的某些classes 没有存在必要,可以使用[折叠继承关系](dealing-with-generalization.md#_1)将它们移除。 283 | 284 | 有时候你会发现继承并非最佳选择,你真正需要的其实是委托(delegation),那么,[以委托取代继承](dealing-with-generalization.md#_12)可以帮助你把继承改为委托。有时候你又会想要做反向修改,此时就可使用[以继承取代委托](dealing-with-generalization.md#_11)。 285 | 286 | ## 大型重构 287 | ---------- 288 | 289 | 前面的章节已经向读者展示了各个单项重构步骤。目前还缺乏的是对整个「游戏」 的完整概念。你之所以进行重构,必定是为了达到某个目的,而不仅仅是为了看起来有所动作(起码大多数时候你的重构是为了达到某个目的)。那么,整个「游戏」 看起来又是怎样的呢? 290 | 291 | ***这场游戏的本质*** 292 | 293 | 以下面介绍的重构手法中,你肯定会注意到一件事:重构步骤的描述,不再如前面 那么仔细。这是因为在大型重构中,情况有很多变化,我们无法告诉你准确的重构步骤;如果没有看到实际情况,任谁都无法确切知道该怎么做。当你为某个函数添加参数时,作法可以很仔细而清楚,因为重构范围(作用域)很清楚。但是当你分 解一个继承体系时,由于每个继承体系都是不同的,所以我们无法告诉你确切的重构步骤。 294 | 295 | 另外,对于这些大型重构,还有一件事需要注意:它们会耗费相当长的时间。第6 章至第11章所介绍的重构手法,都可以在数分钟(至多一个小时)内完成,但是我们曾经进行过的一些大型重构,却需要数月甚至数年的时间。如果你需要给一个运行中的系统添加功能,你不可能说服经理把系统停止运行两个月让你进行重构; 你只能一点一点地做你的工作,今天一点点,明天一点点。 296 | 297 | 在这个过程中,你应该根据需要安排自己的工作,只在需要添加新功能或修补错误 时才进行重构。你不必一开始就完成整个系统的重构;重构程度只要能满足其他任务的需要就行了。反正明天你还可以回来重构。 298 | 299 | 本章范例也反映出这样的哲学。如果要向你展示本书中所有的重构,轻易就能耗去上百页篇幅。我们很清楚这一点,因为Martin 的确尝试过。所以,我们把范例压缩至「数张概略图」的尺度。 300 | 301 | 由于大型重构可能需要花费相当长的时间,因此它们并不像其他章节介绍的重构那样,能够立刻让人满意。你必须有那么一点小小的信仰:你每天都在使你自己的程序世界更安全。 302 | 303 | 进行大规模重构时,有必要为整个开发团队建立共识;这是小型重构所不需要的。大型重构为许许多多的修改指定了方向。整个团队都必须意识到:有一个大型重构正在进行,每个人都应该相应地安排自己的行动。说到这里,我想给大家讲个故事。 两个家伙的车子在山顶附近抛锚了,于是他俩走下车,一人走到车的一头,开始推车。经过毫无成果的半小时之后,车头那家伙开口说道:『我从来不知道把车推下山这么难!』另一个家伙答道:『嘿,你说「推下山」是什么意思?难道我们不是想把车推上山吗?』我猜你一定不想让这个故事在你的开发团队中重演,对吧! 304 | 305 | ***大型重构的重要性*** 306 | 307 | 我们已经看到,使那些小型重构突显价值的质量(可预测的结果、可观察的过程、 立竿见影的满足等等〕,在大型重构中往往并不存在。既然如此,为什么大型重构还那么重要,以至于我们想要把它们放进本书?那是因为如果没有它们,我们就可能面临这样的风险:投入了大把时间学习重构,在实际工作中却无法获得实在的利益。这对我们来说是非常糟糕的,我们不能容忍这种事情发生。 308 | 309 | 更重要的是,你之所以需要重构,决不会是因为它很好玩,而是因为你希望它能对你的程序有所帮助,让你能够做一些重构之前无法做的事情。 310 | 311 | 正如水草会堵塞河道一样,在一知半解的情况下做出的设计决策,一旦堆积起来,也会使你的程序陷于瘫痪。通过重构,你可以保证随时在程序中反映出自己对于「应该如何设计程序」的完整理解。正如水草会迅速蔓延一样,对系统理解不够完整的设计决策,也会很快地将它们的影响蔓延到整个程序中。要根除这种错误,一 个、两个、甚至十个单独的行为都是不够的,只有持续而无处不在的重构才有可能竟其功。 312 | 313 | ***四个大型重构*** 314 | 315 | 本章之中,我们将介绍四个大型重构实例。这些仅仅是例子,我们并没有打算覆盖所有领域。迄今为止,绝大多数关于重构的研究和实践都集中于比较小的重构手法上,以这种方式谈论大型重构,是一种非常新鲜的作法,这主要来自于:Kent 的经验。在大规模重构方面,Kent 的经验比其他所有人都要丰富。 316 | 317 | [梳理并分解继承体系](big-refactorings.md#_1) 用于处理混乱的继承体系——这种继承体系往往以一种令人迷惑的方式组合了数个不同方面的变化(variations)。[将过程化设计转化为对象设计](big-refactorings.md#_2)可以帮助你解决一个「古典」问题:如何处理程序性代码(procedural code )?许多使用面向对象语言的程序员,其实并没有真正理解面向对象技术,因此你常会需要使用这项重构。如果你看到以传统的双层结构(two-tier, 用户界面和数据库)方式编写的代码,你能需要使用[将领域和表述/显示分离](big-refactorings.md#_3) 将业务逻辑(business logic)与用户界面(user interface )隔离开来。经验丰富的面向对象开发人员发现:对于一个长时间、大负荷运转的系统来说,这样的分离是至关重要的。[提炼继承体系](big-refactorings.md#_4) 则可以将过于复杂的class 转变为一群subclass ,从而简化系统。 318 | 319 | -------------------------------------------------------------------------------- /fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /images/06fig01.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/06fig01.gif -------------------------------------------------------------------------------- /images/07fig01.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/07fig01.gif -------------------------------------------------------------------------------- /images/07fig02.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/07fig02.gif -------------------------------------------------------------------------------- /images/07fig03.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/07fig03.gif -------------------------------------------------------------------------------- /images/07fig04.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/07fig04.gif -------------------------------------------------------------------------------- /images/07fig05.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/07fig05.gif -------------------------------------------------------------------------------- /images/07fig06.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/07fig06.gif -------------------------------------------------------------------------------- /images/07fig07.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/07fig07.gif -------------------------------------------------------------------------------- /images/07fig08.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/07fig08.gif -------------------------------------------------------------------------------- /images/08fig01.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/08fig01.gif -------------------------------------------------------------------------------- /images/08fig02.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/08fig02.gif -------------------------------------------------------------------------------- /images/08fig03.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/08fig03.gif -------------------------------------------------------------------------------- /images/08fig04.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/08fig04.gif -------------------------------------------------------------------------------- /images/08fig05.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/08fig05.gif -------------------------------------------------------------------------------- /images/08fig06.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/08fig06.gif -------------------------------------------------------------------------------- /images/08fig07.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/08fig07.gif -------------------------------------------------------------------------------- /images/08fig08.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/08fig08.gif -------------------------------------------------------------------------------- /images/08fig09.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/08fig09.gif -------------------------------------------------------------------------------- /images/08fig10.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/08fig10.gif -------------------------------------------------------------------------------- /images/08fig11.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/08fig11.gif -------------------------------------------------------------------------------- /images/08fig12.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/08fig12.gif -------------------------------------------------------------------------------- /images/09fig01.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/09fig01.gif -------------------------------------------------------------------------------- /images/09fig01a.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/09fig01a.gif -------------------------------------------------------------------------------- /images/09fig01b.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/09fig01b.gif -------------------------------------------------------------------------------- /images/10fig01.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/10fig01.gif -------------------------------------------------------------------------------- /images/10fig02.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/10fig02.gif -------------------------------------------------------------------------------- /images/10fig03.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/10fig03.gif -------------------------------------------------------------------------------- /images/10fig04.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/10fig04.gif -------------------------------------------------------------------------------- /images/10fig05.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/10fig05.gif -------------------------------------------------------------------------------- /images/10fig06.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/10fig06.gif -------------------------------------------------------------------------------- /images/10fig07.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/10fig07.gif -------------------------------------------------------------------------------- /images/10fig08.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/10fig08.gif -------------------------------------------------------------------------------- /images/11fig01.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/11fig01.gif -------------------------------------------------------------------------------- /images/11fig02.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/11fig02.gif -------------------------------------------------------------------------------- /images/11fig03-1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/11fig03-1.gif -------------------------------------------------------------------------------- /images/11fig03.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/11fig03.gif -------------------------------------------------------------------------------- /images/11fig04.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/11fig04.gif -------------------------------------------------------------------------------- /images/11fig05.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/11fig05.gif -------------------------------------------------------------------------------- /images/11fig06.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/11fig06.gif -------------------------------------------------------------------------------- /images/11fig07.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/11fig07.gif -------------------------------------------------------------------------------- /images/11fig08.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/11fig08.gif -------------------------------------------------------------------------------- /images/11fig09.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/11fig09.gif -------------------------------------------------------------------------------- /images/11fig10.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/11fig10.gif -------------------------------------------------------------------------------- /images/11fig11.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/11fig11.gif -------------------------------------------------------------------------------- /images/11fig12.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/11fig12.gif -------------------------------------------------------------------------------- /images/11fig13.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/11fig13.gif -------------------------------------------------------------------------------- /images/11fig14.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/11fig14.gif -------------------------------------------------------------------------------- /images/11fig15.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/11fig15.gif -------------------------------------------------------------------------------- /images/12fig01.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/12fig01.gif -------------------------------------------------------------------------------- /images/12fig02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/12fig02.png -------------------------------------------------------------------------------- /images/12fig03.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/12fig03.gif -------------------------------------------------------------------------------- /images/12fig04.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/12fig04.gif -------------------------------------------------------------------------------- /images/12fig05.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/12fig05.gif -------------------------------------------------------------------------------- /images/12fig06.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/12fig06.gif -------------------------------------------------------------------------------- /images/12fig07.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/12fig07.gif -------------------------------------------------------------------------------- /images/12fig08.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/12fig08.gif -------------------------------------------------------------------------------- /images/12fig09.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/12fig09.gif -------------------------------------------------------------------------------- /images/12fig10.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/12fig10.gif -------------------------------------------------------------------------------- /images/12fig11.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/12fig11.gif -------------------------------------------------------------------------------- /images/12fig12.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/12fig12.gif -------------------------------------------------------------------------------- /images/12fig13.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/12fig13.gif -------------------------------------------------------------------------------- /images/12fig14.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/12fig14.gif -------------------------------------------------------------------------------- /images/12fig15.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/12fig15.gif -------------------------------------------------------------------------------- /images/12fig16.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/12fig16.gif -------------------------------------------------------------------------------- /images/arrow.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/images/arrow.gif -------------------------------------------------------------------------------- /img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangvsa/refactoring-cheat-sheet/1fcfe08372c2481748f2a0b949ca666eb5894a39/img/favicon.ico -------------------------------------------------------------------------------- /js/base.js: -------------------------------------------------------------------------------- 1 | /* center the image */ 2 | $("img").parent().css("text-align", "center"); 3 | 4 | /* Prettyify */ 5 | $( document ).ready(function() { 6 | prettyPrint(); 7 | }); 8 | 9 | 10 | /* Scrollspy */ 11 | var navHeight = $('.navbar').outerHeight(true) + 10 12 | 13 | $('body').scrollspy({ 14 | target: '.bs-sidebar', 15 | offset: navHeight 16 | }) 17 | 18 | 19 | /* Prevent disabled links from causing a page reload */ 20 | $("li.disabled a").click(function() { 21 | event.preventDefault(); 22 | }); 23 | 24 | 25 | /* Adjust the scroll height of anchors to compensate for the fixed navbar */ 26 | window.disableShift = false; 27 | var shiftWindow = function() { 28 | if (window.disableShift) { 29 | window.disableShift = false; 30 | } else { 31 | /* If we're at the bottom of the page, don't erronously scroll up */ 32 | var scrolledToBottomOfPage = ( 33 | (window.innerHeight + window.scrollY) >= document.body.offsetHeight 34 | ); 35 | if (!scrolledToBottomOfPage) { 36 | scrollBy(0, -60); 37 | }; 38 | }; 39 | }; 40 | if (location.hash) {shiftWindow();} 41 | window.addEventListener("hashchange", shiftWindow); 42 | 43 | 44 | /* Deal with clicks on nav links that do not change the current anchor link. */ 45 | $("ul.nav a" ).click(function() { 46 | var href = this.href; 47 | var suffix = location.hash; 48 | var matchesCurrentHash = (href.indexOf(suffix, href.length - suffix.length) !== -1); 49 | if (location.hash && matchesCurrentHash) { 50 | /* Force a single 'hashchange' event to occur after the click event */ 51 | window.disableShift = true; 52 | location.hash=''; 53 | }; 54 | }); 55 | -------------------------------------------------------------------------------- /js/bootstrap-3.0.3.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.0.3 (http://getbootstrap.com) 3 | * Copyright 2013 Twitter, Inc. 4 | * Licensed under http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | 7 | if("undefined"==typeof jQuery)throw new Error("Bootstrap requires jQuery");+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]}}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one(a.support.transition.end,function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b()})}(jQuery),+function(a){"use strict";var b='[data-dismiss="alert"]',c=function(c){a(c).on("click",b,this.close)};c.prototype.close=function(b){function c(){f.trigger("closed.bs.alert").remove()}var d=a(this),e=d.attr("data-target");e||(e=d.attr("href"),e=e&&e.replace(/.*(?=#[^\s]*$)/,""));var f=a(e);b&&b.preventDefault(),f.length||(f=d.hasClass("alert")?d:d.parent()),f.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(f.removeClass("in"),a.support.transition&&f.hasClass("fade")?f.one(a.support.transition.end,c).emulateTransitionEnd(150):c())};var d=a.fn.alert;a.fn.alert=function(b){return this.each(function(){var d=a(this),e=d.data("bs.alert");e||d.data("bs.alert",e=new c(this)),"string"==typeof b&&e[b].call(d)})},a.fn.alert.Constructor=c,a.fn.alert.noConflict=function(){return a.fn.alert=d,this},a(document).on("click.bs.alert.data-api",b,c.prototype.close)}(jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d)};b.DEFAULTS={loadingText:"loading..."},b.prototype.setState=function(a){var b="disabled",c=this.$element,d=c.is("input")?"val":"html",e=c.data();a+="Text",e.resetText||c.data("resetText",c[d]()),c[d](e[a]||this.options[a]),setTimeout(function(){"loadingText"==a?c.addClass(b).attr(b,b):c.removeClass(b).removeAttr(b)},0)},b.prototype.toggle=function(){var a=this.$element.closest('[data-toggle="buttons"]'),b=!0;if(a.length){var c=this.$element.find("input");"radio"===c.prop("type")&&(c.prop("checked")&&this.$element.hasClass("active")?b=!1:a.find(".active").removeClass("active")),b&&c.prop("checked",!this.$element.hasClass("active")).trigger("change")}b&&this.$element.toggleClass("active")};var c=a.fn.button;a.fn.button=function(c){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof c&&c;e||d.data("bs.button",e=new b(this,f)),"toggle"==c?e.toggle():c&&e.setState(c)})},a.fn.button.Constructor=b,a.fn.button.noConflict=function(){return a.fn.button=c,this},a(document).on("click.bs.button.data-api","[data-toggle^=button]",function(b){var c=a(b.target);c.hasClass("btn")||(c=c.closest(".btn")),c.button("toggle"),b.preventDefault()})}(jQuery),+function(a){"use strict";var b=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=this.sliding=this.interval=this.$active=this.$items=null,"hover"==this.options.pause&&this.$element.on("mouseenter",a.proxy(this.pause,this)).on("mouseleave",a.proxy(this.cycle,this))};b.DEFAULTS={interval:5e3,pause:"hover",wrap:!0},b.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},b.prototype.getActiveIndex=function(){return this.$active=this.$element.find(".item.active"),this.$items=this.$active.parent().children(),this.$items.index(this.$active)},b.prototype.to=function(b){var c=this,d=this.getActiveIndex();return b>this.$items.length-1||0>b?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){c.to(b)}):d==b?this.pause().cycle():this.slide(b>d?"next":"prev",a(this.$items[b]))},b.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition.end&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},b.prototype.next=function(){return this.sliding?void 0:this.slide("next")},b.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},b.prototype.slide=function(b,c){var d=this.$element.find(".item.active"),e=c||d[b](),f=this.interval,g="next"==b?"left":"right",h="next"==b?"first":"last",i=this;if(!e.length){if(!this.options.wrap)return;e=this.$element.find(".item")[h]()}this.sliding=!0,f&&this.pause();var j=a.Event("slide.bs.carousel",{relatedTarget:e[0],direction:g});if(!e.hasClass("active")){if(this.$indicators.length&&(this.$indicators.find(".active").removeClass("active"),this.$element.one("slid.bs.carousel",function(){var b=a(i.$indicators.children()[i.getActiveIndex()]);b&&b.addClass("active")})),a.support.transition&&this.$element.hasClass("slide")){if(this.$element.trigger(j),j.isDefaultPrevented())return;e.addClass(b),e[0].offsetWidth,d.addClass(g),e.addClass(g),d.one(a.support.transition.end,function(){e.removeClass([b,g].join(" ")).addClass("active"),d.removeClass(["active",g].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger("slid.bs.carousel")},0)}).emulateTransitionEnd(600)}else{if(this.$element.trigger(j),j.isDefaultPrevented())return;d.removeClass("active"),e.addClass("active"),this.sliding=!1,this.$element.trigger("slid.bs.carousel")}return f&&this.cycle(),this}};var c=a.fn.carousel;a.fn.carousel=function(c){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c),g="string"==typeof c?c:f.slide;e||d.data("bs.carousel",e=new b(this,f)),"number"==typeof c?e.to(c):g?e[g]():f.interval&&e.pause().cycle()})},a.fn.carousel.Constructor=b,a.fn.carousel.noConflict=function(){return a.fn.carousel=c,this},a(document).on("click.bs.carousel.data-api","[data-slide], [data-slide-to]",function(b){var c,d=a(this),e=a(d.attr("data-target")||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"")),f=a.extend({},e.data(),d.data()),g=d.attr("data-slide-to");g&&(f.interval=!1),e.carousel(f),(g=d.attr("data-slide-to"))&&e.data("bs.carousel").to(g),b.preventDefault()}),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var b=a(this);b.carousel(b.data())})})}(jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d),this.transitioning=null,this.options.parent&&(this.$parent=a(this.options.parent)),this.options.toggle&&this.toggle()};b.DEFAULTS={toggle:!0},b.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},b.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b=a.Event("show.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.$parent&&this.$parent.find("> .panel > .in");if(c&&c.length){var d=c.data("bs.collapse");if(d&&d.transitioning)return;c.collapse("hide"),d||c.data("bs.collapse",null)}var e=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[e](0),this.transitioning=1;var f=function(){this.$element.removeClass("collapsing").addClass("in")[e]("auto"),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return f.call(this);var g=a.camelCase(["scroll",e].join("-"));this.$element.one(a.support.transition.end,a.proxy(f,this)).emulateTransitionEnd(350)[e](this.$element[0][g])}}},b.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse").removeClass("in"),this.transitioning=1;var d=function(){this.transitioning=0,this.$element.trigger("hidden.bs.collapse").removeClass("collapsing").addClass("collapse")};return a.support.transition?(this.$element[c](0).one(a.support.transition.end,a.proxy(d,this)).emulateTransitionEnd(350),void 0):d.call(this)}}},b.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()};var c=a.fn.collapse;a.fn.collapse=function(c){return this.each(function(){var d=a(this),e=d.data("bs.collapse"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c);e||d.data("bs.collapse",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.collapse.Constructor=b,a.fn.collapse.noConflict=function(){return a.fn.collapse=c,this},a(document).on("click.bs.collapse.data-api","[data-toggle=collapse]",function(b){var c,d=a(this),e=d.attr("data-target")||b.preventDefault()||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,""),f=a(e),g=f.data("bs.collapse"),h=g?"toggle":d.data(),i=d.attr("data-parent"),j=i&&a(i);g&&g.transitioning||(j&&j.find('[data-toggle=collapse][data-parent="'+i+'"]').not(d).addClass("collapsed"),d[f.hasClass("in")?"addClass":"removeClass"]("collapsed")),f.collapse(h)})}(jQuery),+function(a){"use strict";function b(){a(d).remove(),a(e).each(function(b){var d=c(a(this));d.hasClass("open")&&(d.trigger(b=a.Event("hide.bs.dropdown")),b.isDefaultPrevented()||d.removeClass("open").trigger("hidden.bs.dropdown"))})}function c(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}var d=".dropdown-backdrop",e="[data-toggle=dropdown]",f=function(b){a(b).on("click.bs.dropdown",this.toggle)};f.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=c(e),g=f.hasClass("open");if(b(),!g){if("ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(''}),b.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),b.prototype.constructor=b,b.prototype.getDefaults=function(){return b.DEFAULTS},b.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content")[this.options.html?"html":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},b.prototype.hasContent=function(){return this.getTitle()||this.getContent()},b.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},b.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")},b.prototype.tip=function(){return this.$tip||(this.$tip=a(this.options.template)),this.$tip};var c=a.fn.popover;a.fn.popover=function(c){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof c&&c;e||d.data("bs.popover",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.popover.Constructor=b,a.fn.popover.noConflict=function(){return a.fn.popover=c,this}}(jQuery),+function(a){"use strict";function b(c,d){var e,f=a.proxy(this.process,this);this.$element=a(c).is("body")?a(window):a(c),this.$body=a("body"),this.$scrollElement=this.$element.on("scroll.bs.scroll-spy.data-api",f),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||(e=a(c).attr("href"))&&e.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.offsets=a([]),this.targets=a([]),this.activeTarget=null,this.refresh(),this.process()}b.DEFAULTS={offset:10},b.prototype.refresh=function(){var b=this.$element[0]==window?"offset":"position";this.offsets=a([]),this.targets=a([]);var c=this;this.$body.find(this.selector).map(function(){var d=a(this),e=d.data("target")||d.attr("href"),f=/^#\w/.test(e)&&a(e);return f&&f.length&&[[f[b]().top+(!a.isWindow(c.$scrollElement.get(0))&&c.$scrollElement.scrollTop()),e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){c.offsets.push(this[0]),c.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,d=c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(b>=d)return g!=(a=f.last()[0])&&this.activate(a);for(a=e.length;a--;)g!=f[a]&&b>=e[a]&&(!e[a+1]||b<=e[a+1])&&this.activate(f[a])},b.prototype.activate=function(b){this.activeTarget=b,a(this.selector).parents(".active").removeClass("active");var c=this.selector+'[data-target="'+b+'"],'+this.selector+'[href="'+b+'"]',d=a(c).parents("li").addClass("active");d.parent(".dropdown-menu").length&&(d=d.closest("li.dropdown").addClass("active")),d.trigger("activate.bs.scrollspy")};var c=a.fn.scrollspy;a.fn.scrollspy=function(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.scrollspy.Constructor=b,a.fn.scrollspy.noConflict=function(){return a.fn.scrollspy=c,this},a(window).on("load",function(){a('[data-spy="scroll"]').each(function(){var b=a(this);b.scrollspy(b.data())})})}(jQuery),+function(a){"use strict";var b=function(b){this.element=a(b)};b.prototype.show=function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.data("target");if(d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),!b.parent("li").hasClass("active")){var e=c.find(".active:last a")[0],f=a.Event("show.bs.tab",{relatedTarget:e});if(b.trigger(f),!f.isDefaultPrevented()){var g=a(d);this.activate(b.parent("li"),c),this.activate(g,g.parent(),function(){b.trigger({type:"shown.bs.tab",relatedTarget:e})})}}},b.prototype.activate=function(b,c,d){function e(){f.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),b.addClass("active"),g?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu")&&b.closest("li.dropdown").addClass("active"),d&&d()}var f=c.find("> .active"),g=d&&a.support.transition&&f.hasClass("fade");g?f.one(a.support.transition.end,e).emulateTransitionEnd(150):e(),f.removeClass("in")};var c=a.fn.tab;a.fn.tab=function(c){return this.each(function(){var d=a(this),e=d.data("bs.tab");e||d.data("bs.tab",e=new b(this)),"string"==typeof c&&e[c]()})},a.fn.tab.Constructor=b,a.fn.tab.noConflict=function(){return a.fn.tab=c,this},a(document).on("click.bs.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(b){b.preventDefault(),a(this).tab("show")})}(jQuery),+function(a){"use strict";var b=function(c,d){this.options=a.extend({},b.DEFAULTS,d),this.$window=a(window).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(c),this.affixed=this.unpin=null,this.checkPosition()};b.RESET="affix affix-top affix-bottom",b.DEFAULTS={offset:0},b.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},b.prototype.checkPosition=function(){if(this.$element.is(":visible")){var c=a(document).height(),d=this.$window.scrollTop(),e=this.$element.offset(),f=this.options.offset,g=f.top,h=f.bottom;"object"!=typeof f&&(h=g=f),"function"==typeof g&&(g=f.top()),"function"==typeof h&&(h=f.bottom());var i=null!=this.unpin&&d+this.unpin<=e.top?!1:null!=h&&e.top+this.$element.height()>=c-h?"bottom":null!=g&&g>=d?"top":!1;this.affixed!==i&&(this.unpin&&this.$element.css("top",""),this.affixed=i,this.unpin="bottom"==i?e.top-d:null,this.$element.removeClass(b.RESET).addClass("affix"+(i?"-"+i:"")),"bottom"==i&&this.$element.offset({top:document.body.offsetHeight-h-this.$element.height()}))}};var c=a.fn.affix;a.fn.affix=function(c){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof c&&c;e||d.data("bs.affix",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.affix.Constructor=b,a.fn.affix.noConflict=function(){return a.fn.affix=c,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var b=a(this),c=b.data();c.offset=c.offset||{},c.offsetBottom&&(c.offset.bottom=c.offsetBottom),c.offsetTop&&(c.offset.top=c.offsetTop),b.affix(c)})})}(jQuery); -------------------------------------------------------------------------------- /js/prettify-1.0.min.js: -------------------------------------------------------------------------------- 1 | var q=null;window.PR_SHOULD_USE_CONTINUATION=!0; 2 | (function(){function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92)return f;var b=a.charAt(1);return(f=r[b])?f:"0"<=b&&b<="7"?parseInt(a.substring(1),8):b==="u"||b==="x"?parseInt(a.substring(2),16):a.charCodeAt(1)}function e(a){if(a<32)return(a<16?"\\x0":"\\x")+a.toString(16);a=String.fromCharCode(a);if(a==="\\"||a==="-"||a==="["||a==="]")a="\\"+a;return a}function h(a){for(var f=a.substring(1,a.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),a= 3 | [],b=[],o=f[0]==="^",c=o?1:0,i=f.length;c122||(d<65||j>90||b.push([Math.max(65,j)|32,Math.min(d,90)|32]),d<97||j>122||b.push([Math.max(97,j)&-33,Math.min(d,122)&-33]))}}b.sort(function(a,f){return a[0]-f[0]||f[1]-a[1]});f=[];j=[NaN,NaN];for(c=0;ci[0]&&(i[1]+1>i[0]&&b.push("-"),b.push(e(i[1])));b.push("]");return b.join("")}function y(a){for(var f=a.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=f.length,d=[],c=0,i=0;c=2&&a==="["?f[c]=h(j):a!=="\\"&&(f[c]=j.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return f.join("")}for(var t=0,s=!1,l=!1,p=0,d=a.length;p=5&&"lang-"===b.substring(0,5))&&!(o&&typeof o[1]==="string"))c=!1,b="src";c||(r[f]=b)}i=d;d+=f.length;if(c){c=o[1];var j=f.indexOf(c),k=j+c.length;o[2]&&(k=f.length-o[2].length,j=k-c.length);b=b.substring(5);B(l+i,f.substring(0,j),e,p);B(l+i+j,c,C(b,c),p);B(l+i+k,f.substring(k),e,p)}else p.push(l+i,b)}a.e=p}var h={},y;(function(){for(var e=a.concat(m), 9 | l=[],p={},d=0,g=e.length;d=0;)h[n.charAt(k)]=r;r=r[1];n=""+r;p.hasOwnProperty(n)||(l.push(r),p[n]=q)}l.push(/[\S\s]/);y=L(l)})();var t=m.length;return e}function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?m.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/, 10 | q,"'\"`"]):m.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&e.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var h=a.hashComments;h&&(a.cStyleComments?(h>1?m.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):m.push(["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),e.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,q])):m.push(["com",/^#[^\n\r]*/, 11 | q,"#"]));a.cStyleComments&&(e.push(["com",/^\/\/[^\n\r]*/,q]),e.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));a.regexLiterals&&e.push(["lang-regex",/^(?:^^\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\(|\*|\*=|\+=|,|-=|->|\/|\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\^=|\^\^|\^\^=|{|\||\|=|\|\||\|\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(h=a.types)&&e.push(["typ",h]);a=(""+a.keywords).replace(/^ | $/g, 12 | "");a.length&&e.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),q]);m.push(["pln",/^\s+/,q," \r\n\t\xa0"]);e.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,q],["pun",/^.[^\s\w"-$'./@\\`]*/,q]);return x(m,e)}function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.className))break;if("BR"===a.nodeName)h(a), 13 | a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)e(a);break;case 3:case 4:if(p){var b=a.nodeValue,d=b.match(t);if(d){var c=b.substring(0,d.index);a.nodeValue=c;(b=b.substring(d.index+d[0].length))&&a.parentNode.insertBefore(s.createTextNode(b),a.nextSibling);h(a);c||a.parentNode.removeChild(a)}}}}function h(a){function b(a,d){var e=d?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),g=a.nextSibling;f.appendChild(e);for(var h=g;h;h=g)g=h.nextSibling,f.appendChild(h)}return e} 14 | for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),e;(e=a.parentNode)&&e.nodeType===1;)a=e;d.push(a)}var k=/(?:^|\s)nocode(?:\s|$)/,t=/\r\n?|\n/,s=a.ownerDocument,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=s.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);for(l=s.createElement("LI");a.firstChild;)l.appendChild(a.firstChild);for(var d=[l],g=0;g=0;){var h=m[e];A.hasOwnProperty(h)?window.console&&console.warn("cannot override language handler %s",h):A[h]=a}}function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*=o&&(h+=2);e>=c&&(a+=2)}}catch(w){"console"in window&&console.log(w&&w.stack?w.stack:w)}}var v=["break,continue,do,else,for,if,return,while"],w=[[v,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"], 18 | "catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],F=[w,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],G=[w,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"], 19 | H=[G,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"],w=[w,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],I=[v,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"], 20 | J=[v,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],v=[v,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],K=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/,N=/\S/,O=u({keywords:[F,H,w,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+ 21 | I,J,v],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};k(O,["default-code"]);k(x([],[["pln",/^[^]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]), 22 | ["default-markup","htm","html","mxml","xhtml","xml","xsl"]);k(x([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css", 23 | /^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);k(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);k(u({keywords:F,hashComments:!0,cStyleComments:!0,types:K}),["c","cc","cpp","cxx","cyc","m"]);k(u({keywords:"null,true,false"}),["json"]);k(u({keywords:H,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:K}),["cs"]);k(u({keywords:G,cStyleComments:!0}),["java"]);k(u({keywords:v,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);k(u({keywords:I,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}), 24 | ["cv","py"]);k(u({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);k(u({keywords:J,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb"]);k(u({keywords:w,cStyleComments:!0,regexLiterals:!0}),["js"]);k(u({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes", 25 | hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);k(x([],[["str",/^[\S\s]+/]]),["regex"]);window.prettyPrintOne=function(a,m,e){var h=document.createElement("PRE");h.innerHTML=a;e&&D(h,e);E({g:m,i:e,h:h});return h.innerHTML};window.prettyPrint=function(a){function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;p=0){var k=k.match(g),f,b;if(b= 26 | !k){b=n;for(var o=void 0,c=b.firstChild;c;c=c.nextSibling)var i=c.nodeType,o=i===1?o?b:c:i===3?N.test(c.nodeValue)?b:o:o;b=(f=o===b?void 0:o)&&"CODE"===f.tagName}b&&(k=f.className.match(g));k&&(k=k[1]);b=!1;for(o=n.parentNode;o;o=o.parentNode)if((o.tagName==="pre"||o.tagName==="code"||o.tagName==="xmp")&&o.className&&o.className.indexOf("prettyprint")>=0){b=!0;break}b||((b=(b=n.className.match(/\blinenums\b(?::(\d+))?/))?b[1]&&b[1].length?+b[1]:!0:!1)&&D(n,b),d={g:k,h:n,i:b},E(d))}}p