前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到教程。

买了《重构 - 改善既有代码的设计 》一书,一直没有好好看,大致过了下也觉得只是有点点印象而已,最后还是决定把代码敲一次,记录一下这些学习过程。

第一例:租赁影片

程序说明:顾客租了哪些影片,租期多长,根据租赁时间和影片类型算出费用和积分。

1. 分解并重组statement()

原代码如下有3个类。

package bean;
/*** 租赁订单* @author Administrator*/
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;}}
package bean;/*** 影片* @author Administrator*/
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) {this._title = _title;this._priceCode = _priceCode;}public int getPriceCode() {return _priceCode;}public void setPriceCode(int _priceCode) {this._priceCode = _priceCode;}public String getTitle() {return _title;}}
package bean;import java.util.Enumeration;
import java.util.Vector;/*** 顾客* @author Administrator*/
public class Customer {private String _name; // 顾客名字private Vector _rentals = new Vector();  // 租赁订单数组public Customer(String name) {super();this._name = name;}public void addRental(Rental arg){_rentals.addElement(arg);}public String getName() {return _name;}/*** 生成订单* @return*/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();// 确定每种片子的租金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;}break;}// 增加积分frequentRenterPoints ++;// 新片+租赁时间达2天  积分+1 if(each.getMovie().getPriceCode() == Movie.NEW_RELEASE && each.getDaysRented() > 1){frequentRenterPoints ++;}// 本次租赁记录说明result += "\t"+each.getMovie().getTitle()+"\t"+ String.valueOf(thisAmount)+"\n";totalAmount += thislAmount;}// 页脚result +="Amount owed is "+ String.valueOf(totalAmount)+"\n";result +="You eared "+String.valueOf(frequentRenterPoints)+"frequent renter points";return result;}}

1.1 抽离switch 语句到独立方法

1.1.1 Customer类改为:

package bean;import java.util.Enumeration;
import java.util.Vector;/*** 顾客* @author Administrator*/
public class Customer {private String _name; // 顾客名字private Vector _rentals = new Vector();  // 租赁订单数组public Customer(String name) {super();this._name = name;}public void addRental(Rental arg){_rentals.addElement(arg);}public String getName() {return _name;}/*** 生成订单* @return*/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); // 计算租金// 增加积分frequentRenterPoints ++; // 新片+租赁时间达2天  积分+1 if(each.getMovie().getPriceCode() == Movie.NEW_RELEASE && each.getDaysRented() > 1){frequentRenterPoints ++;}// 本次租赁记录说明result += "\t"+each.getMovie().getTitle()+"\t"+ String.valueOf(thisAmount)+"\n";totalAmount += thisAmount;}// 页脚result +="Amount owed is "+ String.valueOf(totalAmount)+"\n";result +="You eared "+String.valueOf(frequentRenterPoints)+"frequent renter points";return result;}// 计算租金private int amountFor(Rental each){int 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;}break;}return thisAmount;}}

1.1.2 注意 amountFor 方法的返回类型应该是double类型:

Customer类改为:

package bean;import java.util.Enumeration;
import java.util.Vector;/*** 顾客* @author Administrator*/
public class Customer {private String _name; // 顾客名字private Vector _rentals = new Vector();  // 租赁订单数组public Customer(String name) {super();this._name = name;}public void addRental(Rental arg){_rentals.addElement(arg);}public String getName() {return _name;}/*** 生成订单* @return*/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); // 计算租金// 增加积分frequentRenterPoints ++; // 新片+租赁时间达2天  积分+1 if(each.getMovie().getPriceCode() == Movie.NEW_RELEASE && each.getDaysRented() > 1){frequentRenterPoints ++;}// 本次租赁记录说明result += "\t"+each.getMovie().getTitle()+"\t"+ String.valueOf(thisAmount)+"\n";totalAmount += thisAmount;}// 页脚result +="Amount owed is "+ String.valueOf(totalAmount)+"\n";result +="You eared "+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;}break;}return thisAmount;}}

1.3  变量名称应该见名知意:好的代码应该清楚的表达出自己的功能,变量名称是代码清晰的一个关键。

amountFor方法中局部变量 thisAmount , 参数each 要改名。

改变量名 , Customer类改为:

package bean;import java.util.Enumeration;
import java.util.Vector;/*** 顾客* @author Administrator*/
public class Customer {private String _name; // 顾客名字private Vector _rentals = new Vector();  // 租赁订单数组public Customer(String name) {super();this._name = name;}public void addRental(Rental arg){_rentals.addElement(arg);}public String getName() {return _name;}/*** 生成订单* @return*/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); // 计算租金// 增加积分frequentRenterPoints ++; // 新片+租赁时间达2天  积分+1 if(each.getMovie().getPriceCode() == Movie.NEW_RELEASE && each.getDaysRented() > 1){frequentRenterPoints ++;}// 本次租赁记录说明result += "\t"+each.getMovie().getTitle()+"\t"+ String.valueOf(thisAmount)+"\n";totalAmount += thisAmount;}// 页脚result +="Amount owed is "+ String.valueOf(totalAmount)+"\n";result +="You eared "+String.valueOf(frequentRenterPoints)+"frequent renter points";return result;}// 计算租金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;}}

1.4 函数应该放在它所使用的数据所属的对象中, 顾客租金的计算应该移动到Rental类中去。

Rental 类改为 :  (计算租金方法去掉参数并改名 )

package bean;
/*** 租赁订单* @author Administrator*/
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;}}

原顾客类中amountFor方法直接调用计算租金方法即可,Customer 类改为:

package bean;import java.util.Enumeration;
import java.util.Vector;/*** 顾客* @author Administrator*/
public class Customer {private String _name; // 顾客名字private Vector _rentals = new Vector();  // 租赁订单数组public Customer(String name) {super();this._name = name;}public void addRental(Rental arg){_rentals.addElement(arg);}public String getName() {return _name;}/*** 生成订单* @return*/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); // 计算租金// 增加积分frequentRenterPoints ++; // 新片+租赁时间达2天  积分+1 if(each.getMovie().getPriceCode() == Movie.NEW_RELEASE && each.getDaysRented() > 1){frequentRenterPoints ++;}// 本次租赁记录说明result += "\t"+each.getMovie().getTitle()+"\t"+ String.valueOf(thisAmount)+"\n";totalAmount += thisAmount;}// 页脚result +="Amount owed is "+ String.valueOf(totalAmount)+"\n";result +="You eared "+String.valueOf(frequentRenterPoints)+"frequent renter points";return result;}// 计算租金private double amountFor(Rental aRental){return aRental.getCharge();}}

1.5  去掉旧函数amountFor,直接调用新函数getCharge。Customer类改为 :

package bean;import java.util.Enumeration;
import java.util.Vector;/*** 顾客* @author Administrator*/
public class Customer {private String _name; // 顾客名字private Vector _rentals = new Vector();  // 租赁订单数组public Customer(String name) {super();this._name = name;}public void addRental(Rental arg){_rentals.addElement(arg);}public String getName() {return _name;}/*** 生成订单* @return*/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(); // 计算租金// 增加积分frequentRenterPoints ++; // 新片+租赁时间达2天  积分+1 if(each.getMovie().getPriceCode() == Movie.NEW_RELEASE && each.getDaysRented() > 1){frequentRenterPoints ++;}// 本次租赁记录说明result += "\t"+each.getMovie().getTitle()+"\t"+ String.valueOf(thisAmount)+"\n";totalAmount += thisAmount;}// 页脚result +="Amount owed is "+ String.valueOf(totalAmount)+"\n";result +="You eared "+String.valueOf(frequentRenterPoints)+"frequent renter points";return result;}}

1.6  尽量去掉临时变量,临时变量会导致大量参数的传递,没有必要。 thisAmount 是个多余的临时变量,直接去掉。

Customer 改为:

package bean;import java.util.Enumeration;
import java.util.Vector;/*** 顾客* @author Administrator*/
public class Customer {private String _name; // 顾客名字private Vector _rentals = new Vector();  // 租赁订单数组public Customer(String name) {super();this._name = name;}public void addRental(Rental arg){_rentals.addElement(arg);}public String getName() {return _name;}/*** 生成订单* @return*/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 ++; // 新片+租赁时间达2天  积分+1 if(each.getMovie().getPriceCode() == Movie.NEW_RELEASE && each.getDaysRented() > 1){frequentRenterPoints ++;}// 本次租赁记录说明result += "\t"+each.getMovie().getTitle()+"\t"+ String.valueOf(each.getCharge())+"\n";totalAmount += each.getCharge();}// 页脚result +="Amount owed is "+ String.valueOf(totalAmount)+"\n";result +="You eared "+String.valueOf(frequentRenterPoints)+"frequent renter points";return result;}}

1.7 把积分计算方法放到Rental类中,写为常客积分计算方法(getFrequentRenterPoints),并改变Customer类中积分计算代码 。

Rental类改为:

package bean;
/*** 租赁订单* @author Administrator*/
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;}// 常客积分计算int getFrequentRenterPoints(){// 增加积分int frequentRenterPoints =0;frequentRenterPoints++; // (新片+租赁时间达2天  积分+1 )if(getMovie().getPriceCode() == Movie.NEW_RELEASE && getDaysRented() > 1){frequentRenterPoints ++;}return frequentRenterPoints;}}

Customer类中statement方法改为:

/*** 生成订单* @return*/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();// 本次租赁记录说明result += "\t"+each.getMovie().getTitle()+"\t"+ String.valueOf(each.getCharge())+"\n";totalAmount += each.getCharge();}// 页脚result +="Amount owed is "+ String.valueOf(totalAmount)+"\n";result +="You eared "+String.valueOf(frequentRenterPoints)+"frequent renter points";return result;}

1.8 去掉statement方法中的2个临时变量:totalAmount 和 frequentRenterPoints 。抽离出对应计算方法,并调用。

Customer 类改为:

package bean;import java.util.Enumeration;
import java.util.Vector;/*** 顾客* @author Administrator*/
public class Customer {private String _name; // 顾客名字private Vector _rentals = new Vector();  // 租赁订单数组public Customer(String name) {super();this._name = name;}public void addRental(Rental arg){_rentals.addElement(arg);}public String getName() {return _name;}/*** 生成订单* @return*/public String statement(){Enumeration rentals = _rentals.elements();String result = "Rental Record for "+ getName() + "\n";while( rentals.hasMoreElements()){Rental each = (Rental)rentals.nextElement();// 本次租赁记录说明result += "\t"+each.getMovie().getTitle()+"\t"+ String.valueOf(each.getCharge())+"\n";}// 页脚result +="Amount owed is "+ String.valueOf(getTotalCharge())+"\n";result +="You eared "+String.valueOf(getTotalFrequentRenterPoints())+"frequent renter points";return result;}// 计算总积分private int getTotalFrequentRenterPoints(){int result = 0;Enumeration rentals = _rentals.elements();while(rentals.hasMoreElements()){Rental each = (Rental)rentals.nextElement();result += each.getFrequentRenterPoints();}return result;}// 计算总租金private double getTotalCharge(){double result = 0;Enumeration rentals = _rentals.elements();while(rentals.hasMoreElements()){Rental each = (Rental)rentals.nextElement();result += each.getCharge();}return result;}}

本步改写作用说明:

这里虽然从1个循环变为3个,但是多了2个查询函数。

1)  使得Customer 类中的任何代码都可以调用这些查询函数。

2)  若系统其它部分需要这些信息,也可以轻松地将查询函数加入 Customer 类接口。而若没有这些查询波函数,其它函数就必须了解  Rental 类,并自行建立循环。

1.9 加功能:打印凭条。

statement 方法改为 htmlStatement :

/*** 生成订单(打印凭条)* @return*/public String htmlStatement(){Enumeration rentals = _rentals.elements();String result = "<P><H1>Rentals for <EM> "+ getName() + "</EM></H1></P>\n";while( rentals.hasMoreElements()){Rental each = (Rental)rentals.nextElement();// 本次租赁记录说明result += each.getMovie().getTitle()+":"+ String.valueOf(each.getCharge())+"<BR>\n";}// 页脚result +="<P>You owe <EM>"+ String.valueOf(getTotalCharge())+"</EM></P>\n";result +="<P> on this rental you earned <EM> "+String.valueOf(getTotalFrequentRenterPoints())+"</EM> frequent renter points </P>";return result;}

未完,见  : 重构-改善既有代码的设计-第1例:租赁影片(2)

《重构-改善既有代码的设计》-第1例:租赁影片(1)相关推荐

  1. 《重构-改善既有代码的设计》-第1例:租赁影片(2)

    前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家.点击跳转到教程. 上接  重构-改善既有代码的设计-第1例:租赁影片(1) 2  运用多态取代与价格相关的条件逻辑 2 ...

  2. 重构—改善既有代码的设计

    概述 1.1 参考资料 <重构-改善既有代码的设计>读后总结 <重构改善既有代码的设计> 22种代码的坏味道,一句话概括 1.2 何谓重构 首先要说明的是:视上下文不同,重构的 ...

  3. PHP 杂谈《重构-改善既有代码的设计》之二 对象之间搬移特性

    思维导图 索引: Ø Move Method(搬移函数) Ø Move Field (搬移值域) Ø Extract Class (提炼类) Ø Inline Class (将类内联化,就是把当前的类 ...

  4. 『重构--改善既有代码的设计』读书笔记----序

    作为C++的程序员,我从大学就开始不间断的看书,看到如今上班,也始终坚持每天多多少少阅读技术文章,书看的很多,但很难有一本书,能让我去反复的翻阅.但唯独『重构--改善既有代码的设计』这本书让我重复看了 ...

  5. PHP 杂谈《重构-改善既有代码的设计》之三 重新组织数据

    介绍 承接上文的PHP 杂谈<重构-改善既有代码的设计>之 重新组织你的函数继续重构方面的内容. 这章主要针对数据的重构. 1.争论的声音--直接访问Field还是通过函数(Accesso ...

  6. 重构 改善既有代码的设计:代码的坏

    以下内容来自<<重构 改善既有代码的设计>> 一.什么是重构 所谓重构(Refactoring)是这样一个过程:在不改变代码外在行为的前提下,对代码做出修改以改进程序的内部结构 ...

  7. 实践提高《重构改善既有代码的设计第2版》PDF中文+PDF英文+对比分析

    重构是编程的基础,是在不改变外部行为的前提下,有条不紊地改善代码.编程爱好者都知道,Martin Fowler 的<重构:改善既有代码的设计>已经成为全球有经验的程序员手中的利器,既可用来 ...

  8. 重构改善既有代码的设计(github源码)

    refactoring improving the design of existing code(重构改善既有代码的设计) https://github.com/CoderDream/refacto ...

  9. 重构:改善既有代码的设计(软件开发的不朽经典)

    重构:改善既有代码的设计(软件开发的不朽经典) 基本信息 作者: (美)Martin Fowler   译者: 熊节[同译者作品] 丛书名: 图灵程序设计丛书 出版社:人民邮电出版社 ISBN:978 ...

最新文章

  1. 博客园markdown语法扩展
  2. python日期函数_python 时间及日期函数
  3. 表格外粗内细怎么设置_金游艇复刻机芯-N厂灰游艇价格 | 复刻表评测
  4. SQL Server安装问题程序被挂起的错误解决办法
  5. 【QT编程】QT介绍——What‘s QT?
  6. 微信小程序 禁止弹框下面的内容滑动
  7. 如何在你朋友面前伪装黑客7(程序代码)
  8. Blender建模练习:人物模型多边形建模流程图解(一核心布线篇)
  9. Livezilla安装配置, APE(Ajax Push Engine)安装配置
  10. win764位loadrunner安装问题:提示:少了Microsoft Visual c++2005 sp1运行时组件,安装时会提示命令行选项语法错误,键入“命令/?”可获取帮肋信息,无法正常安装;
  11. 黑苹果鼠标不动_MacOS系统:解决黑果睡眠唤醒后假死问题(如键盘鼠标无反应等)...
  12. rand和srand函数的用法
  13. 网页游戏运营模式研究
  14. SparkEnv源码解读
  15. 【练习】获取新浪搜索中的热搜榜的标题
  16. arcgis的python安装包_用Python使用arcgis中站点包arcpy
  17. C++-Cmake指令:cmake_minimum_required
  18. Error querying database. Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You hav
  19. 凯泽窗带通滤波器MATLAB,用凯泽窗设计线性相位带通FIR滤波器
  20. hg版本管理工具使用

热门文章

  1. 舒尔特注意力训练表格_舒尔特注意力训练,舒尔特方格练习入口
  2. 怎么查询共享使用人_企业微信微盘怎么共享使用?企业微信如何设置微盘权限?...
  3. 2019年总结【跨越今天,更不平凡】
  4. LeetCode 375. 猜数字大小 II
  5. 【HDU - 5876】Sparse Graph(补图bfs,STLset)
  6. 【POJ - 2594】Treasure Exploration(floyd传递闭包 + 最小路径覆盖,图论)
  7. 【牛客 - 297B】little w and Sum(水题,前缀和)
  8. 【HDU - 1216 】Assistance Required (模拟,类似素数打表,不是素数问题!)
  9. Python之Numpy入门实战教程(2):进阶篇之线性代数
  10. linux 使用gzip压缩打包的文件,linux常用的解压,压缩,打包