- 浏览: 230344 次
- 性别:
- 来自: 广州
文章分类
最新评论
-
飞出四季做的茧:
只是转移了恶心的switch吧,并没有去除掉
java 代码重构-第一章(使用策略模式,把恶心的switch代码去掉...) 一 -
niqingyang:
https://www.cnblogs.com/xueduan ...
百度搜索url编码解密(url encode decode) -
niqingyang:
function urldecode(str, charset ...
百度搜索url编码解密(url encode decode) -
ttdeye:
private Jedis getJedis(){if(jed ...
spring 结合 Redis 例子,简单入门例子 -
adair_java:
最近手机baidu,搜索关键字编码好像加密了比如这个:http ...
各搜索引擎referer关键字,编码
上一篇文章:java 代码重构-第一章(提炼代码)
说到了代码的提炼,把一些代码提炼出来写成一个方法,然后再去调用它,好了不多说了,想了解看上一篇吧
去除临时变量
正如我在前面提过的,临时变量可能是个问题。它们只在自己所属的函数中有效,所以它们会助长「冗长而复杂」的函数。这里我们有两个临时变量,两者都是用来从Customer 对象相关的Rental 对象中获得某个总量。不论ASCII 版或HTML 版都需要这些总量。我打算运用 Replace Temp with Query,并利用所谓的query method 来取代totalAmount 和frequentRentalPoints 这两个临时变量。由于class 内的任何函数都可以取用(调用)上述所谓query methods ,所以它能够促进较干净的设计,而非冗长复杂的函数:
首先:我们看回旧的代码
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()) { 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; } }
我以Customer class 的getTotalCharge() 取代totalAmount 。
代码变成下面:
class Customer { ... /** * 通计清单 * * @return */ public String statement() { int frequentRentePoints = 0; Enumeration<Rental> enu_rentals = rentals.elements(); String result = "Rental Record for " + this.getName() + " \n"; while (enu_rentals.hasMoreElements()) { Rental each = enu_rentals.nextElement(); frequentRentePoints += each.getFrequentRenterPoints(); result += "\t" + each.getMovie().getTitle() + "\t" + String.valueOf(each.getCharge()) + "\n"; } result += "Amount owed is " + String.valueOf(getTotalCharge()) + "\n"; result += "You earned " + String.valueOf(frequentRentePoints) + " frequent renter points"; return result; } // 译注:此即所谓query method private double getTotalCharge() { double result = 0; Enumeration<Rental> enu_rentals = rentals.elements(); while (enu_rentals.hasMoreElements()) { Rental each = (Rental) enu_rentals.nextElement(); result += each.getCharge(); } return result; } }
重构之后,重新编译并测试,然后以同样手法处理frequentRenterPoints。
class customer ... public String statement() { 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" + } //add footer lines result += "Amount owed is " + String.valueOf(getTotalCharge()) + "\n"; result += "You earned " + String.valueOf(frequentRenterPoints) + " frequent renter points"; return result; }
添加方法代码如下:
class customer ... /** * 通计清单 * * @return */ public String statement() { Enumeration<Rental> enu_rentals = rentals.elements(); String result = "Rental Record for " + this.getName() + " \n"; while (enu_rentals.hasMoreElements()) { Rental each = enu_rentals.nextElement(); result += "\t" + each.getMovie().getTitle() + "\t" + String.valueOf(each.getCharge()) + "\n"; } result += "Amount owed is " + String.valueOf(getTotalCharge()) + "\n"; result += "You earned " + String.valueOf(getTotalFrequentRenterPoints()) + " frequent renter points"; return result; } // 此即所谓query method private double getTotalCharge() { double result = 0; Enumeration<Rental> enu_rentals = rentals.elements(); while (enu_rentals.hasMoreElements()) { Rental each = (Rental) enu_rentals.nextElement(); result += each.getCharge(); } return result; } // 此即所谓query method private int getTotalFrequentRenterPoints() { int result = 0; Enumeration<Rental> enu_rentals = rentals.elements(); while (enu_rentals.hasMoreElements()) { Rental each = (Rental) enu_rentals.nextElement(); result += each.getFrequentRenterPoints(); } return result; }
图1.8至图1.11分别以UML class diagram(类图)和interaction diagram (交互作用图)展示statement() 重构前后的变化。
对比
想一想:
做完这次重构,有必要停下来思考一下。大多数重构都会减少代码总量,但这次却增加了代码总量,那是因为Java 1.1需要大量语句(statements)来设置一个总和(summing)循环。哪怕只是一个简单的总和循环,每个元素只需一行代码,外围的支持代码也需要六行之多。这其实是任何程序员都熟悉的习惯写法,但代码数量还是太多了。
这次重构存在另一个问题,那就是性能。原本代码只执行while 循环一次,新版本要执行三次。如果循环耗时很多,就可能大大降低程序的性能。单单为了这个原因,许多程序员就不愿进行这个重构动作。但是请注意我的用词:如果和可能。除非我进行评测(profile),否则我无法确定循环的执行时间,也无法知道这个循环是否被经常使用以至于影响系统的整体性能。重构时你不必担心这些,优化时你才需要担心它们,但那时候你已处于一个比较有利的位置,有更多选择可以完成有效优化
现在,Customer class 内的任何代码都可以取用这些query methods 了,如果系统他处需要这些信息,也可以轻松地将query methods 加入Customer class 接口。如果没有这些query methods ,其他函数就必须了解Rental class,并自行建立循环。在一个复杂系统中,这将使程序的编写难度和维护难度大大增加。
你可以很明显看出来,htmlStatement() 和statement() 是不同的。现在,我应该脱下「重构」的帽子,戴上「添加功能」的帽子。我可以像下面这样编写htmlStatement() ,并添加相关测试。
htmlStatement代码
class sustumer... /** * 输出html清单 * * @return */ public String htmlStatement() { Enumeration<Rental> enu_rentals = rentals.elements(); String result = "<H1>Rentals for <EM>" + getName() + "</EM></ H1><P>\n"; while (enu_rentals.hasMoreElements()) { Rental each = (Rental) enu_rentals.nextElement(); // show figures for each rental result += each.getMovie().getTitle() + ": " + String.valueOf(each.getCharge()) + "<BR>\n"; } // add footer lines result += "<P>You owe <EM>" + String.valueOf(getTotalCharge()) + "</EM><P>\n"; result += "On this rental you earned <EM>" + String.valueOf(getTotalFrequentRenterPoints()) + "</EM> frequent renter points<P>"; return result; }
通过计算逻辑的提炼,我可以完成一个htmlStatement() ,并复用(reuse)原本statements() 内的所有计算。我不必剪剪贴贴,所以如果计算规则发生改变,我只需在程序中做一处修改。完成其他任何类型的报表也都很快而且很容易。这次重构并不花很多时间,其中大半时间我用来弄清楚代码所做的事,而这是我无论如何都得做的。
前述有些重构码系从ASCII 版本里头拷贝过来——主要是循环设置部分。更深入的重构动作可以清除这些重复代码。我可以把处理表头(header)、表尾(footer)和报表细目的代码都分别提炼目出来。在 Form Template Method 实例中,.你可以看到如何做这些动作。但是,现在用户又开始嘀咕了,他们准备修改影片分类规则。我们尚未清楚他们想怎么做,但似乎新分类法很快就要引入,现有的分类法马上就要变更。与之相应的费用计算方式和常客积点计算方式都还待决定,现在就对程序做修改,肯定是愚蠢的。我必须进入费用计算和常客积点计算中,把「因条件 而异的代码」(译注:指的是switch 语句内的case 子句)替换掉,这样才能为 将来的改变镀上一层保护膜。现在,请重新戴回「重构」这顶帽子。
那么下面是完整代码:
Customer
package com.mkfree.refactoring.shap1; import java.util.Enumeration; import java.util.Vector; /** * 顾客 * * @author hk * * 2012-12-25 下午10:59:03 */ public class Customer { private String name; private Vector<Rental> rentals = new Vector<>(); public Customer(String name) { this.name = name; } /** * 添加 * * @param rental */ public void addRentals(Rental rental) { rentals.add(rental); } public String getName() { return name; } /** * 输出html清单 * * @return */ public String htmlStatement() { Enumeration<Rental> enu_rentals = rentals.elements(); String result = "<H1>Rentals for <EM>" + getName() + "</EM></ H1><P>\n"; while (enu_rentals.hasMoreElements()) { Rental each = (Rental) enu_rentals.nextElement(); // show figures for each rental result += each.getMovie().getTitle() + ": " + String.valueOf(each.getCharge()) + "<BR>\n"; } // add footer lines result += "<P>You owe <EM>" + String.valueOf(getTotalCharge()) + "</EM><P>\n"; result += "On this rental you earned <EM>" + String.valueOf(getTotalFrequentRenterPoints()) + "</EM> frequent renter points<P>"; return result; } /** * 通计清单 * * @return */ public String statement() { Enumeration<Rental> enu_rentals = rentals.elements(); String result = "Rental Record for " + this.getName() + " \n"; while (enu_rentals.hasMoreElements()) { Rental each = enu_rentals.nextElement(); result += "\t" + each.getMovie().getTitle() + "\t" + String.valueOf(each.getCharge()) + "\n"; } result += "Amount owed is " + String.valueOf(getTotalCharge()) + "\n"; result += "You earned " + String.valueOf(getTotalFrequentRenterPoints()) + " frequent renter points"; return result; } // 此即所谓query method private double getTotalCharge() { double result = 0; Enumeration<Rental> enu_rentals = rentals.elements(); while (enu_rentals.hasMoreElements()) { Rental each = (Rental) enu_rentals.nextElement(); result += each.getCharge(); } return result; } // 此即所谓query method private int getTotalFrequentRenterPoints() { int result = 0; Enumeration<Rental> enu_rentals = rentals.elements(); while (enu_rentals.hasMoreElements()) { Rental each = (Rental) enu_rentals.nextElement(); result += each.getFrequentRenterPoints(); } return result; } }
Movie
package com.mkfree.refactoring.shap1; /** * 电影类 * @author hk * * 2012-12-25 下午10:55:14 */ 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 String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public int getPriceCode() { return priceCode; } public void setPriceCode(int priceCode) { this.priceCode = priceCode; } }
Rental
package com.mkfree.refactoring.shap1; /** * 租凭 * * @author hk * * 2012-12-25 下午10:57:00 */ public class Rental { private Movie movie; private int daysRented; public Rental(Movie movie, int daysRented) { this.movie = movie; this.daysRented = daysRented; } public Movie getMovie() { return movie; } public int getDaysRented() { return daysRented; } 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; } /** * * @return */ int getFrequentRenterPoints() { if ((getMovie().getPriceCode() == Movie.NEW_RELEASE) && getDaysRented() > 1) return 2; else return 1; } }
Client
package com.mkfree.refactoring.shap1; import org.junit.Test; public class Client { @Test public void testStatement() { Movie movie1 = new Movie(); movie1.setTitle("少林足球"); movie1.setPriceCode(1); Rental rental1 = new Rental(movie1, 2); Movie movie2 = new Movie(); movie2.setTitle("大话西游"); movie2.setPriceCode(2); Rental rental2 = new Rental(movie2, 3); Customer customer = new Customer("oyhk"); customer.addRentals(rental1); customer.addRentals(rental2); String statement = customer.statement(); System.out.println(statement); System.out.println("------------------------------------------------"); String htmlStatment = customer.htmlStatement(); System.out.println(htmlStatment); } }
最后输出的结果:有两个 1 statement 2 htmlStatement
发表评论
-
springMvc 注解配置例子(hello world)
2013-02-21 10:53 2757oyhk 学习笔记 用spring mvc 已经有一段时间 ... -
Spring Data MongoDB 去掉_class属性字段
2013-02-08 10:03 12637oyhk 学习笔记 Spring Dat ... -
springMvc 三种接收客户端参数方法
2013-02-04 12:23 2507oyhk学习笔记springMvc 三种接收客户端参数方法 ... -
spring MongoDB 集成(分页)
2013-01-26 08:42 15162oyhk 学习笔记 spring MongoDB 集成(分 ... -
spring MongoDB 集成crud操作(简单封装)
2013-01-23 08:35 7627oyhk 学习笔记 这两 ... -
elasticsearch结合spring springmvc jest 使用做成WEB架构
2013-01-17 12:16 5244oyhk 学习笔记 上一篇文章,说到了先利用jest ju ... -
elasticsearch RESTful搜索引擎-(java jest 使用[入门])
2013-01-14 10:01 21187oyhk学习笔记 elasticsearch简称ES ... -
eclispe freemarker ide 插件安装
2013-01-12 11:44 2299oyhk学习笔记 由于本网站(http://blog. ... -
elasticsearch RESTful搜索引擎-安装
2013-01-11 09:53 1811oyhk 学习笔记... 1.首先下载e ... -
elasticsearch RESTful搜索引擎-简介
2013-01-10 11:31 1683oyhk学习笔记 搜索了一些资料...关于elasti ... -
java把html标签字符转普通字符(反转换成html标签)
2013-01-09 08:28 13531oyhk 学习笔记 下面是java把html标签字符转 ... -
spring 结合 Redis 例子,简单入门例子
2013-01-07 09:25 11287oyhk 学习笔记 好了费话不多说了,介绍下sprin ... -
java 代码重构-第一章(使用策略模式,把恶心的switch代码去掉...) 二
2013-01-07 09:13 2977上一篇文章:java 代码重构-第一章(使用策略模式,把 ... -
java 代码重构-第一章(使用策略模式,把恶心的switch代码去掉...) 一
2013-01-06 09:49 5741上一篇文章:java 代码重构-第一章(终于…我们来到继 ... -
java 代码重构-第一章(终于…我们来到继承(Inheritance))
2013-01-05 09:18 1728上一篇文章:java 代码重构-第一章(运用多态(Pol ... -
java 代码重构-第一章(运用多态(Polymorphism)取代与价格相关的条件逻辑)
2013-01-04 18:40 2043上一篇文章:java 代码重 ... -
java 代码重构-第一章(提炼代码)
2013-01-04 01:39 1095上篇文章说了,类做回自己的事 上一篇文章:jav ... -
java 代码重构-第一章(类自己该做自己的事)
2012-12-30 22:19 1761重构小提示:重构技术系以微小的步伐修改程序。如果你犯下错 ... -
java 代码重构-第一章(分解并重组statement())
2012-12-28 11:34 1297上一篇文章:java 代码重构-第一章(起点) 下一篇 ... -
java 代码重构-第一章(起点)
2012-12-27 12:57 1541oyhk 学习笔记 对于重构,大家应该都一些认识了吧. ...
相关推荐
在Java编程中,代码重构是一种常见的实践,尤其在大型项目中,为了保持代码的健康状态和持续优化,重构是必不可少的。 1. **重构的目的** - 提高代码质量:通过消除冗余代码,简化复杂的逻辑,使得代码更加清晰...
### Java代码重构:掌握优化现有代码的艺术 #### 引言 在软件开发的过程中,随着项目的不断演进,代码库往往会出现复杂度增加、可读性和可维护性下降的问题。这时,进行代码重构变得至关重要。重构是指在不改变...
第1章 重构,第一个案例 1 1.1 起点 1 1.2 重构的第一步 7 1.3 分解并重组statement() 8 1.4 运用多态取代与价格相关的条件逻辑 34 1.5 结语 52 第2章 重构原则 53 2.1 何谓重构 53 2.2 为何重构 ...
《重构-第3章 代码的坏味道》是软件开发领域的一本经典著作,由Martin Fowler所著。这本书深入探讨了如何识别并消除代码中的不良设计模式,以提高代码质量、可读性和可维护性。在第三章中,作者详细列举了多种"代码...
Java 代码重构经验分享 Java 代码重构是指在不改变外部行为的情况下,修改代码的内部结构,以提高代码的可维护性、可读性和可扩展性。本文总结了 Java 代码重构的经验和技术规范,包括重构要求、重构的工作、代码的...
本书《从Java到Kotlin:重构指南》由Duncan McGregor与Nat Pryce共同编写,是一本专为已经熟悉Java编程语言并希望将其现有代码转换为Kotlin的开发者准备的书籍。书中不仅提供了丰富的重构技巧,还包括了实用的示例和...
### Java代码重构经验总结 在软件开发过程中,代码重构是一项重要的技能,它旨在不改变代码外部行为的前提下,改进其内部结构,从而提升代码质量和可维护性。本文将深入探讨Java代码重构的关键点,涵盖重构原则、...
java开发经典书籍,重构--改善既有代码的设计_中文版 java开发经典书籍,重构--改善既有代码的设计_中文版 java开发经典书籍,重构--改善既有代码的设计_中文版
6. **消除临时变量**: 在某些情况下,临时变量的存在只是为了传递数据,这种情况下可以考虑使用函数引用来直接传递,减少变量的使用。 7. **使用断言**:在关键位置添加断言,可以帮助在开发过程中尽早发现错误,...
重构-改善既有代码的设计.pdf重构-改善既有代码的设计.pdf
Java代码重构示例 Java代码重构示例 Java代码重构示例 Java代码重构示例 Java代码重构示例 Java代码重构示例 Java代码重构示例
Java代码重构是一种优化编程实践,旨在改进代码的结构和可读性,而不改变其外部行为。重构对于提高软件质量和维护性至关重要,尤其是在大型项目中。以下是一些在Java重构中的关键原则和技巧,通过实例来展示如何应用...
java代码重构以前忽视了,最近在看 字字珠玑,相见恨晚
软件设计是演进过程,而重构是设计演进的基本方法。重构是指不改变软件行为的前提下,修改程序内部结构。重构说简单,做不简单。首先,需要知道代码的好坏,即代码异味,设计原则等。其次,需要以自动测试作为保障。
Java代码重构是一个重要的软件开发实践,它涉及到对现有代码的改进,目的是提高代码的可读性、可维护性和整体质量,而不改变其外在行为。重构对于大型项目尤其关键,因为随着时间的推移,代码可能会变得复杂且难以...
Java 8 OpenJDK AMD64 是一个针对64位AMD架构的开源Java开发和运行环境,由OpenJDK项目提供。OpenJDK是Java Development Kit(JDK)的一个实现,它遵循GNU General Public License(GPL)条款,旨在提供一个免费、...
在压缩包文件"src"中,通常包含了与《重构》第一章相关的示例代码或练习素材。读者可以通过分析和实践这些代码,更好地理解和应用书中的重构技巧。 总之,Martin Fowler的《重构--提高既有代码的设计》第一章为我们...
这一过程对于诸如静态代码分析、代码生成、代码重构等应用场景尤为重要。 #### Abstract Syntax Tree (AST) 当JavaParser处理Java源代码时,它会创建一个称为抽象语法树的数据结构。AST是一种树形结构,其中每个...