第九讲 重构到模式. 什么是重构( Refactoring ) 所谓重构是这样一个过程:在不改变代码外在行为的 前提下,对代码做出修改,以改进程序的内部结构。

Slides:



Advertisements
Similar presentations
第十二章 常微分方程 返回. 一、主要内容 基本概念 一阶方程 类 型 1. 直接积分法 2. 可分离变量 3. 齐次方程 4. 可化为齐次 方程 5. 全微分方程 6. 线性方程 类 型 1. 直接积分法 2. 可分离变量 3. 齐次方程 4. 可化为齐次 方程 5. 全微分方程 6. 线性方程.
Advertisements

Refactoring: Improving the Design of Existing Code © Martin Fowler, Martin Fowler.
概率统计( ZYH ) 节目录 3.1 二维随机变量的概率分布 3.2 边缘分布 3.4 随机变量的独立性 第三章 随机向量及其分布 3.3 条件分布.
PHPCMS 使用指南及二次开发向导 --- 系统设置篇 PHPCMS 网络培训课程 --- 系统设置篇 PHPCMS 项目部 王官庆制作 系统相关设置 1. 站点管理 2. 发布点管理 3. 系统其它设置 管理员设置 1. 角色定义 2. 管理员管理.
石化的 IT 大挑战 洛阳石化工程公司. 石化公司简介 中国石化集团洛阳石油化工工程公司,是国内能源化 工领域集技术专利商与工程承包商于一体的高科技企 业。拥有中国综合设计甲级资质,为国家首批业务涵 盖 21 个行业的工程咨询企业之一,拥有工程总承包、 工程设计、工程监理、工程咨询和环境影响评价等甲.
一、拟合优度检验 二、变量的显著性检验 三、参数的置信区间
计算机 在分析化学的应用 ( 简介 ) 陈辉宏. 一. 概述 信息时代的来临, 各门学科的研究方法都 有了新的发展. 计算机的介入, 为分析化学的进展提供了 一种更方便的研究方法.
编译程序 构造原理和实现技术 授课教师:吕江花. 第一章 编译程序概述 主要内容: 几个基本概念 编译器的工作过程概述 编译器各个阶段的功能描述 编译程序的实现途径.
嵌入式操作系统 陈香兰 Fall 系统调用 10/27/09 嵌入式 OS 3/12 系统调用的意义  操作系统为用户态进程与硬件设备进行交互提供 了一组接口 —— 系统调用  把用户从底层的硬件编程中解放出来  极大的提高了系统的安全性  使用户程序具有可移植性.
第 2 章 数据流分析 内容概述 数据流分析推导的是数据沿着程序执行路 径流动的信息 – 过程内的分析:可用表达式分析、到达-定值分 析等 – 过程间分析 –Shape 分析 – 理论基础 – 数据流方程的求解.
吉林大学远程教育课件 主讲人 : 杨凤杰学 时: 64 ( 第六十二讲 ) 离散数学. 最后,我们构造能识别 A 的 Kleene 闭包 A* 的自动机 M A* =(S A* , I , f A* , s A* , F A* ) , 令 S A* 包括所有的 S A 的状态以及一个 附加的状态 s.
1 为了更好的揭示随机现象的规律性并 利用数学工具描述其规律, 有必要引入随 机变量来描述随机试验的不同结果 例 电话总机某段时间内接到的电话次数, 可用一个变量 X 来描述 例 检测一件产品可能出现的两个结果, 也可以用一个变量来描述 第五章 随机变量及其分布函数.
主讲教师:陈殿友 总课时: 124 第八讲 函数的极限. 第一章 机动 目录 上页 下页 返回 结束 § 3 函数的极限 在上一节我们学习数列的极限,数列 {x n } 可看作自变量 为 n 的函数: x n =f(n),n ∈ N +, 所以,数列 {x n } 的极限为 a, 就是 当自变量 n.
吉林大学远程教育课件 主讲人 : 杨凤杰学 时: 64 ( 第三十八讲 ) 离散数学. 第八章 格与布尔代数 §8.1 引 言 在第一章中我们介绍了关于集 合的理论。如果将 ρ ( S )看做 是集合 S 的所有子集组成的集合, 于是, ρ ( S )中两个集合的并 集 A ∪ B ,两个集合的交集.
吉林大学远程教育课件 主讲人 : 杨凤杰学 时: 64 ( 第四十八讲 ) 离散数学. 例 设 S 是一个非空集合, ρ ( s )是 S 的幂集合。 不难证明 :(ρ(S),∩, ∪,ˉ, ,S) 是一个布尔代数。 其中: A∩B 表示 A , B 的交集; A ∪ B 表示 A ,
第二部分 行政法律关系主体 第一节 行政主体 一、行政主体 (一)行政主体的概念 cc (二)行政主体资格含义及构成要件 CASE1CASE1\CASE2CASE2 (三)行政主体的职权和职责 1 、行政职权的概念及内容 2 、行政职权的特点 3 、行政职责.
协同工作环境研究中心 协同共享 助力科研. 主要内容  认识协同及协同软件  协同科研软件 duckling 介绍.
线性代数习题课 吉林大学 术洪亮 第一讲 行 列 式 前面我们已经学习了关 于行列式的概念和一些基本 理论,其主要内容可概括为:
吉林大学远程教育课件 主讲人 : 杨凤杰学 时: 64 ( 第二十五讲 ) 离散数学. 定理 群定义中的条件 ( 1 )和( 2 )可以减弱如下: ( 1 ) ’ G 中有一个元素左壹适合 1 · a=a; ( 2 ) ’ 对于任意 a ,有一个元素左逆 a -1 适 合 a -1 ·
第二章 随机变量及其分布 第一节 随机变量及其分布函数 一、随机变量 用数量来表示试验的基本事件 定义 1 设试验 的基本空间为 , ,如果对试验 的每一个基 本事件 ,规定一个实数记作 与之对应,这样就得到一个定义在基本空 间 上的一个单值实函数 ,称变量 为随机变量. 随机变量常用字母 、 、 等表示.或用.
第 4 章 过程与变量的作用范围. 4.1 Visual Basic 的代码模块 Visual Basic 的应用程序是由过程组成的, 过程代码存放在模块中。 Visual Basic 提供了 三类模块,它们是窗体模块、标准模块和类 模块。 窗体模块 窗体模块是大多数 Visual Basic.
数 学 系 University of Science and Technology of China DEPARTMENT OF MATHEMATICS 第 3 章 曲线拟合的最小二乘法 给出一组离散点,确定一个函数逼近原函数,插值是这样的一种手段。 在实际中,数据不可避免的会有误差,插值函数会将这些误差也包括在内。
吉林大学远程教育课件 主讲人 : 杨凤杰学 时: 64 ( 第三十九讲 ) 离散数学. 例 设 S 是一个集合, ρ ( S )是 S 的幂集合,集合 的交( ∩ ),并(∪)是 ρ ( S )上的两个代数运算, 于是,( ρ ( S ), ∩ ,∪) 是一个格。而由例 知.
实验三: 用双线性变换法设计 IIR 数字滤波器 一、实验目的 1 熟悉用双线性变换法设计 IIR 数字滤波器的原理与方法。 2 掌握数字滤波器的计算机仿真方法。 3 通过观察对实际心电图信号的滤波作用, 获得数字滤波的感性知 识。
OS 进程调度模拟演示 制作人: 钱晶 高上上. OS 进程调度模拟-实验原理 静态优先级原理 在这种方式下,系统一旦把处理机分配给就绪队 列中的优先权最高的进程后,该进程便一直执行下去, 直至完成。或因为发生某事件使该进程放弃处理机,系 统方可再将处理机分配给另一优先级最高的进程。这些 事件包括有优先级更高的进程进入,或是因为某些原因.
1 第 7 章 专家控制系统 概述 专家系统的起源与发展 专家系统的一般结构 专家系统的知识表示和获取 专家系统的特点及分类.
信息利用与学术论文写作 Library of Jiangsu University, Zhenjiang Sha Zhenjiang
流态化 概述 一、固体流态化:颗粒物料与流动的流体接触,使颗粒物料呈类 似于流体的状态。 二、流态化技术的应用:流化催化裂化、吸附、干燥、冷凝等。 三、流态化技术的优点:连续化操作;温度均匀,易调节和维持; 气、固间传质、传热速率高等。 四、本章基本内容: 1. 流态化基本概念 2. 流体力学特性 3.
数 学 系 University of Science and Technology of China DEPARTMENT OF MATHEMATICS 第 5 章 解线性方程组的直接法 实际中,存在大量的解线性方程组的问题。很多数值方 法到最后也会涉及到线性方程组的求解问题:如样条插值的 M 和.
在发明中学习 线性代数 概念的引入 李尚志 中国科学技术大学. 随风潜入夜 : 知识的引入 之一、线性方程组的解法 加减消去法  方程的线性组合  原方程组的解是新方程的解 是否有 “ 增根 ” ?  互为线性组合 : 等价变形  初等变换  高斯消去法.
东南大学计算中心 网站应用与实践 主讲人 吴俊. 2 东南大学计算中心 网站制作流程  确定主题、风格  规划栏目、收集素材  版面设计、配色  编辑页面  测试发布 FrontPage 要完成的任务.
第一节 相图基本知识 1 三元相图的主要特点 (1)是立体图形,主要由曲面构成; (2)可发生四相平衡转变; (3)一、二、三相区为一空间。
9的乘法口诀 1 .把口诀说完全。 二八( ) 四六( ) 五八( ) 六八( ) 三七( ) 三八( ) 六七( ) 五七( ) 五六( ) 十六 四十八 四十二 二十四 二十一 三十五 四十 二十四 三十 2 .口算, 并说出用的是哪句口诀。 8×8= 4×6= 7×5= 6×8= 5×8=
第五节 学习要点 对句子的分析,向来是从句型、句类、句式的角度进行的。 以这三个角度为切入点,我们可以建立句型系统、句类系统和句 式系统。 ★句型系统 —— 按照句子的结构模式划分出来的类型系统。
量子力学教程 ( 第二版 ) 3.4 连 续 谱 本 征 函 数 的 归 一 化 连续谱本征函数是不能归一化的 一维粒子的动量本征值为的本征函数 ( 平面波 ) 为 可以取 中连续变化的一切实数值. 不难看出,只要则 在量子力学中, 坐标和动量的取值是连续变化 的 ; 角动量的取值是离散的.
第 3 章 控制流分析 内容概述 – 定义一个函数式编程语言,变量可以指称函数 – 以 dynamic dispatch problem 为例(作为参数的 函数被调用时,究竟执行的是哪个函数) – 规范该控制流分析问题,定义什么是可接受的控 制流分析 – 定义可接受分析在语义模型上的可靠性 – 讨论分析算法.
吉林大学远程教育课件 主讲人 : 杨凤杰学 时: 64 ( 第五十三讲 ) 离散数学. 定义 设 G= ( V , T , S , P ) 是一个语法结构,由 G 产生的语言 (或者说 G 的语言)是由初始状态 S 演绎出来的所有终止符的集合, 记为 L ( G ) ={w  T *
Relevance Theory Lecture 12. Relevance Theory 交际研究的对象是交际的概念、内容、 性质、功能、方法和交际行为、交际参 加者之间的关系等的认识和阐述。 20 世 纪 70 年代末以来, Sperber & Wilson 把认 知与交际结合起来,于 1986.
编译原理总结. 基本概念  编译器 、解释器  编译过程 、各过程的功能  编译器在程序执行过程中的作用  编译器的实现途径.
 符号表  标识符的作用: 声明部分:定义了各种对象及对应的属性和 使用规则。 程序体:对所定义的对象进行各种操作。 $ididname IdnameAttributeIR  必要性 Token : 新表-符号表(种类、类型等信息):
数 学 系 University of Science and Technology of China DEPARTMENT OF MATHEMATICS 第 8 章 常微分方程 实际中,很多问题的数学模型都是微分方程。我们 可以研究它们的一些性质。但是,只有极少数特殊的方程 有解析解。对于绝大部分的微分方程是没有解析解的。
Department of Mathematics 第二章 解析函数 第一节 解析函数的概念 与 C-R 条件 第二节 初等解析函数 第三节 初等多值函数.
1 第 7 章 存储过程、触发器和程序包 在很多时候,都需要保存 PL/SQL 程序块,以便 随后可以重新使用。这也意味着,程序块需要一个名 称,这样需才可以调用或者引用它。命名的 PL/SQL 程序块可被独立编译并存储在数据库中,任何与数据 库相连接的应用程序都可以访问这些存储的 PL/SQL 程序块。
首 页 首 页 上一页 下一页 本讲内容本讲内容 视图,剖视图(Ⅰ) 复习: P107 ~ P115 作业: P48(6-2,6-4), P49( 去 6-6) P50, P51(6-13), P52 P50, P51(6-13), P52 P53 (6-18,6-20) P53 (6-18,6-20)
《 UML 分析与设计》 交互概述图 授课人:唐一韬. 知 识 图 谱知 识 图 谱知 识 图 谱知 识 图 谱.
1 、如果 x + 5 > 4 ,那么两边都 可得 x >- 1 2 、在- 3y >- 4 的两边都乘以 7 可得 3 、在不等式 — x≤5 的两边都乘以- 1 可得 4 、将- 7x — 6 < 8 移项可得 。 5 、将 5 + a >- 2 a 移项可得 。 6 、将- 8x < 0.
新一代移动物联系统 MC MOBILE CONTROL 开启办公物联网时代 Product Promotion.
本讲主要内容: 1. 如何登陆网站后台 2. 基础信息修改 3. 网站文章类型资料更新 4. 网站图片类型资料更新 5. 网站链接类型资料更新 本讲主要目的: 在结束本讲内容之后,能够按照客户的需求对网站的资料 进行实时更新操作。
Java 水晶报表. 目录 水晶报表开发 (CrystalReports 2008) Java 水晶报表 (JBuilder 2005) 报表项目布署 (WebSphere v6.1)
Refactoring: Improving the Design of Existing Code © Martin Fowler, Martin Fowler.
EC 营客通产品操作(九) EC 营客通产品操作(九) 400 电话 400 电话. 400 电话有助于提升企业形象,无论企业地址变更、机构 变化、人员变动,联系方式永远不变。且在 EC 上申请的 400 电话可以在 EC 平台上进行统一的 400 电话接听及 400 电话客服的管理。
脱口而出 即兴演讲的结构. 即兴演讲的价值 即兴演讲的价值 即兴演讲的结构 即兴演讲的结构 即兴演讲的要求 即兴演讲的要求 即兴演讲的评估 即兴演讲的评估.
§10.2 对偶空间 一、对偶空间与对偶基 二、对偶空间的有关结果 三、例题讲析.
请同学们仔细观察下列两幅图有什么共同特点? 如果两个图形不仅形状相同,而且每组对应点所在的直线 都经过同一点, 那么这样的两个图形叫做位似图形, 这个点叫做位 似中心.
表单自定义 “ 表单自定义 ” 功能是用于制作表单的 工具,用数飞 OA 提供的表单自定义 功能能够快速制作出内容丰富、格 式规范、美观的表单。
力的合成 力的合成 一、力的合成 二、力的平行四边形 上一页下一页 目 录 退 出. 一、力的合成 O. O. 1. 合力与分力 我们常常用 一个力来代替几个力。如果这个 力单独作用在物体上的效果与原 来几个力共同作用在物体上的效 果完全一样,那么,这一个力就 叫做那几个力的合力,而那几个 力就是这个力的分力。
数学广角——优化 沏茶问题.
个体 精子 卵细胞 父亲 受精卵 母亲 人类生活史 问题:人类产生配子(精、卵 细胞)是不是有丝分裂?
算得清写的准 —— 物业费公示报告的编写 讲师:朱芸 物业费的 构成? 哪些是管 理人员工 资呢? 哪些算工程费 用? 怎样才能核 算的清楚呢?
逻辑设计基础 1 第 7 章 多级与(或)非门电路 逻辑设计基础 多级门电路.
“ 百链 ” 云图书馆. 什么是百链云图书馆?1 百链云图书馆的实际效果?2 百链云图书馆的实现原理?3 百链云图书馆的价值?44 图书馆要做什么?55 提 纲.
参考文献管理软件. 常见参考文献管理软件 : 中文 : CNKI E-learning (目前免费) NoteExpress NoteFirst 英文: Refworks Endnote (网络版免费) Mendeley……
感谢您的关注 联系电话: – 677 手机: QQ :
Refactoring Improving code after it has been written.
1 第三章 数列 数列的概念 考点 搜索 ●数列的概念 ●数列通项公式的求解方法 ●用函数的观点理解数列 高考 猜想 以递推数列、新情境下的 数列为载体, 重点考查数列的通 项及性质, 是近年来高考的热点, 也是考题难点之所在.
第二节. 广告牌为什么会被风吹倒? 结构的稳定性: 指结构在负载的作用下 维持其原有平衡状态的能力。 它是结构的重要性质之一。
目录 上页 下页 返回 结束 二、无界函数反常积分的审敛法 * 第五节 反常积分 无穷限的反常积分 无界函数的反常积分 一、无穷限反常积分的审敛法 反常积分的审敛法  函数 第五章 第五章.
SCI 数据库检索练习参考 本练习完全依照 SCI 数据库实际检索过程而 实现。 本练习完全依照 SCI 数据库实际检索过程而 实现。 练习中,选择了可以举一反三的题目,读 者可以根据题目进行另外的检索练习,如: 可将 “ 与 ” 运算检索改为 “ 或 ” 、 “ 非 ” 运算检索 等等。 练习中,选择了可以举一反三的题目,读.
§7.2 估计量的评价标准 上一节我们看到,对于总体 X 的同一个 未知参数,由于采用的估计方法不同,可 能会产生多个不同的估计量.这就提出一 个问题,当总体的一个参数存在不同的估 计量时,究竟采用哪一个好呢?或者说怎 样评价一个估计量的统计性能呢?下面给 出几个常用的评价准则. 一.无偏性.
A (Very) Simple Example Consolidate duplicate conditional fragments if (isSpecialDeal()) { total = price * 0.95; send (); } else { total = price * 0.98;
Presentation transcript:

第九讲 重构到模式

什么是重构( Refactoring ) 所谓重构是这样一个过程:在不改变代码外在行为的 前提下,对代码做出修改,以改进程序的内部结构。

Extract Method 名称: Extract Method 动机:过长的函数或者一段需要注释才能让人理解用途 的代码

Extract Method 做法: 创造一个新函数,根据这个函数的意图来给它命名(以它做什么来命名,而不是以 它怎样做命名) 将提炼出的代码从源函数拷贝到新建的目标函数中 仔细检查提炼出的代码,看看其中是否引用了作用域限于源函数的变量(包括局部 变量和源函数参数) 检查是否有仅用于被提炼码的临时变量,如果有,在目标函数中将它们声明为临时 变量 检查被提炼码,看看是否有任何局部变量的值被它改变,如果一个临时变量值被修 改了,看看是否可以将被提炼码处理为一个查询,并将结果赋值给相关变量,如果 很难这样做,或如果被修改的变量不止一个,你就不能仅仅将这段代码原封不动地 提炼出来,你可能需要先使用 Split Temporary Variable ,然后再尝试提炼,也可以 使用 Replace Temp with Query 将临时变量消灭掉 将被提炼码中需要读取的局部变量,当作参数传给目标函数 处理完所有局部变量之后,进行编译 在源函数中,将被提炼码替换为对目标函数的调用 编译,测试

Extract Method 范例: void printOwing(double amount) { printBanner(); //print details System.out.println ("name:" + _name); System.out.println ("amount" + amount); } ↓ void printOwing(double amount) { printBanner(); printDetails(amount); } void printDetails (double amount) { System.out.println ("name:" + _name); System.out.println ("amount" + amount); }

重构 - 示例 这是一个影碟出租店用的程序,计算每一位顾客的消 费金额并打印报表( Statement )。操作者告诉程序: 顾客租了哪些影片、租期多长,程序便根据租赁时间 和影片类型算出费用。影片分为三类:普通片、儿童 片和新片。除了计算费用,还要为顾客计算点数;点 数会随着租片种类是否为新片而有所不同。

重构 - 示例

Movie Movie 只是一个简单的 data class (纯数据类) public class Movie { public static final int CHILDRENS = 2; public static final int REGULAR = 0; public static final int NEW_RELEASE = 1; private String _title; private int _priceCode; public Movie(String title, int priceCode) { _title = title; _priceCode = priceCode; } public int getPriceCode() { return _priceCode; } public void setPriceCode(int arg) { _priceCode = arg; } public String getTitle (){ return _title; }; }

Rental Rental class 表示 某个顾客租了一部影片 class Rental { private Movie _movie; private int _daysRented; public Rental(Movie movie, int daysRented) { _movie = movie; _daysRented = daysRented; } public int getDaysRented() { return _daysRented; } public Movie getMovie() { return _movie; }

Customer Customer class 用来表示顾客,就像其他 classes 一样,它也拥有数据和相 应的访问函数( accessor ): class Customer { private String _name; private Vector _rentals = new Vector(); public Customer (String name){ _name = name; }; public void addRental(Rental arg) { _rentals.addElement(arg); } public String getName (){ return _name; };

Customer 提供报表方法的交互过 程

报表相应代码 public String statement() { double totalAmount = 0; int frequentRenterPoints = 0; Enumeration rentals = _rentals.elements(); String result = "Rental Record for " + getName() + "\n"; while (rentals.hasMoreElements()) { double thisAmount = 0; Rental each = (Rental) rentals.nextElement(); //determine amounts for each line switch (each.getMovie().getPriceCode()) { case Movie.REGULAR: thisAmount += 2; if (each.getDaysRented() > 2) 16 thisAmount += (each.getDaysRented() - 2) * 1.5; break; case Movie.NEW_RELEASE: thisAmount += each.getDaysRented() * 3; break; case Movie.CHILDRENS: thisAmount += 1.5; if (each.getDaysRented() > 3) thisAmount += (each.getDaysRented() - 3) * 1.5; break; } // add frequent renter points frequentRenterPoints ++; // add bonus for a two day new release rental if ((each.getMovie().getPriceCode() == Movie.NEW_RELEASE) && each.getDaysRented() > 1) frequentRenterPoints ++; //show figures for this rental result += "\t" + each.getMovie().getTitle()+ "\t" + String.valueOf(thisAmount) + "\n"; totalAmount += thisAmount; } //add footer lines result += "Amount owed is " + String.valueOf(totalAmount) + "\n"; result += "You earned " + String.valueOf(frequentRenterPoints) + " frequent renter points"; return result; }

起始程序的讨论 问题:考虑一下增加 HTML 格式打印报表的需求;改 变影片分类规则 如果你发现自己需要为程序添加一个特性,而代码结 构使你无法很方便地那么做,那就先重构那个程序, 使特性的添加比较容易进行,然后再添加特性

重构的第一步 重构的第一个步骤永远相同:为即将修改的代码建立 一组可靠的测试环境。这些测试必须有自我检测( self-checking )能力。

分解并重组 statement() 第一个步骤是找出代码的逻辑泥团( logical clump ) 并运用 Extract Method

修改后的 statement 方法 public String statement() { double totalAmount = 0; int frequentRenterPoints = 0; Enumeration rentals = _rentals.elements(); String result = "Rental Record for " + getName() + "\n"; while (rentals.hasMoreElements()) { double thisAmount = 0; Rental each = (Rental) rentals.nextElement(); thisAmount = amountFor(each); // add frequent renter points frequentRenterPoints ++; // add bonus for a two day new release rental if ((each.getMovie().getPriceCode() == Movie.NEW_RELEASE) && each.getDaysRented() > 1) frequentRenterPoints ++; //show figures for this rental 20 result += "\t" + each.getMovie().getTitle()+ "\t" + String.valueOf(thisAmount) + "\n"; totalAmount += thisAmount; } //add footer lines result += "Amount owed is " + String.valueOf(totalAmount) + "\n"; result += "You earned " + String.valueOf(frequentRenterPoints) + " frequent renter points"; return result; } private double amountFor(Rental each) { double thisAmount = 0; switch (each.getMovie().getPriceCode()) { case Movie.REGULAR: thisAmount += 2; if (each.getDaysRented() > 2) thisAmount += (each.getDaysRented() - 2) * 1.5; break; case Movie.NEW_RELEASE: thisAmount += each.getDaysRented() * 3; break; case Movie.CHILDRENS: thisAmount += 1.5; if (each.getDaysRented() > 3) thisAmount += (each.getDaysRented() - 3) * 1.5; 21 break; } return thisAmount; }

改名后的代码 private double amountFor(Rental aRental) { double result = 0; switch (aRental.getMovie().getPriceCode()) { case Movie.REGULAR: result += 2; if (aRental.getDaysRented() > 2) result += (aRental.getDaysRented() - 2) * 1.5; break; case Movie.NEW_RELEASE: result += aRental.getDaysRented() * 3; break; case Movie.CHILDRENS: result += 1.5; if (aRental.getDaysRented() > 3) result += (aRental.getDaysRented() - 3) * 1.5; break; } return result; }

转移 金额计算 代码 通过观察 amountFor(), 可以发现这个函数使用了来自 Rental class 信息,却没有使用来自 customer class 的信息 private double amountFor(Rental aRental) { double result = 0; switch (aRental.getMovie().getPriceCode()) { case Movie.REGULAR: result += 2; if (aRental.getDaysRented() > 2) result += (aRental.getDaysRented() - 2) * 1.5; 23 break; case Movie.NEW_RELEASE: result += aRental.getDaysRented() * 3; break; case Movie.CHILDRENS: result += 1.5; if (aRental.getDaysRented() > 3) result += (aRental.getDaysRented() - 3) * 1.5; break; } return result; }

Move Method Public double getCharge() { double result = 0; switch (getMovie().getPriceCode()) { case Movie.REGULAR: result += 2; if (getDaysRented() > 2) result += (getDaysRented() - 2) * 1.5; break; case Movie.NEW_RELEASE: result += getDaysRented() * 3; break; case Movie.CHILDRENS: result += 1.5; if (getDaysRented() > 3) result += (getDaysRented() - 3) * 1.5; break; } return result; }

Move Method Customer.amountFor() 函数内的处理,使它委托( delegate) 新函数: private double amountFor(Rental aRental) { return aRental.getCharge(); }

Move Method 下一步 找出程序对于旧函数的所有引用点,并修改它们让它们改用新函数 class Customer public String statement() { double totalAmount = 0; int frequentRenterPoints = 0; Enumeration rentals = _rentals.elements(); String result = "Rental Record for " + getName() + "\n"; while (rentals.hasMoreElements()) { double thisAmount = 0; Rental each = (Rental) rentals.nextElement(); thisAmount = each.getCharge(); // add frequent renter points 25 frequentRenterPoints ++; // add bonus for a two day new release rental if ((each.getMovie().getPriceCode() == Movie.NEW_RELEASE) && each.getDaysRented() > 1) frequentRenterPoints ++; //show figures for this rental result += "\t" + each.getMovie().getTitle()+ "\t" + String.valueOf(thisAmount) + "\n"; totalAmount += thisAmount; } //add footer lines result += "Amount owed is " + String.valueOf(totalAmount) + "\n"; result += "You earned " + String.valueOf(frequentRenterPoints) + " frequent renter points"; return result; }

转移金额计算函数后,所有 classes 的状态( state )

Replace Temp with Query public String statement() { double totalAmount = 0; int frequentRenterPoints = 0; Enumeration rentals = _rentals.elements(); String result = "Rental Record for " + getName() + "\n"; while (rentals.hasMoreElements()) { Rental each = (Rental) rentals.nextElement(); // add frequent renter points frequentRenterPoints ++; // add bonus for a two day new release rental if ((each.getMovie().getPriceCode() == Movie.NEW_RELEASE) && each.getDaysRented() > 1) frequentRenterPoints ++; //show figures for this rental result += "\t" + each.getMovie().getTitle()+ "\t" + String.valueOf (each.getCharge()) + "\n"; totalAmount += each.getCharge(); } //add footer lines result += "Amount owed is " + String.valueOf(totalAmount) + "\n"; result += "You earned " + String.valueOf(frequentRenterPoints) + " frequent renter points"; return result; }

提炼顾客积分计算代码 public String statement() { double totalAmount = 0; int frequentRenterPoints = 0; Enumeration rentals = _rentals.elements(); String result = "Rental Record for " + getName() + "\n"; while (rentals.hasMoreElements()) { Rental each = (Rental) rentals.nextElement(); // add frequent renter points frequentRenterPoints ++; // add bonus for a two day new release rental if ((each.getMovie().getPriceCode() == Movie.NEW_RELEASE) && each.getDaysRented() > 1) frequentRenterPoints ++; //show figures for this rental result += "\t" + each.getMovie().getTitle()+ "\t" + String.valueOf(each.getCharge()) + "\n"; totalAmount += each.getCharge(); } //add footer lines result += "Amount owed is " + String.valueOf(totalAmount) + "\n"; result += "You earned " + String.valueOf(frequentRenterPoints) + " frequent renter points"; return result; }

提炼后代码 public String statement() { double totalAmount = 0; int frequentRenterPoints = 0; Enumeration rentals = _rentals.elements(); String result = "Rental Record for " + getName() + "\n"; while (rentals.hasMoreElements()) { Rental each = (Rental) rentals.nextElement(); frequentRenterPoints += each.getFrequentRenterPoints(); //show figures for this rental result += "\t" + each.getMovie().getTitle()+ "\t" + String.valueOf(each.getCharge()) + "\n"; totalAmount += each.getCharge(); } //add footer lines result += "Amount owed is " + String.valueOf(totalAmount) + "\n"; result += "You earned " + String.valueOf(frequentRenterPoints) + " frequent renter points"; return result; } class Rental... int getFrequentRenterPoints() { if ((getMovie().getPriceCode() == Movie.NEW_RELEASE) && getDaysRented() > 1) return 2; else return 1; }

顾客积分计算函数提炼及转移后

结论 重构技术系以微小的步伐修改程序。如果你犯下错误 ,很容易便可发现它。 写出人类容易理解的代码,才是优秀的程序员

重构 重构:对软件内部结构的一种调整,目的是在不改变 软件的可观察行为前提下,提高其可理解性,降低其 修改成本 一般而言重构都是对软件的小改动,但重构可以包含 另一个重构。例如 Extract Class 通常包含 Move Method 和 Move Field

为何重构 重构可以改进软件设计。重构就像是在整理代码,让 所有东西回到应该的位置上。 重构使软件更易被理解 重构可以帮助寻找 bugs 重构帮助提高编程速度。良好的设计是快速软件开发 的根本,恶劣的设计会是开发速度下降,引起耗时的 调试,增加新功能,维护等。

何时重构 三次法则( The Rule of Three ) 第一次做某件事时只管去做;第二次做类似的事会产生反 感,但无论如何还是做了;第三次再做类似的事,你就 应该重构!

Design SmellsThe Odors of Rotting Software The software is rotting when it starts to exhibit any of the following odors Rigidity Fragility Immobility Viscosity Needless complexity Needless repetition Opacity

Rigidity Rigidity is the tendency for software to be difficult to change, even in simple ways. A design is rigid if a single change causes a cascade of subsequent changes in dependent modules. The more modules that must be changed, the more rigid the design. Most developers have faced this situation in one way or another. They are asked to make what appears to be a simple change. They look the change over and make a reasonable estimate of the work required. But later, as they work through the change, they find that there are unanticipated repercussions to the change. The developers find themselves chasing the change through huge portions of the code, modifying far more modules than they had first estimated, and discovering thread after thread of other changes that they must remember to make. In the end, the changes take far longer than the initial estimate. When asked why their estimate was so poor, they repeat the traditional software developers lament: "It was a lot more complicated than I thought!"

Fragility Fragility is the tendency of a program to break in many places when a single change is made. Often, the new problems are in areas that have no conceptual relationship with the area that was changed. Fixing those problems leads to even more problems, and the development team begins to resemble a dog chasing its tail. As the fragility of a module increases, the likelihood that a change will introduce unexpected problems approaches certainty. This seems absurd, but such modules are not at all uncommon. These are the modules that are continually in need of repair, the ones that are never off the bug list. These modules are the ones that the developers know need to be redesigned, but nobody wants to face the spectre of redesigning them. These modules are the ones that get worse the more you fix them.

Immobility A design is immobile when it contains parts that could be useful in other systems, but the effort and risk involved with separating those parts from the original system are too great. This is an unfortunate but very common occurrence.

Viscosity Viscosity comes in two forms: viscosity of the software and viscosity of the environment. When faced with a change, developers usually find more than one way to make that change. Some of the ways preserve the design; others do not (i.e., they are hacks). When the design-preserving methods are more difficult to use than the hacks, the viscosity of the design is high. It is easy to do the wrong thing but difficult to do the right thing. We want to design our software such that the changes that preserve the design are easy to make. Viscosity of environment comes about when the development environment is slow and inefficient. For example, if compile times are very long, developers will be tempted to make changes that don't force large recompiles, even though those changes don't preserve the design. If the source code control system requires hours to check in just a few files, developers will be tempted to make changes that require as few check-ins as possible, regardless of whether the design is preserved. In both cases, a viscous project is one in which the design of the software is difficult to preserve. We want to create systems and project environments that make it easy to preserve and improve the design.

Needless Complexity A design smells of needless complexity when it contains elements that aren't currently useful. This frequently happens when developers anticipate changes to the requirements and put facilities in the software to deal with those potential changes. At first, this may seem like a good thing to do. After all, preparing for future changes should keep our code flexible and prevent nightmarish changes later. Unfortunately, the effect is often just the opposite. By preparing for many contingencies, the design becomes littered with constructs that are never used. Some of those preparations may pay off, but many more do not. Meanwhile, the design carries the weight of these unused design elements. This makes the software complex and difficult to understand.

Needless Repetition Cut and paste may be useful text-editing operations, but they can be disastrous code-editing operations. All too often, software systems are built on dozens or hundreds of repeated code elements. It happens like this: Ralph needs to write some code that fravles the arvadent.[2] He looks around in other parts of the code where he suspects other arvadent fravling has occurred and finds a suitable stretch of code. He cuts and pastes that code into his module and makes the suitable modifications. [2] For those of you who do not have English as your first language, the term fravle the arvadent is composed of nonsense words and is meant to imply some nondescript programming activity. Unbeknownst to Ralph, the code he scraped up with his mouse was put there by Todd, who scraped it out of a module written by Lilly. Lilly was the first to fravle an arvadent, but she realized that fravling an arvadent was very similar to fravling a garnatosh. She found some code somewhere that fravled a garnatosh, cut and paste it into her module, and modified it as necessary. When the same code appears over and over again, in slightly different forms, the developers are missing an abstraction. Finding all the repetition and eliminating it with an appropriate abstraction may not be high on their priority list, but it would go a long way toward making the system easier to understand and maintain. When there is redundant code in the system, the job of changing the system can become arduous. Bugs found in such a repeating unit have to be fixed in every repetition. However, since each repetition is slightly different from every other, the fix is not always the same.

Opacity Opacity is the tendency of a module to be difficult to understand. Code can be written in a clear and expressive manner, or it can be written in an opaque and convoluted manner. Code that evolves over time tends to become more and more opaque with age. A constant effort to keep the code clear and expressive is required in order to keep opacity to a minimum. When developers first write a module, the code may seem clear to them. After all, they have immersed themselves in it and understand it at an intimate level. Later, after the intimacy has worn off, they may return to that module and wonder how they could have written anything so awful. To prevent this, developers need to put themselves in their readers' shoes and make a concerted effort to refactor their code so that their readers can understand it. They also need to have their code reviewed by others.

Why Software Rots In nonagile environments, designs degrade because requirements change in ways that the initial design did not anticipate. Often, these changes need to be made quickly and may be made by developers who are not familiar with the original design philosophy. So, though the change to the design works, it somehow violates the original design. Bit by bit, as the changes continue, these violations accumulate until malignancy sets in. However, we cannot blame the drifting of the requirements for the degradation of the design. We, as software developers, know full well that requirements change. Indeed, most of us realize that the requirements are the most volatile elements in the project. If our designs are failing owing to the constant rain of changing requirements, it is our designs and practices that are at fault. We must somehow find a way to make our designs resilient to such changes and use practices that protect them from rotting. An agile team thrives on change. The team invests little up front and so is not vested in an aging initial design. Rather, the team keeps the design of the system as clean and simple as possible and backs it up with lots of unit tests and acceptance tests. This keeps the design flexible and easy to change. The team takes advantage of that flexibility in order to continuously improve the design; thus, each iteration ends with a system whose design is as appropriate as it can be for the requirements in that iteration.

例子 Copy program structure chart

There are three modules, or subprograms, in the application. The Copy module calls the other two. The Copy program fetches characters from the Read Keyboard module and routes them to the Write Printer module. public class Copier { public static void Copy() { int c; while((c=Keyboard.Read()) != -1) Printer.Write(c); }

First modification of Copy program public class Copier { //remember to reset this flag public static bool ptFlag = false; public static void Copy() { int c; while((c=(ptFlag ? PaperTape.Read() : Keyboard.Read())) != -1) Printer.Write(c); }

Second modification of Copy program public class Copier { //remember to reset these flags public static bool ptFlag = false; public static bool punchFlag = false; public static void Copy() { int c; while((c=(ptFlag ? PaperTape.Read() : Keyboard.Read())) != -1) punchFlag ? PaperTape.Punch(c) : Printer.Write(c); }

Agile version 2 of Copy public interface Reader { int Read(); } public class KeyboardReader : Reader { public int Read() {return Keyboard.Read();} } public class Copier { public static Reader reader = new KeyboardReader(); public static void Copy() { int c; while((c=(reader.Read())) != -1) Printer.Write(c); }

单一职责原则 (SRP) 就一个类而言,应该仅有一个引起它变化的原因。

More than one responsibility

Separated responsibilities

Defining a Responsibility public interface Modem { public void Dial(string pno); public void Hangup(); public void Send(char c); public char Recv(); }

Separating Coupled Responsibilities I kept both responsibilities coupled in the ModemImplementation class. This is not desirable, but it may be necessary. There are often reasons, having to do with the details of the hardware or operating system, that force us to couple things that we'd rather not couple. However, by separating their interfaces, we have decoupled the concepts as far as the rest of the application is concerned. We may view the ModemImplementation class as a kludge or a wart; however, note that all dependencies flow away from it. Nobody needs to depend on this class. Nobody except main needs to know that it exists. Thus, we've put the ugly bit behind a fence. Its ugliness need not leak out and pollute the rest of the application.

Coupled persistence

Conclusion The Single-Responsibility Principle is one of the simplest of the principles but one of the most difficult to get right. Conjoining responsibilities is something that we do naturally. Finding and separating those responsibilities is much of what software design is really about. Indeed, the rest of the principles we discuss come back to this issue in one way or another.

开放 - 封闭原则 (OCP) 软件实体(类、模块、函数等)应该是可以扩展的,但是不可修 改。 When a single change to a program results in a cascade of changes to dependent modules, the design smells of rigidity. OCP advises us to refactor the system so that further changes of that kind will not cause more modifications. If OCP is applied well, further changes of that kind are achieved by adding new code, not by changing old code that already works. This may seem like motherhood and apple piethe golden, unachievable idealbut in fact, there are some relatively simple and effective strategies for approaching that ideal.

Description of OCP 1. They are open for extension. This means that the behavior of the module can be extended. As the requirements of the application change, we can extend the module with new behaviors that satisfy those changes. In other words, we are able to change what the module does. 2. They are closed for modification. Extending the behavior of a module does not result in changes to the source, or binary, code of the module. The binary executable version of the modulewhether in a linkable library, a DLL, or a.EXE fileremains untouched.

Client is not open and closed.

STRATEGY pattern: Client is both open and closed.

TEMPLATE METHOD pattern: Base class is open and closed.

Conclusion In many ways, the Open/Closed Principle is at the heart of object- oriented design. Conformance to this principle is what yields the greatest benefits claimed for object-oriented technology: flexibility, reusability, and maintainability. Yet conformance to this principle is not achieved simply by using an object-oriented programming language. Nor is it a good idea to apply rampant abstraction to every part of the application. Rather, it requires a dedication on the part of the developers to apply abstraction only to those parts of the program that exhibit frequent change. Resisting premature abstraction is as important as abstraction itself.

Liskov 替换原则 (LSP) 子类型必须能够替换掉它们的基类型。 The importance of this principle becomes obvious when you consider the consequences of violating it. Presume that we have a function f that takes as its argument a reference to some base class B. Presume also that when passed to f in the guise of B, some derivative D of B causes f to misbehave. Then D violates LSP. Clearly, D is fragile in the presence of f. The authors of f will be tempted to put in some kind of test for D so that f can behave properly when a D is passed to it. This test violates OCP because now, f is not closed to all the various derivatives of B. Such tests are a code smell that are the result of inexperienced developers or, what's worse, developers in a hurry reacting to LSP violations.

Conclusion The Open/Closed Principle is at the heart of many of the claims made for object- oriented design. When this principle is in effect, applications are more maintainable, reusable, and robust. The Liskov Substitution Principle is one of the prime enablers of OCP. The substitutability of subtypes allows a module, expressed in terms of a base type, to be extensible without modification. That substitutability must be something that developers can depend on implicitly. Thus, the contract of the base type has to be well and prominently understood, if not explicitly enforced, by the code. The term IS-A is too broad to act as a definition of a subtype. The true definition of a subtype is substitutable, where substitutability is defined by either an explicit or implicit contract.

依赖反转原则 (DIP) High-level modules should not depend on low-level modules. Both should depend on abstractions. 抽象不应该依赖于细节。细节应该依赖于抽象

Layering According to Booch, "all well structured object-oriented architectures have clearly- defined layers, with each layer providing some coherent set of services through a well-defined and controlled interface."[1] A naive interpretation of this statement might lead a designer to produce a structure similar to Figure In this diagram, the high-level Policy layer uses a lower-level Mechanism layer, which in turn uses a detailed-level Utility layer. Although this may look appropriate, it has the insidious characteristic that the Policy layer is sensitive to changes all the way down in the Utility layer. Dependency is transitive. The Policy layer depends on something that depends on the Utility layer; thus, the Policy layer transitively depends on the Utility layer. This is very unfortunate.

Naive layering scheme Figure

Inverted layers

Dependence on Abstractions No variable should hold a reference to a concrete class. No class should derive from a concrete class. No method should override an implemented method of any of its base classes.

Naive model of a Button and a Lamp

Dependency inversion applied to Lamp

Generic regulator

Conclusion Traditional procedural programming creates a dependency structure in which policy depends on detail. This is unfortunate, since the policies are then vulnerable to changes in the details. Objectoriented programming inverts that dependency structure such that both details and policies depend on abstraction, and service interfaces are often owned by their clients. Indeed, this inversion of dependencies is the hallmark of good object-oriented design. It doesn't matter what language a program is written in. If its dependencies are inverted, it has an OO design. If its dependencies are not inverted, it has a procedural design. The principle of dependency inversion is the fundamental low-level mechanism behind many of the benefits claimed for object-oriented technology. Its proper application is necessary for the creation of reusable frameworks. It is also critically important for the construction of code that is resilient to change. Since abstractions and details are isolated from each other, the code is much easier to maintain.

接口隔离原则 (ISP) 不应该强迫客户依赖于它们不用的方法。接口属于客户,不属于 它所在的类层次结构。 This principle deals with the disadvantages of "fat" interfaces. Classes whose interfaces are not cohesive have "fat" interfaces. In other words, the interfaces of the class can be broken up into groups of methods. Each group serves a different set of clients. Thus, some clients use one group of methods, and other clients use the other groups. ISP acknowledges that there are objects that require noncohesive interfaces; however, it suggests that clients should not know about them as a single class. Instead, clients should know about abstract base classes that have cohesive interfaces.

TimerClient at top of hierarchy The

The Interface Segregation Principle Clients should not be forced to depend on methods they do not use. When clients are forced to depend on methods they don't use, those clients are subject to changes to those methods. This results in an inadvertent coupling between all the clients. Said another way, when a client depends on a class that contains methods that the client does not use but that other clients do use, that client will be affected by the changes that those other clients force on the class. We would like to avoid such couplings where possible, and so we want to separate the interfaces.

重用发布等价原则( REP ) 重用的粒度就是发布的粒度

共同封闭原则( CCP ) 包中的所有类对于同一类性质的变化应该是共同封闭 的。一个变化若对一个包产生影响,则将对该包中的 所有类产生影响,而对于其他的包不造成任何影响

共同重用原则( CRP ) 一个包中的所有类应该是共同重用的。如果重用了包 中的一个类,那么就要重用包中的所有类

无环依赖原则( ADP ) 在包的依赖关系图中不允许存在环

稳定依赖原则( SDP ) 朝着稳定的方向进行依赖

稳定抽象原则( SAP ) 包的抽象程度应该和其稳定程度一致

谢谢大家! 谢谢大家!