重构-改善既有代码的设计 读书笔记一
影片出租店的程序:计算每位顾客的消费金额并打印详单。
操作者告诉程序:顾客租了哪些影片,租期多长。程序根据租赁时间和影片类型计算费用。
影片分为3类:普通片,儿童片,新片。除了计算费用还要为常客计算积分,积分根据组片种类是否为新片而不同。
Movie(movieKind, name)
Rent(影片名称,租赁时间)
RentTotal(Rent列表)
阶段1:从statement()到htmlStatement()
使用的重构方法:
1.extract method(抽取逻辑,可能在多处使用的相同逻辑,只需要保留一份逻辑代码,而在使用时对该逻辑进行调用。
而不是写重复的逻辑代码。)
确保逻辑代码唯一,而不是逻辑代码重复。
即确保逻辑实体唯一,而不是逻辑实体重复。
确保只有一个该逻辑的实体,而不是多个该逻辑的实体。因为如果某个逻辑产生多个实体,必须维护它们的一致性。
唯一的逻辑实体 + 逻辑实体的多次调用。
原因:逻辑代码重复,当需要修改该逻辑代码时,必须确保该逻辑代码的所有实体都是一致性。
这个一致性维护工作难度根据逻辑实体分散程度增加,逻辑实体的个数增加,出错的概率也不断提升。
2.move method(函数搬家,函数应该放在它使用的数据的对象内)
调整修改代码,让其使用新函数,旧函数如果是public则可以保留,如果其他类还引用了旧函数,不需要修改它们。
3.replace temp with query。(使用函数而不是临时变量)
阶段2:结合变化量,影片的类型,限制变化的影响范围。本例中将影片类型的变化影响限制到Movie类中。
如果getCharge()和getRentPoints()方法在Movie类中,我们只需要在Movie类中修改类型,getCharge() getRentPoints()方法。
如果getCharge()和getRentPoints()方法在Rental类中,当影片类型发生变化,我们需要在Movie类中需改类型,并在Rental类中修改这2个方法。
尽管修改的东西都是一样的,但是后者在2个类中修改,前者在1个类中修改。显然前者更好。
所以把Rental中的getCharge()方法和getRentPoints()方法搬到Movie类中
方式:将Rental中的getCharge()方法和getRentPoints()方法搬到Movie类中
阶段3:使用多态替换类型代码
1.replace type code with state/strategy
1.self encapsulate field
2.move method
3.replace conditional with polymorphism
重构的节奏:测试小修改,测试小修改。。。
重构(名词):对软件内部结构的一种调整,目的是在不改变软件可观察行为的前提下,提高其可理解性,降低其修改成本。
重构(动词):使用一系列重构手法,在不改变软件可观察行为的前提下,调整其结构。
重构是对软件的小修改,但重构之中还可以包含另一个重构。
重构的目的:在不改变软件可观察行为的前提下,调整内部结构,使其更容易被理解和修改。
性能优化:在不改变软件可观察行为的前提下,调整内部结构,提高性能,但往往代码会变得更难理解。
使用重构技术开发软件:
时间分配给2种截然不同的行为:添加新功能,以及重构。
添加新功能时不应修改代码,只管添加功能。
重构时不应添加新功能,只管调整结构。
经常变换帽子,但无论何时都应该清楚自己戴的是哪顶帽子。
重构把我带到更高的理解层次。
重构使程序拥有良好的设计,而良好的设计使快速开发的根本。
如果没有良好的设计,某段时间你可能进展迅速,但恶劣的设计很快就让你的速度慢下来。你会花时间在调试上面,修改的时间
越来越长,因为你必须花更多的时间取理解系统、寻找重复的代码,随着你给程序打上一个有一个补丁,新特性需要更多的代码来实现。
真是个恶性循环。
何时重构?
重构本来就不是一件应该特别拨出时间做的事情,随时随地都可以。
如果你想做某件事情,而重构可以帮你把它做得更好,就用重构。
如果你想理解代码,而重构可以帮助你理解,那就重构。
如果你想添加新功能,重构可以帮助你理解代码,并更容易的添加新功能。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package shop; 2 3 public class Movie { 4 public static final int NEW_RELEASE = 0; 5 public static final int REGULAR = 1; 6 public static final int CHILDREN = 2; 7 8 // private int priceCode; 9 private String title; 10 private Price price; 11 12 public Movie() { 13 } 14 15 public Movie(String title, int priceCode) { 16 setPriceCode(priceCode); 17 this.title = title; 18 } 19 20 public int getPriceCode() { 21 return price.getPriceCode(); 22 } 23 24 public void setPriceCode(int priceCode) { 25 switch (priceCode){ 26 case Movie.NEW_RELEASE: 27 price = new NewPrice(); 28 break; 29 case Movie.REGULAR: 30 price = new RegularPrice(); 31 break; 32 case Movie.CHILDREN: 33 price = new ChildrenPrice(); 34 break; 35 default: 36 throw new IllegalArgumentException("非法的影片类型:" + priceCode); 37 } 38 } 39 40 public String getTitle() { 41 return title; 42 } 43 44 public void setTitle(String title) { 45 this.title = title; 46 } 47 48 public double getCharge(int daysRent){ 49 return price.getCharge(daysRent); 50 // double result = 0; 51 // switch(getPriceCode()){ 52 // case Movie.REGULAR: 53 // result += 2; 54 // if(daysRent > 2){ 55 // result += (daysRent - 2) * 1.5; 56 // } 57 // break; 58 // case Movie.NEW_RELEASE: 59 // result += daysRent * 3; 60 // break; 61 // case Movie.CHILDREN: 62 // result += 1.5; 63 // if(daysRent > 3){ 64 // result += (daysRent - 3) * 1.5; 65 // } 66 // break; 67 // } 68 // return result; 69 } 70 71 public int getRentPoints(int daysRent){ 72 return price.getRentPoints(daysRent); 73 // int rentPoints = 1; 74 // if(getPriceCode() == Movie.NEW_RELEASE 75 // && daysRent > 1){ 76 // rentPoints++; 77 // } 78 // return rentPoints; 79 } 80 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package shop; 2 3 public class Rental { 4 private Movie movie; 5 private int daysRent; 6 7 public Rental(){} 8 9 public Rental(Movie movie, int daysRent){ 10 this.movie = movie; 11 this.daysRent = daysRent; 12 } 13 14 public Movie getMovie() { 15 return movie; 16 } 17 18 public void setMovie(Movie movie) { 19 this.movie = movie; 20 } 21 22 public int getDaysRent() { 23 return daysRent; 24 } 25 26 public void setDaysRent(int daysRent) { 27 this.daysRent = daysRent; 28 } 29 30 public int getRentPoints(){ 31 return getMovie().getRentPoints(getDaysRent()); 32 // int rentPoints = 1; 33 // if(getMovie().getPriceCode() == Movie.NEW_RELEASE 34 // && getDaysRent() > 1){ 35 // rentPoints++; 36 // } 37 // return rentPoints; 38 } 39 40 public double getCharge(){ 41 return getMovie().getCharge(getDaysRent()); 42 // double result = 0; 43 // switch(this.getMovie().getPriceCode()){ 44 // case Movie.REGULAR: 45 // result += 2; 46 // if(this.getDaysRent() > 2){ 47 // result += (this.getDaysRent() - 2) * 1.5; 48 // } 49 // break; 50 // case Movie.NEW_RELEASE: 51 // result += this.getDaysRent() * 3; 52 // break; 53 // case Movie.CHILDREN: 54 // result += 1.5; 55 // if(this.getDaysRent() > 3){ 56 // result += (this.getDaysRent() - 3) * 1.5; 57 // } 58 // break; 59 // } 60 // return result; 61 } 62 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package shop; 2 3 import java.util.ArrayList; 4 5 public class CustomerOrder01 { 6 private String name; 7 private ArrayList<Rental> rentals = new ArrayList<>(); 8 9 public CustomerOrder01(String name){ 10 this.name = name; 11 } 12 13 public void addRental(Rental rental){ 14 rentals.add(rental); 15 } 16 17 public String statement(){ 18 String result ="Rental Record for " + getName() + "\n"; 19 for (Rental rental : rentals) { 20 // double thisAmount = 0; 21 // switch(rental.getMovie().getPriceCode()){ 22 // case Movie.REGULAR: 23 // thisAmount += 2; 24 // if(rental.getDaysRent() > 2){ 25 // thisAmount += (rental.getDaysRent() - 2) * 1.5; 26 // } 27 // break; 28 // case Movie.NEW_RELEASE: 29 // thisAmount += rental.getDaysRent() * 3; 30 // case Movie.CHILDREN: 31 // thisAmount += 1.5; 32 // if(rental.getDaysRent() > 3){ 33 // thisAmount += (rental.getDaysRent() - 3) * 1.5; 34 // } 35 // break; 36 // } 37 // thisAmount = amountFor(rental); 38 // thisAmount = rental.getCharge(); 39 // rentPoints++; 40 // if(rental.getMovie().getPriceCode() == Movie.NEW_RELEASE 41 // && rental.getDaysRent() > 1){ 42 // rentPoints++; 43 // } 44 result += "\t" + rental.getMovie().getTitle() + "\t" + String.valueOf(rental.getCharge()) + "\n"; 45 } 46 result += "Amount owed is " + String.valueOf(getTotalAmount()) + "\n"; 47 result += "You earned " + String.valueOf(getTotalRentPoints()) + " frequent renter points"; 48 return result; 49 } 50 51 public double amountFor(Rental rental){ 52 // double result = 0; 53 // switch(rental.getMovie().getPriceCode()){ 54 // case Movie.REGULAR: 55 // result += 2; 56 // if(rental.getDaysRent() > 2){ 57 // result += (rental.getDaysRent() - 2) * 1.5; 58 // } 59 // break; 60 // case Movie.NEW_RELEASE: 61 // result += rental.getDaysRent() * 3; 62 // break; 63 // case Movie.CHILDREN: 64 // result += 1.5; 65 // if(rental.getDaysRent() > 3){ 66 // result += (rental.getDaysRent() - 3) * 1.5; 67 // } 68 // break; 69 // } 70 // return result; 71 return rental.getCharge(); 72 } 73 74 public double getTotalAmount(){ 75 double totalAmount = 0; 76 for (Rental rental : rentals) { 77 totalAmount += rental.getCharge(); 78 } 79 return totalAmount; 80 } 81 82 public int getTotalRentPoints(){ 83 int totalRentPoints = 0; 84 for (Rental rental : rentals) { 85 totalRentPoints += rental.getRentPoints(); 86 } 87 return totalRentPoints; 88 } 89 public String getName(){ 90 return name; 91 } 92 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package shop; 2 3 public class CustomerTest01 { 4 public static void main(String[] args) { 5 Movie movie1 = new Movie("阿凡达", Movie.NEW_RELEASE); 6 Movie movie2 = new Movie("僵尸世界大战", Movie.REGULAR); 7 Movie movie3 = new Movie("熔炉", Movie.CHILDREN); 8 Movie movie4 = new Movie("星际穿越", Movie.REGULAR); 9 10 Rental rental1 = new Rental(movie1,3); 11 Rental rental2 = new Rental(movie2,5); 12 Rental rental3 = new Rental(movie3,5); 13 14 CustomerOrder01 customerOrder = new CustomerOrder01("liubei"); 15 customerOrder.addRental(rental1); 16 customerOrder.addRental(rental2); 17 customerOrder.addRental(rental3); 18 19 System.out.println(customerOrder.statement()); 20 } 21 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package shop; 2 3 public abstract class Price { 4 public abstract int getPriceCode(); 5 6 public abstract double getCharge(int daysRent); 7 8 public int getRentPoints(int daysRent){ 9 return 1; 10 // int rentPoints = 1; 11 // if(getPriceCode() == Movie.NEW_RELEASE 12 // && daysRent > 1){ 13 // rentPoints++; 14 // } 15 // return rentPoints; 16 } 17 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package shop; 2 3 public class NewPrice extends Price{ 4 @Override 5 public int getPriceCode() { 6 return Movie.NEW_RELEASE; 7 } 8 9 @Override 10 public double getCharge(int daysRent) { 11 return daysRent * 3; 12 } 13 14 @Override 15 public int getRentPoints(int daysRent) { 16 return daysRent > 1 ? 2 : 1; 17 } 18 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package shop; 2 3 public class ChildrenPrice extends Price { 4 @Override 5 public int getPriceCode(){ 6 return Movie.CHILDREN; 7 } 8 9 @Override 10 public double getCharge(int daysRent) { 11 double result = 1.5; 12 if(daysRent > 3){ 13 result += (daysRent - 3) * 1.5; 14 } 15 return result; 16 } 17 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package shop; 2 3 public class RegularPrice extends Price { 4 @Override 5 public int getPriceCode() { 6 return Movie.REGULAR; 7 } 8 9 @Override 10 public double getCharge(int daysRent) { 11 double result = 2; 12 if(daysRent > 2){ 13 result += (daysRent - 2) * 1.5; 14 } 15 return result; 16 } 17 }