影片出租店的程序:计算每位顾客的消费金额并打印详单。
操作者告诉程序:顾客租了哪些影片,租期多长。程序根据租赁时间和影片类型计算费用。
影片分为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种截然不同的行为:添加新功能,以及重构。
添加新功能时不应修改代码,只管添加功能。
重构时不应添加新功能,只管调整结构。
经常变换帽子,但无论何时都应该清楚自己戴的是哪顶帽子。
重构把我带到更高的理解层次。
重构使程序拥有良好的设计,而良好的设计使快速开发的根本。
如果没有良好的设计,某段时间你可能进展迅速,但恶劣的设计很快就让你的速度慢下来。你会花时间在调试上面,修改的时间
越来越长,因为你必须花更多的时间取理解系统、寻找重复的代码,随着你给程序打上一个有一个补丁,新特性需要更多的代码来实现。
真是个恶性循环。
何时重构?
重构本来就不是一件应该特别拨出时间做的事情,随时随地都可以。
如果你想做某件事情,而重构可以帮你把它做得更好,就用重构。
如果你想理解代码,而重构可以帮助你理解,那就重构。
如果你想添加新功能,重构可以帮助你理解代码,并更容易的添加新功能。
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 }
Movie
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 }
Rental
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() + "
";
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 += " " + rental.getMovie().getTitle() + " " + String.valueOf(rental.getCharge()) + "
";
45 }
46 result += "Amount owed is " + String.valueOf(getTotalAmount()) + "
";
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 }
CustomerOrder
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 }
CustomerTest
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 }
Price
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 }
NewPrice
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 }
ChildrenPrice
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 }
RegularPrice