读《重构-改善既有代码的设计》 one 第一章案例
前言
和其他博客略有不同的是,我并不希望一篇博客写完正本书的总结,总结是在读完整本书或整个章节中一个“大局"观念。而绝不适用于没有读这本书或这个章节的人。如果你没有读过这本书,我建议你先去读读,而不是翻阅各种博客,寻求某种“方便”的方法论后不了了之。
直接去读这本书和读提炼的内容是不同的,读他的书,你可以体验到作者的描述方式,他的思路,态度,思想,甚至他的为人处世之道。这是我觉得重要的。
当然先看总结也没什么问题,你可以了解大致的内容后,再去深入的读这本书。如果你只做到了前者,那真是得不偿失。
总之,这本书的价值远远超过这篇博客。这篇博客可以给出的一点价值是:如果你也重构完第一章的内容,你就可以拿过来和我这里的对比。如果你觉得你写的更加优秀或者有更好的想法,欢迎在评论区给出你的见解。
重构
所谓重构
(refactoring)是这样一个过程:在不改变代码外在行为的前提 下,对代码做出修改,以改进程序的内部结构。重构是一种经千锤百炼形成的有 条不紊的程序整理方法,可以最大限度地减小整理过程中引入错误的概率。本质 上说,重构就是在代码写好之后改进它的设计。
傻瓜都能写出计算机可以理解的代码。唯有能写出人类容易理解的代码的,才是优秀的程序员。
第一章:重构的第一个案例
读书时的摘录/感悟
-
如果一个方法中传入了某个对象,那么我们可以将方法移入那个类中,采用当前对象调用这个方法。
把它放在它应该在的位置上
。 -
P58:这边他将一些计总数的变量在循环里计数的过程提取出来单独成了一个方法获取总数(
replace temp with Query用查询取代临时变量
)。但是等于重新遍历了一次。但是这里说的是重构,这是为了程序员的方便理解。对于实际项目中如果存在必要的性能需求,那么可能会对于这方面有所忽略,优化过的语句往往难以理解。实际上这里的性能十有八九不会出现问题,即使出现了,我们也会在性能优化的时候去处理,一个良好的代码组织方式,我们往往会找到更好的性能优化方案。如果没有这个重构,你可能难以发现更好的方案。况且,如果真的影响性能严重,将临时变量再放回去简直太简单了。 -
最好不要在另一个对象的属性基础上运用switch语句。
-
State(状态)模式这里的使用方式:使用一个抽象类,然后子类覆写其抽象方法。
-
重构的节奏:测试、小修改、测试、小修改…
小步快走,快速迭代
,事不过三,三则重构
。 -
这是作者个人的编码风格:
永远将函数的返回值命名为“result”
,这样我一眼就能知道它的作用。
书中代码
我按着作者的思路将其代码进行了重构。案例如下:
文章重构前的初始版本:(部分import已略)
import java.util.Enumeration;
import java.util.Vector;
public 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;
}
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)
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;
}
}
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 String getTitle() {
return _title;
}
public void setPriceCode(int priceCode) {
_priceCode = priceCode;
}
}
public 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;
}
}
采用state模式之前的重构结果:
Movie.java
import java.util.Enumeration;
import java.util.Vector;
public class Customer {
/**
* 姓名
*/
private String name;
/**
* 租借记
*/
private Vector rentals = new Vector();
public Customer(String name) {
this.name = name;
};
public void addRental(Rental arg) {
rentals.addElement(arg);
}
public String getName() {
return name;
}
public static void main(String[] args) {
Customer customer = new Customer("John");
String title = "Titanic";
int priceCode = 2;
int daysRented = 7;
Movie movie = new Movie(title, priceCode);
Rental rental = new Rental(movie, daysRented);
customer.addRental(rental);
String result = customer.statement();
System.out.println(result);
}
public double getTotalCharge(){
double result = 0;
Enumeration rentals = this.rentals.elements();
while (rentals.hasMoreElements()) {
Rental each = (Rental) rentals.nextElement();
result += each.getCharge();
}
return result;
}
public int getFrequentRenterPoints(){
int result = 0;
Enumeration rentals = this.rentals.elements();
while (rentals.hasMoreElements()) {
Rental each = (Rental) rentals.nextElement();
result += each.addFrequentRenterPoints();
}
return result;
}
public String statement() {
// 常客积点
Enumeration rentals = this.rentals.elements();
StringBuilder result = new StringBuilder("Rental Record for " + getName() + "\n");
while (rentals.hasMoreElements()) {
// 取得一笔租借记。
Rental each = (Rental) rentals.nextElement();
result.append("\t").append(each.getMovie().getTitle()).append("\t").append(String.valueOf( each.getCharge())).append("\n");
}
// add footer lines(结尾打印)
result.append("Amount owed is ").append(String.valueOf(getTotalCharge())).append("\n");
result.append("You earned ").append(String.valueOf(getFrequentRenterPoints())).append(" frequent renter points");
return result.toString();
}
}
Movie.java
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 String getTitle() {
return title;
}
public void setPriceCode(int priceCode) {
this.priceCode = priceCode;
}
public double getCharge(int daysRented){
double result = 0;
// 取得影片出租价格
switch (getPriceCode()) {
case REGULAR:
result += 2;
if (daysRented > 2) {
result += (daysRented - 2) * 1.5;
}
break;
case NEW_RELEASE:
result += daysRented * 3;
break;
case CHILDRENS:
result += 1.5;
if (daysRented > 3){
result += (daysRented - 3) * 1.5;
}
break;
default:
}
return result;
}
public int getFrequentRenterPoints(int daysRented){
if (getPriceCode() == NEW_RELEASE && daysRented > 1){
return 2;
}
return 1;
}
}
Rental.java
public 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;
}
public double getCharge( ){
return movie.getCharge(this.daysRented);
}
public int addFrequentRenterPoints(){
return movie.getFrequentRenterPoints(this.daysRented);
}
}
采用State模式重构的结果:
AbstractPrice .java
public abstract class AbstractPrice {
public abstract int getPriceCode();
public abstract double getCharge(int daysRented);
public int getFrequentRenterPoints(int daysRented){
return 1;
}
}
Customer .java
import java.util.Enumeration;
import java.util.Vector;
public class Customer {
/**
* 姓名
*/
private String name;
/**
* 租借记
*/
private Vector rentals = new Vector();
public Customer(String name) {
this.name = name;
};
public void addRental(Rental arg) {
rentals.addElement(arg);
}
public String getName() {
return name;
}
public static void main(String[] args) {
Customer customer = new Customer("John");
String title = "Titanic";
int priceCode = 2;
int daysRented = 7;
Movie movie = new Movie(title, priceCode);
Rental rental = new Rental(movie, daysRented);
customer.addRental(rental);
String result = customer.statement();
System.out.println(result);
}
public double getTotalCharge(){
double result = 0;
Enumeration rentals = this.rentals.elements();
while (rentals.hasMoreElements()) {
Rental each = (Rental) rentals.nextElement();
result += each.getCharge();
}
return result;
}
public int getFrequentRenterPoints(){
int result = 0;
Enumeration rentals = this.rentals.elements();
while (rentals.hasMoreElements()) {
Rental each = (Rental) rentals.nextElement();
result += each.addFrequentRenterPoints();
}
return result;
}
public String statement() {
// 常客积点
Enumeration rentals = this.rentals.elements();
StringBuilder result = new StringBuilder("Rental Record for " + getName() + "\n");
while (rentals.hasMoreElements()) {
// 取得一笔租借记。
Rental each = (Rental) rentals.nextElement();
result.append("\t").append(each.getMovie().getTitle()).append("\t").append(String.valueOf( each.getCharge())).append("\n");
}
// add footer lines(结尾打印)
result.append("Amount owed is ").append(String.valueOf(getTotalCharge())).append("\n");
result.append("You earned ").append(String.valueOf(getFrequentRenterPoints())).append(" frequent renter points");
return result.toString();
}
}
Movie.java
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 AbstractPrice price;
public Movie(String title, int priceCode) {
this.title = title;
setPriceCode(priceCode);
}
public int getPriceCode() {
return price.getPriceCode();
}
public String getTitle() {
return title;
}
public void setPriceCode(int priceCode) {
switch (priceCode) {
case REGULAR:
price = new RegularPrice();
break;
case NEW_RELEASE:
price = new NewReleasePrice();
break;
case CHILDRENS:
price = new ChildrensPrice();
break;
default: throw new IllegalArgumentException("Incorrect priceCode");
}
}
public double getCharge(int daysRented){
return price.getCharge(daysRented);
}
public int getFrequentRenterPoints(int daysRented){
return price.getFrequentRenterPoints(daysRented);
}
}
Rental.java
public 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;
}
public double getCharge( ){
return movie.getCharge(this.daysRented);
}
public int addFrequentRenterPoints(){
return movie.getFrequentRenterPoints(this.daysRented);
}
}
ChildrensPrice.java
public class ChildrensPrice extends AbstractPrice {
@Override
public int getPriceCode() {
return Movie.CHILDRENS;
}
@Override
public double getCharge(int daysRented) {
double result = 1.5;
if (daysRented > 3){
result += (daysRented - 3) * 1.5;
}
return result;
}
}
NewReleasePrice.java
public class NewReleasePrice extends AbstractPrice {
@Override
public int getPriceCode() {
return Movie.NEW_RELEASE;
}
@Override
public double getCharge(int daysRented) {
return (double) (daysRented * 3);
}
@Override
public int getFrequentRenterPoints(int daysRented){
return daysRented > 1 ? 2:1;
}
}
RegularPrice.java
public class RegularPrice extends AbstractPrice{
@Override
public int getPriceCode() {
return Movie.REGULAR;
}
@Override
public double getCharge(int daysRented) {
double result = 2;
if (daysRented > 2) {
result += (daysRented - 2) * 1.5;
}
return result;
}
}
to be continued |