《重构》增强代码可读性
创始人
2025-05-28 08:00:26
0

文章目录

    • 重构原则
      • 何为重构
      • 为何重构
      • 何时重构
      • 重构会影响性能吗
    • 实例
      • 原始类
      • 进行重构
        • 分解statements方法
          • 提取函数
          • 搬移函数
          • 提炼“积分计算”功能
          • 去除临时变量(以查询取代临时变量)
        • 运用多态取代与价格相关的条件逻辑
          • 代码迁移Movie类
          • Price类 状态模式
          • 搬移函数
          • 以多态取代表达式

在这里插入图片描述

重构原则

何为重构

不改变代码本身可观察的行为,进行代码的构造变化

为何重构

  1. 消除重复代码,方便未来的修改
  2. 利用重构协助理解不熟悉的代码
  3. 提高以后维护代码的速率

何时重构

  1. 添加代码时
  2. 修改bug时
  3. 复审代码质量时

重构会影响性能吗

可能会,但原则是先写出能完善的代码,再测试性能

实例

原始类

在这里插入图片描述

Movie类:该类主要记录类型、价格和标题等,是单纯数据类。

/*** Movie记录类型、价格和标题等,单纯数据类。*/
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;}
}

Rental类:表示某位顾客租了一部影片,表示行为。

/*** Rental表示某位顾客租了一部影片,表示行为。*/
class Rental {private Movie movie;private int daysRented;public Rental(Movie movie, int daysRented) {this.movie = movie;this.daysRented = daysRented;}public int getDaysRented() {return daysRented;}public Movie getMovie() {return movie;}
}

Customer类:表示顾客,有数据和相应的访问函数。

/*** Customer表示顾客,有数据和相应的访问函数*/
public class Customer {private String name;private Vector rentals = new Vector();public Customer(String name) {this.name = name;}public void addRental(Rental rental) {rentals.add(rental);}public String getName() {return name;}/*** 提供一个用于生成详单的函数*/public String statement() {double totalAmount = 0;//常客计算积分时使用int frequentRenterPoints = 0;Enumeration enumeration = rentals.elements();String result = "Rental Record for " + getName() + "\n";while (enumeration.hasMoreElements()) {//总金额double thisAmount = 0;Rental each = (Rental) rentals.elements();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++;//如果是新书,另算积分呢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 earned " + String.valueOf(frequentRenterPoints) + " frequent renter points";return result;}
}

进行重构

分解statements方法

长长的函数需要大卸八块,代码块越小,代码的移动和处理也就越轻松。将较小代码块移至更合适的类,降低代码重复使新函数更容易撰写。

提取函数

找出逻辑泥团并运用提取函数方法。本例中的switch语句需提炼至独立函数。找出函数内局部变量和参数。each(未被修改,可以当成参数传入新的函数)和thisAmount(会被修改,格外小心,如果只有一个变量修改,可以将其作为返回值)。那么将新函数返回值返回给thisAmount是可行的。

/*** 提供一个用于生成详单的函数*/
public String statement() {.​...while (enumeration.hasMoreElements()) {//总金额double thisAmount = 0;Rental each = (Rental) rentals.elements();thisAmount = amountFor(each);....}.​...
}/*** 金额计算* @param aRental* @return*/
private double amountFor(Rental aRental) {//注意double、int类型之间的转换。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类的信息却没有使用来自Customer类的信息,函数是应该放在它所使用的数据的对象内的,所以amountFor应该要放到Rental类而非Customer类,调整代码以使用新类。

class Rental {..../*** 金额计算* @return*/public double getCharge() {//注意double、int类型之间的转换。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;}
}
public class Customer {..../*** 提供一个用于生成详单的函数*/public String statement() {....while (enumeration.hasMoreElements()) {...result += "\t" + each.getMovie().getTitle() + "\t" + String.valueOf(each.getCharge()) + "\n";totalAmount += each.getCharge();}.​..}
}
提炼“积分计算”功能

积分计算因影片种类而有所不同,针对“积分计算”代码运用Extract Method重构手法。局部变量each,另一个临时变量是frequentRenterPoints(这个参数在使用之前已初始化,但提炼出的函数并未读取该值,因此无需传入,只需作为新函数的返回值累加上去即可)。

class Rental {.../*** 计算常客积分* @return*/public int getFrequentRenterPoints() {//如果是新书,另算积分呢if (getMovie().getPriceCode() == Movie.NEW_RELEASE && getDaysRented() >= 1) {return 2;} else {return 1;}}
}
public class Customer {..../*** 提供一个用于生成详单的函数*/public String statement() {double totalAmount = 0;//常客计算积分时使用int frequentRenterPoints = 0;Enumeration enumeration = rentals.elements();String result = "Rental Record for " + getName() + "\n";while (enumeration.hasMoreElements()) {Rental each = (Rental) rentals.elements();//计算常客积分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 earned " + String.valueOf(frequentRenterPoints) + " frequent renter points";return result;}
}
去除临时变量(以查询取代临时变量)

临时变量会造成冗长复杂的函数,以查询取代临时变量方法,以查询函数替代totalAmount和frequentRentalPoints临时变量。任何函数均可调用,促成干净设计、减少冗长函数。

public class Customer {.../*** 提供一个用于生成详单的函数*/public String statement() {//常客计算积分时使用Enumeration enumeration = rentals.elements();String result = "Rental Record for " + getName() + "\n";while (enumeration.hasMoreElements()) {Rental each = (Rental) rentals.elements();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;}/*** 获取总积分* @return*/private double getTotalFrequentRenterPoints() {int result = 0;Enumeration enumeration = rentals.elements();while (enumeration.hasMoreElements()) {Rental each = (Rental) rentals.elements();result += each.getFrequentRenterPoints();}return result;}/*** 获取总金额* @return*/private double getTotalCharge() {double result = 0;Enumeration enumeration = rentals.elements();while (enumeration.hasMoreElements()) {Rental each = (Rental) rentals.elements();result +=  each.getCharge();}return result;}
}

运用多态取代与价格相关的条件逻辑

代码迁移Movie类

用户准备修改影片分类规则。费用计算和常客积分计算也会因此而发生改变。因为系统可能会加入新影片类型,不稳定,因此在Movie对象内计算费用。同样的手法处理常客积分函数。

public class Movie {.../*** 根据影片类型获取费用* @param daysRented* @return*/public double getCharge(int daysRented) {double result = 0;switch (getPriceCode()) {case Movie.REGULAR:result += 2;//优惠力度if (daysRented > 2) {result += (daysRented - 2) * 1.5;}break;case Movie.NEW_RELEASE://果然还是新书最贵啊result += daysRented * 3;break;case Movie.CHILDRENS:result += 1.5;if (daysRented > 3) {result += (daysRented - 3) * 1.5;}break;}return result;}public int getFrequentRenterPoints(int daysRented) {//如果是新书,另算积分呢if (getPriceCode() == Movie.NEW_RELEASE && daysRented >= 1) {return 2;} else {return 1;}}
}
class Rental {private Movie movie;private int daysRented;.​../*** 金额计算* @return*/public double getCharge() {return movie.getCharge(daysRented);}/*** 计算常客积分* @return*/public int getFrequentRenterPoints() {return movie.getFrequentRenterPoints(daysRented);}
}
Price类 状态模式

多态取代switch语句,多态设计时不要直接继承Movie,而是通过Price间接去处理,一部影片可以在生命周期内修改自己的分类,但一个对象却不能再生命周期内修改自己所属的类,使用state模式

abstract class Price {abstract  int getPriceCode();
}
public class ChildrenPrice extends Price{@Overrideint getPriceCode() {return Movie.CHILDRENS;}
}
public class NewReleasePrice extends Price {@Overrideint getPriceCode() {return Movie.NEW_RELEASE;}
}
public class RegularPrice extends Price {@Overrideint getPriceCode() {return Movie.REGULAR;}
}
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 Price price;public Movie(String title, int priceCode) {this.title = title;setPriceCode(priceCode);}public int getPriceCode() {return price.getPriceCode();}public void setPriceCode(int arg) {switch (arg) {case Movie.REGULAR:price = new RegularPrice();break;case Movie.NEW_RELEASE:price = new NewReleasePrice();break;case Movie.CHILDRENS:price = new ChildrenPrice();break;default:throw new IllegalArgumentException("Incorrect Price Code");}}....}}
搬移函数

将Movie中的getCharge方法下沉至Price方法中。

abstract class Price {abstract int getPriceCode();/*** 根据影片类型获取费用* @param daysRented* @return*/public double getCharge(int daysRented) {double result = 0;switch (getPriceCode()) {case Movie.REGULAR:result += 2;//优惠力度if (daysRented > 2) {result += (daysRented - 2) * 1.5;}break;case Movie.NEW_RELEASE://果然还是新书最贵啊result += daysRented * 3;break;case Movie.CHILDRENS:result += 1.5;if (daysRented > 3) {result += (daysRented - 3) * 1.5;}break;}return result;}
}
public class Movie {private Price price;.​..public int getPriceCode() {return price.getPriceCode();}....
}
以多态取代表达式

次取出getPriceCode的一个case分支,在对应的类建立覆盖函数。同样的方法处理getFrequentRenterPoints方法

/*** 新建Price类,并提供类型相关的行为,为此,加入抽象函数,并在所有子类中加上对应的具体操作。*/
abstract class Price {/*** 获取影片类型code码* @return*/abstract  int getPriceCode();/*** 根据影片类型获取费用* @param daysRented* @return*/abstract double getCharge(int daysRented);/*** 如果是新书,采用复写的方法,在超类中留下一个已定义的函数,使之成为一种默认行为。* @param daysRented* @return*/int getFrequentRenterPoints(int daysRented) {return 1;}
}
public class RegularPrice extends Price {@Overrideint getPriceCode() {return Movie.REGULAR;}@Overridepublic double getCharge(int daysRented) {double result = 2;//优惠力度if (daysRented > 2) {result += (daysRented - 2) * 1.5;}return result;}
}
public class NewReleasePrice extends Price {@Overrideint getPriceCode() {return Movie.NEW_RELEASE;}@Overridepublic double getCharge(int daysRented) {//果然还是新书最贵啊return daysRented * 3;}@Overridepublic int getFrequentRenterPoints(int daysRented) {return (daysRented > 1) ? 2 : 1;}
}
public class ChildrenPrice extends Price{@Overrideint getPriceCode() {return Movie.CHILDRENS;}@Overridepublic double getCharge(int daysRented) {double result = 1.5;if (daysRented > 3) {result += (daysRented - 3) * 1.5;}return result;}
}

重构之后的类图
在这里插入图片描述

相关内容

热门资讯

“无计可施”的意思 “无计可施”的意思 成语拼音: [wú jì kě shī] ...
“相呴相济”的意思 “相呴相济”的意思 成语拼音: [xiāng xǔ xiāng jì] ...
“财匮力绌”的意思 “财匮力绌”的意思 成语拼音: [cái kuì lì chù] ...
成语肝胆相照的出处释义及典故 成语肝胆相照的出处释义及典故  【出处】(西汉)司马迁《史记·淮阴侯列传》和(宋代)赵令畸《侯鲭录》...
“言信行果”的意思 “言信行果”的意思 成语拼音: [yán xìn xíng guǒ] ...
“升高自下”的意思 “升高自下”的意思 成语拼音: [shēng gāo zì xià] ...
成语百代文宗的解释 关于成语百代文宗的解释  【成语】:百代文宗  【拼音】:bǎi dài wén zōng  【简拼...
赞美父亲的诗歌朗诵 关于赞美父亲的诗歌朗诵6首  在生活中,你读懂父亲的爱了吗?下面是小编收集整理的关于赞美父亲的诗歌朗...
有关马字的成语   有关马字的成语  1、一言既出,驷马难追:一句话说出了口,就是套上四匹马拉的车也难追上。指话说出...
带有沉浮的成语及解释 带有沉浮的成语及解释  [沉浮俯仰] chén fú fǔ yǎng  形容人、事物、时势的盛衰。 ...
金波的诗歌小鹿赏析 金波的诗歌小鹿赏析  金波对平凡事物所蕴含之美的感受力,对真善美的追求和表达,也深刻表达了天人合一的...
赞美运动员的诗歌   运动员是我国的骄傲,快来为他们运加油吧!下面是小编为大家整理的赞美运动员的诗歌,欢迎大家参考! ...
曹操《短歌行》赏析 曹操《短歌行》赏析  《短歌行》是政治性很强的诗作,主要是为曹操当时所实行的政治路线和政治策略服务的...
关于梦想的文艺诗歌5篇 在日复一日的学习、工作或生活中,大家都收藏过令自己印象深刻的诗歌吧,诗歌具有语言高度凝练、篇幅短小精...
六一儿童节简单的诗歌 六一儿童节简单的诗歌(通用29首)  在日复一日的学习、工作或生活中,许多人对一些广为流传的诗歌都不...
劳动诗歌 关于劳动诗歌(精选14首)  无论在学习、工作或是生活中,大家都听说过或者使用过一些比较经典的诗歌吧...
保护环境的诗歌 保护环境的诗歌  在平日的学习、工作和生活里,大家或多或少都接触过一些经典的诗歌吧,诗歌以强烈的节奏...
经典唐诗鉴赏 经典唐诗鉴赏  在学习、工作或生活中,大家都听说过或者使用过一些比较经典的诗歌吧,诗歌在形式上,不是...
诗歌《帆》赏析 诗歌《帆》赏析  《帆》赏析《帆》是一首杰出的具有象征意义的风景哲理抒情诗。它既有风景画面的精彩描绘...
歌颂党的诗歌名篇 歌颂党的诗歌名篇(精选12篇)  从建立至今,中国共产党历经了无数的风雨,是我们最最敬爱的党员先烈们...