Form Template Method

  《重构》中此方法叫做塑造模板函数,在设计模式中,对应的模式就是模板模式。重构中的很多变动比较大的方法都会导致重构,但重构中有非常多的小重构手法。就好像建筑一个房子,设计模式教你厨房客厅怎么搭配以设计出一个什么样的风格,而重构中给出了更多的建议,细小的细节,哪些地方应该怎么处理,会导致程序易读、易维护、易扩展。塑造模板函数是一个非常重要的重构手法,熟练运用此手法能直接将过程化的代码“变得”那么面向对象。

  重构前的主体代码:

  1 package io.nelson;
  2 
  3 import java.util.Enumeration;
  4 import java.util.Vector;
  5 
  6 public class Customer {
  7     
  8     private String _name;
  9     private Vector<Rental> _rentals = new Vector<Rental>();
 10     
 11     public Customer(String name){
 12         _name = name;
 13     }
 14     
 15     public void addRental(Rental arg)
 16     {
 17         _rentals.addElement(arg);
 18     }
 19     
 20     public String getName(){
 21         return _name;
 22     }
 23     
 24     public String TextStatement(){
 25         double totalAmount = 0;
 26         int frequentRenterPoints = 0;
 27         Enumeration<Rental> rentals = _rentals.elements();
 28         String result = "Rental Record for "+getName()+"\n";
 29         
 30         while(rentals.hasMoreElements())
 31         {
 32             double thisAmount = 0;
 33             Rental each = (Rental) rentals.nextElement();
 34             
 35             switch(each.getMoive().getPriceCode())
 36             {
 37             case Movie.REGULAR:
 38                 thisAmount += 2;
 39                 if(each.getDaysRented() > 2)
 40                 {
 41                     thisAmount += (each.getDaysRented()-2)*1.5;
 42                 }
 43                 break;
 44             case Movie.NEW_RELEASE:
 45                 thisAmount += each.getDaysRented()*3;
 46                 break;
 47             case Movie.CHILDRENS:
 48                 thisAmount += 1.5;
 49                 if(each.getDaysRented() > 3)
 50                 {
 51                     thisAmount += (each.getDaysRented()-3)*1.5;
 52                 }
 53                 break;
 54             }
 55             
 56             frequentRenterPoints++;
 57             if((each.getMoive().getPriceCode() == Movie.NEW_RELEASE)&& 
 58                     each.getDaysRented()>1) frequentRenterPoints++;
 59             
 60             result += "\t" + each.getMoive().getTitle()+ "\t"+String.valueOf(thisAmount)+"\n";
 61             totalAmount += thisAmount;
 62         }
 63         
 64         result += "Amount owed is "+String.valueOf(totalAmount)+"\n";
 65         result += "You earned "+String.valueOf(frequentRenterPoints)+" frequent renter points";
 66         
 67         return result;
 68     }
 69 }
 70 
 71 //租赁类
 72 class Rental {
 73     
 74     private Movie _movie;
 75     private int _daysRented;
 76     
 77     public Rental(Movie movie,int daysRented)
 78     {
 79         _movie = movie;
 80         _daysRented = daysRented;
 81     }
 82     
 83     public int getDaysRented()
 84     {
 85         return _daysRented;
 86     }
 87     
 88     public Movie getMoive()
 89     {
 90         return _movie;
 91     }
 92 }
 93 
 94 class Movie
 95 {
 96     public static final int CHILDRENS = 2;
 97     public static final int REGULAR = 0;
 98     public static final int NEW_RELEASE = 1;
 99     
100     private String _title;
101     private int _priceCode;
102     
103     public Movie(String title,int priceCode)
104     {
105         _title = title;
106         _priceCode = priceCode;
107     }
108     
109     public int getPriceCode(){
110         return _priceCode;
111     }
112     
113     public String getTitle()
114     {
115         return _title;
116     }
117 }
View Code

  重构前的应用代码:

 1 package io.nelson;
 2 
 3 public class main {
 4 
 5     public static void main(String[] args) {
 6         
 7         System.out.println("Hello Moto!");
 8 
 9         //4部电影
10         Movie txwz = new Movie("天下无贼",Movie.REGULAR);
11         Movie gf = new Movie("功夫",Movie.NEW_RELEASE);
12         Movie gfxm = new Movie("功夫熊猫",Movie.CHILDRENS);
13         Movie bxqy = new Movie("冰雪奇缘",Movie.CHILDRENS);
14         
15         //顾客小明
16         Customer xiaoming = new Customer("xiaoming");
17         
18         //4个租赁
19         Rental r1 = new Rental(txwz,4);  //天下无贼租赁4天
20         Rental r2 = new Rental(gf,3);    //功夫租赁3天
21         Rental r3 = new Rental(gfxm,7);  //功夫熊猫租赁7天
22         Rental r4 = new Rental(bxqy,7);  //冰雪奇缘租赁7天
23         
24         xiaoming.addRental(r1);
25         xiaoming.addRental(r2);
26         xiaoming.addRental(r3);
27         xiaoming.addRental(r4);
28         
29         System.out.println(xiaoming.TextStatement());
30     }
31 }
View Code

  Form Template Method这个重构方法其实非常简单,但要给出一个很好的例子能让人充分体会这个重构方法的优美得好好准备一下。原本想直接从《重构》中Form Template Method这一章节里把例子拿过来,但原书写到这了之后,跟第一章关联太大,要么从第一张重头来,那么这个例子就很难看懂了。想了想还是从第一章的例子从头开始吧,虽然费点时间。把重构前的代码抄了一遍,重构前的应用代码是我添加的。第一次读《重构》时读完第一章感觉就是“窝草,真牛逼”,现在这书也看了几遍,抄这个代码的时候就已经知道所有的重构技巧了,瞬间觉得这个例子简单了,要知道这个例子在书中可是用了50页的篇幅来写的。

  先重构第一版,达到使用Form Template Method方法前的状态,今天要说的是Form Template Method,不能跑偏了。

  本想一次拿出来最终重构代码,算了,这样出错的概率比较大,还是一步一步来。所以下面贴出重构过程,为了验证,或者说减少测试,先贴出上面的代码跑出来的正确结果,以此结果验证我后面每一步重构的正确性。

运行结果图

  第一步重构后的主体代码:

  1 package io.nelson;
  2 
  3 import java.util.Enumeration;
  4 import java.util.Vector;
  5 
  6 public class Customer {
  7     
  8     private String _name;
  9     private Vector<Rental> _rentals = new Vector<Rental>();
 10     
 11     public Customer(String name){
 12         _name = name;
 13     }
 14     
 15     public void addRental(Rental arg)
 16     {
 17         _rentals.addElement(arg);
 18     }
 19     
 20     public String getName(){
 21         return _name;
 22     }
 23     
 24     public String TextStatement(){
 25         double totalAmount = 0;
 26         int frequentRenterPoints = 0;
 27         Enumeration<Rental> rentals = _rentals.elements();
 28         String result = "Rental Record for "+getName()+"\n";
 29         
 30         while(rentals.hasMoreElements())
 31         {
 32             frequentRenterPoints++;
 33             Rental each = (Rental) rentals.nextElement();
 34             
 35             if((each.getMoive().getPriceCode() == Movie.NEW_RELEASE)&& 
 36                     each.getDaysRented()>1) frequentRenterPoints++;
 37             
 38             result += "\t" + each.getMoive().getTitle()+ "\t"+String.valueOf(each.getAmount())+"\n";
 39             totalAmount += each.getAmount();
 40         }
 41         
 42         result += "Amount owed is "+String.valueOf(totalAmount)+"\n";
 43         result += "You earned "+String.valueOf(frequentRenterPoints)+" frequent renter points";
 44         
 45         return result;
 46     }
 47 }
 48 
 49 //租赁类
 50 class Rental {
 51     
 52     private Movie _movie;
 53     private int _daysRented;
 54     
 55     public Rental(Movie movie,int daysRented)
 56     {
 57         _movie = movie;
 58         _daysRented = daysRented;
 59     }
 60     
 61     public int getDaysRented()
 62     {
 63         return _daysRented;
 64     }
 65     
 66     public Movie getMoive()
 67     {
 68         return _movie;
 69     }
 70     
 71     public double getAmount()
 72     {
 73         double amount=0;
 74         switch(getMoive().getPriceCode())
 75         {
 76         case Movie.REGULAR:
 77             amount += 2;
 78             if(getDaysRented() > 2)
 79             {
 80                 amount += (getDaysRented()-2)*1.5;
 81             }
 82             break;
 83         case Movie.NEW_RELEASE:
 84             amount += getDaysRented()*3;
 85             break;
 86         case Movie.CHILDRENS:
 87             amount += 1.5;
 88             if(getDaysRented() > 3)
 89             {
 90                 amount += (getDaysRented()-3)*1.5;
 91             }
 92             break;
 93         }
 94         
 95         return amount;
 96     }
 97 }
 98 
 99 class Movie
100 {
101     public static final int CHILDRENS = 2;
102     public static final int REGULAR = 0;
103     public static final int NEW_RELEASE = 1;
104     
105     private String _title;
106     private int _priceCode;
107     
108     public Movie(String title,int priceCode)
109     {
110         _title = title;
111         _priceCode = priceCode;
112     }
113     
114     public int getPriceCode(){
115         return _priceCode;
116     }
117     
118     public String getTitle()
119     {
120         return _title;
121     }
122 }
View Code

  上面将计算每个租赁的金额抽取出来放到了Rental类中,增加函数getAmount。这一步运行一下,结果也是对的。接着进行下一步重构。

  第二步重构后的主体代码:

  1 package io.nelson;
  2 
  3 import java.util.Enumeration;
  4 import java.util.Vector;
  5 
  6 public class Customer {
  7     
  8     private String _name;
  9     private Vector<Rental> _rentals = new Vector<Rental>();
 10     
 11     public Customer(String name){
 12         _name = name;
 13     }
 14     
 15     public void addRental(Rental arg)
 16     {
 17         _rentals.addElement(arg);
 18     }
 19     
 20     public String getName(){
 21         return _name;
 22     }
 23     
 24     public String TextStatement(){
 25         double totalAmount = 0;
 26         int frequentRenterPoints = 0;
 27         Enumeration<Rental> rentals = _rentals.elements();
 28         String result = "Rental Record for "+getName()+"\n";
 29         
 30         while(rentals.hasMoreElements())
 31         {
 32             frequentRenterPoints++;
 33             Rental each = (Rental) rentals.nextElement();
 34             
 35             if((each.getMoive().getPriceCode() == Movie.NEW_RELEASE)&& 
 36                     each.getDaysRented()>1) frequentRenterPoints++;
 37             
 38             result += "\t" + each.getMoive().getTitle()+ "\t"+String.valueOf(each.getAmount())+"\n";
 39             totalAmount += each.getAmount();
 40         }
 41         
 42         result += "Amount owed is "+String.valueOf(totalAmount)+"\n";
 43         result += "You earned "+String.valueOf(frequentRenterPoints)+" frequent renter points";
 44         
 45         return result;
 46     }
 47 }
 48 
 49 //租赁类
 50 class Rental {
 51     
 52     private Movie _movie;
 53     private int _daysRented;
 54     
 55     public Rental(Movie movie,int daysRented)
 56     {
 57         _movie = movie;
 58         _daysRented = daysRented;
 59     }
 60     
 61     public int getDaysRented()
 62     {
 63         return _daysRented;
 64     }
 65     
 66     public Movie getMoive()
 67     {
 68         return _movie;
 69     }
 70     
 71     public double getAmount()
 72     {
 73         return _movie.getAmount(getDaysRented());
 74     }
 75 }
 76 
 77 class Movie
 78 {
 79     public static final int CHILDRENS = 2;
 80     public static final int REGULAR = 0;
 81     public static final int NEW_RELEASE = 1;
 82     
 83     private String _title;
 84     private int _priceCode;
 85     
 86     public Movie(String title,int priceCode)
 87     {
 88         _title = title;
 89         _priceCode = priceCode;
 90     }
 91     
 92     public int getPriceCode(){
 93         return _priceCode;
 94     }
 95     
 96     public String getTitle()
 97     {
 98         return _title;
 99     }
100     
101     public double getAmount(int days)
102     {
103         double amount=0;
104         switch(getPriceCode())
105         {
106         case Movie.REGULAR:
107             amount += 2;
108             if(days > 2)
109             {
110                 amount += (days-2)*1.5;
111             }
112             break;
113         case Movie.NEW_RELEASE:
114             amount += days*3;
115             break;
116         case Movie.CHILDRENS:
117             amount += 1.5;
118             if(days > 3)
119             {
120                 amount += (days-3)*1.5;
121             }
122             break;
123         default:
124                 break;
125         }
126         
127         return amount;
128     }
129 }
View Code

  这里将获取影片租赁金额的计算直接放在了Movie类中,计算过程中需要一个参数--租赁时间,最简单的方法是传Rental类对象作为参数,我这里没有这么做,因为这样增加了类之间的耦合。现在的Movie类没有使用其他类,是一个高度独立的类,可以方便移动到任何其他地方的。这一步运行一下,结果也是对的。接着进行下一步重构。

  1 package io.nelson;
  2 
  3 import java.util.Enumeration;
  4 import java.util.Vector;
  5 
  6 public class Customer {
  7     
  8     private String _name;
  9     private Vector<Rental> _rentals = new Vector<Rental>();
 10     
 11     public Customer(String name){
 12         _name = name;
 13     }
 14     
 15     public void addRental(Rental arg)
 16     {
 17         _rentals.addElement(arg);
 18     }
 19     
 20     public String getName(){
 21         return _name;
 22     }
 23     
 24     public String TextStatement(){
 25         double totalAmount = 0;
 26         int frequentRenterPoints = 0;
 27         Enumeration<Rental> rentals = _rentals.elements();
 28         String result = "Rental Record for "+getName()+"\n";
 29         
 30         while(rentals.hasMoreElements())
 31         {
 32             frequentRenterPoints++;
 33             Rental each = (Rental) rentals.nextElement();
 34             
 35             frequentRenterPoints += each.getFrequentRetenterPoints();
 36             
 37             result += "\t" + each.getMoive().getTitle()+ "\t"+String.valueOf(each.getAmount())+"\n";
 38             totalAmount += each.getAmount();
 39         }
 40         
 41         result += "Amount owed is "+String.valueOf(totalAmount)+"\n";
 42         result += "You earned "+String.valueOf(frequentRenterPoints)+" frequent renter points";
 43         
 44         return result;
 45     }
 46 }
 47 
 48 //租赁类
 49 class Rental {
 50     
 51     private Movie _movie;
 52     private int _daysRented;
 53     
 54     public Rental(Movie movie,int daysRented)
 55     {
 56         _movie = movie;
 57         _daysRented = daysRented;
 58     }
 59     
 60     public int getDaysRented()
 61     {
 62         return _daysRented;
 63     }
 64     
 65     public Movie getMoive()
 66     {
 67         return _movie;
 68     }
 69     
 70     public double getAmount()
 71     {
 72         return _movie.getAmount(getDaysRented());
 73     }
 74     
 75     public int getFrequentRetenterPoints()
 76     {
 77         if((getMoive().getPriceCode() == Movie.NEW_RELEASE)&& 
 78                 getDaysRented()>1) return 1;
 79         else 
 80             return 0;
 81     }
 82 }
 83 
 84 class Movie
 85 {
 86     public static final int CHILDRENS = 2;
 87     public static final int REGULAR = 0;
 88     public static final int NEW_RELEASE = 1;
 89     
 90     private String _title;
 91     private int _priceCode;
 92     
 93     public Movie(String title,int priceCode)
 94     {
 95         _title = title;
 96         _priceCode = priceCode;
 97     }
 98     
 99     public int getPriceCode(){
100         return _priceCode;
101     }
102     
103     public String getTitle()
104     {
105         return _title;
106     }
107     
108     public double getAmount(int days)
109     {
110         double amount=0;
111         switch(getPriceCode())
112         {
113         case Movie.REGULAR:
114             amount += 2;
115             if(days > 2)
116             {
117                 amount += (days-2)*1.5;
118             }
119             break;
120         case Movie.NEW_RELEASE:
121             amount += days*3;
122             break;
123         case Movie.CHILDRENS:
124             amount += 1.5;
125             if(days > 3)
126             {
127                 amount += (days-3)*1.5;
128             }
129             break;
130         default:
131                 break;
132         }
133         
134         return amount;
135     }
136 }
View Code

  处理了下获取积分的代码,积分计算也不应该在Customer类中完成,把它放在了Retental类中。简单修改下后,运行结果也是对的。接着下一步重构。

  1 package io.nelson;
  2 
  3 import java.util.Enumeration;
  4 import java.util.Vector;
  5 
  6 public class Customer {
  7     
  8     private String _name;
  9     private Vector<Rental> _rentals = new Vector<Rental>();
 10     
 11     public Customer(String name){
 12         _name = name;
 13     }
 14     
 15     public void addRental(Rental arg)
 16     {
 17         _rentals.addElement(arg);
 18     }
 19     
 20     public String getName(){
 21         return _name;
 22     }
 23     
 24     public String TextStatement(){
 25         double totalAmount = 0;
 26         int frequentRenterPoints = 0;
 27         Enumeration<Rental> rentals = _rentals.elements();
 28         String result = "Rental Record for "+getName()+"\n";
 29         
 30         while(rentals.hasMoreElements())
 31         {
 32             frequentRenterPoints++;
 33             Rental each = (Rental) rentals.nextElement();
 34             
 35             frequentRenterPoints += each.getFrequentRetenterPoints();
 36             
 37             result += "\t" + each.getMoive().getTitle()+ "\t"+String.valueOf(each.getAmount())+"\n";
 38             totalAmount += each.getAmount();
 39         }
 40         
 41         result += "Amount owed is "+String.valueOf(totalAmount)+"\n";
 42         result += "You earned "+String.valueOf(frequentRenterPoints)+" frequent renter points";
 43         
 44         return result;
 45     }
 46 }
 47 
 48 //租赁类
 49 class Rental {
 50     
 51     private Movie _movie;
 52     private int _daysRented;
 53     
 54     public Rental(Movie movie,int daysRented)
 55     {
 56         _movie = movie;
 57         _daysRented = daysRented;
 58     }
 59     
 60     public int getDaysRented()
 61     {
 62         return _daysRented;
 63     }
 64     
 65     public Movie getMoive()
 66     {
 67         return _movie;
 68     }
 69     
 70     public double getAmount()
 71     {
 72         return _movie.getAmount(getDaysRented());
 73     }
 74     
 75     public int getFrequentRetenterPoints()
 76     {
 77         return _movie.getFrequentRetenterPoints(getDaysRented());
 78     }
 79 }
 80 
 81 class Movie
 82 {
 83     public static final int CHILDRENS = 2;
 84     public static final int REGULAR = 0;
 85     public static final int NEW_RELEASE = 1;
 86     
 87     private String _title;
 88     private int _priceCode;
 89     
 90     public Movie(String title,int priceCode)
 91     {
 92         _title = title;
 93         _priceCode = priceCode;
 94     }
 95     
 96     public int getPriceCode(){
 97         return _priceCode;
 98     }
 99     
100     public String getTitle()
101     {
102         return _title;
103     }
104     
105     public double getAmount(int days)
106     {
107         double amount=0;
108         switch(getPriceCode())
109         {
110         case Movie.REGULAR:
111             amount += 2;
112             if(days > 2)
113             {
114                 amount += (days-2)*1.5;
115             }
116             break;
117         case Movie.NEW_RELEASE:
118             amount += days*3;
119             break;
120         case Movie.CHILDRENS:
121             amount += 1.5;
122             if(days > 3)
123             {
124                 amount += (days-3)*1.5;
125             }
126             break;
127         default:
128                 break;
129         }
130         
131         return amount;
132     }
133     
134     public int getFrequentRetenterPoints(int days)
135     {
136         if((getPriceCode() == Movie.NEW_RELEASE)&& 
137                 days>1) return 1;
138         else 
139             return 0;
140     }
141 }
View Code

  继续将获取积分的函数下移至Movie类,这一点跟上面修改获取影片价格一样,这一步也是做了简单修改,运行一下,结果也是对的,接着进行下一步重构。

  1 package io.nelson;
  2 
  3 import java.util.Enumeration;
  4 import java.util.Vector;
  5 
  6 public class Customer {
  7     
  8     private String _name;
  9     private Vector<Rental> _rentals = new Vector<Rental>();
 10     
 11     public Customer(String name){
 12         _name = name;
 13     }
 14     
 15     public void addRental(Rental arg)
 16     {
 17         _rentals.addElement(arg);
 18     }
 19     
 20     public String getName(){
 21         return _name;
 22     }
 23     
 24     public String TextStatement(){
 25         Enumeration<Rental> rentals = _rentals.elements();
 26         String result = "Rental Record for "+getName()+"\n";
 27         
 28         while(rentals.hasMoreElements())
 29         {
 30             Rental each = (Rental) rentals.nextElement();
 31             result += "\t" + each.getMoive().getTitle()+ "\t"+String.valueOf(each.getAmount())+"\n";
 32         }
 33         
 34         result += "Amount owed is "+String.valueOf(getTotalAmount())+"\n";
 35         result += "You earned "+String.valueOf(getTotalfrequentRenterPoints())+" frequent renter points";
 36         
 37         return result;
 38     }
 39     
 40     private double getTotalAmount()
 41     {
 42         double totalAmount=0;
 43         Enumeration<Rental> rentals = _rentals.elements();
 44         
 45         while(rentals.hasMoreElements())
 46         {
 47             Rental each = (Rental) rentals.nextElement();
 48             
 49             totalAmount += each.getAmount();
 50         }
 51         
 52         return totalAmount;
 53     }
 54     
 55     private int getTotalfrequentRenterPoints()
 56     {
 57         int frequentRenterPoints=0;
 58         
 59         Enumeration<Rental> rentals = _rentals.elements();
 60         String result = "Rental Record for "+getName()+"\n";
 61         
 62         while(rentals.hasMoreElements())
 63         {
 64             frequentRenterPoints++;
 65             Rental each = (Rental) rentals.nextElement();
 66             
 67             frequentRenterPoints += each.getFrequentRetenterPoints();
 68             
 69             result += "\t" + each.getMoive().getTitle()+ "\t"+String.valueOf(each.getAmount())+"\n";
 70         }
 71         
 72         return frequentRenterPoints;
 73     }
 74 }
 75 
 76 //租赁类
 77 class Rental {
 78     
 79     private Movie _movie;
 80     private int _daysRented;
 81     
 82     public Rental(Movie movie,int daysRented)
 83     {
 84         _movie = movie;
 85         _daysRented = daysRented;
 86     }
 87     
 88     public int getDaysRented()
 89     {
 90         return _daysRented;
 91     }
 92     
 93     public Movie getMoive()
 94     {
 95         return _movie;
 96     }
 97     
 98     public double getAmount()
 99     {
100         return _movie.getAmount(getDaysRented());
101     }
102     
103     public int getFrequentRetenterPoints()
104     {
105         return _movie.getFrequentRetenterPoints(getDaysRented());
106     }
107 }
108 
109 class Movie
110 {
111     public static final int CHILDRENS = 2;
112     public static final int REGULAR = 0;
113     public static final int NEW_RELEASE = 1;
114     
115     private String _title;
116     private int _priceCode;
117     
118     public Movie(String title,int priceCode)
119     {
120         _title = title;
121         _priceCode = priceCode;
122     }
123     
124     public int getPriceCode(){
125         return _priceCode;
126     }
127     
128     public String getTitle()
129     {
130         return _title;
131     }
132     
133     public double getAmount(int days)
134     {
135         double amount=0;
136         switch(getPriceCode())
137         {
138         case Movie.REGULAR:
139             amount += 2;
140             if(days > 2)
141             {
142                 amount += (days-2)*1.5;
143             }
144             break;
145         case Movie.NEW_RELEASE:
146             amount += days*3;
147             break;
148         case Movie.CHILDRENS:
149             amount += 1.5;
150             if(days > 3)
151             {
152                 amount += (days-3)*1.5;
153             }
154             break;
155         default:
156                 break;
157         }
158         
159         return amount;
160     }
161     
162     public int getFrequentRetenterPoints(int days)
163     {
164         if((getPriceCode() == Movie.NEW_RELEASE)&& 
165                 days>1) return 1;
166         else 
167             return 0;
168     }
169 }
View Code

  这一步修改的比较多一点,主要还是在TextStatement函数中,那个循环了其实干了三件事,第一计算价格,第二计算积分,第三“打印”各个租赁影片金额。上面这步重构把三个功能分开,这样就清晰多了。运行一下,结果也是对的。

  下面添加HtmlStatement函数,要讲的From Template Method算正式开始了。

  重构前的主体代码:

  1 package io.nelson;
  2 
  3 import java.util.Enumeration;
  4 import java.util.Vector;
  5 
  6 public class Customer {
  7     
  8     private String _name;
  9     private Vector<Rental> _rentals = new Vector<Rental>();
 10     
 11     public Customer(String name){
 12         _name = name;
 13     }
 14     
 15     public void addRental(Rental arg)
 16     {
 17         _rentals.addElement(arg);
 18     }
 19     
 20     public String getName(){
 21         return _name;
 22     }
 23     
 24     public String TextStatement(){
 25         Enumeration<Rental> rentals = _rentals.elements();
 26         String result = "Rental Record for "+getName()+"\n";
 27         
 28         while(rentals.hasMoreElements())
 29         {
 30             Rental each = (Rental) rentals.nextElement();
 31             result += "\t" + each.getMoive().getTitle()+ "\t"+String.valueOf(each.getAmount())+"\n";
 32         }
 33         
 34         result += "Amount owed is "+String.valueOf(getTotalAmount())+"\n";
 35         result += "You earned "+String.valueOf(getTotalfrequentRenterPoints())+" frequent renter points";
 36         
 37         return result;
 38     }
 39     
 40     public String HtmlStatement(){
 41         Enumeration<Rental> rentals = _rentals.elements();
 42         String result = "<H1>Rental Record for <EM>"+getName()+"</EM><H1><P>\n";
 43         
 44         while(rentals.hasMoreElements())
 45         {
 46             Rental each = (Rental) rentals.nextElement();
 47             result += each.getMoive().getTitle()+ ":"+String.valueOf(each.getAmount())+"<BR>\n";
 48         }
 49         
 50         result += "<P>You owe  <EM>"+String.valueOf(getTotalAmount())+"</EM><P>\n";
 51         result += "On this rental you earned <EM>"+String.valueOf(getTotalfrequentRenterPoints())+"</EM> frequent renter points<P>";
 52         
 53         return result;
 54     }
 55     
 56     private double getTotalAmount()
 57     {
 58         double totalAmount=0;
 59         Enumeration<Rental> rentals = _rentals.elements();
 60         
 61         while(rentals.hasMoreElements())
 62         {
 63             Rental each = (Rental) rentals.nextElement();
 64             
 65             totalAmount += each.getAmount();
 66         }
 67         
 68         return totalAmount;
 69     }
 70     
 71     private int getTotalfrequentRenterPoints()
 72     {
 73         int frequentRenterPoints=0;
 74         
 75         Enumeration<Rental> rentals = _rentals.elements();
 76         String result = "Rental Record for "+getName()+"\n";
 77         
 78         while(rentals.hasMoreElements())
 79         {
 80             frequentRenterPoints++;
 81             Rental each = (Rental) rentals.nextElement();
 82             
 83             frequentRenterPoints += each.getFrequentRetenterPoints();
 84             
 85             result += "\t" + each.getMoive().getTitle()+ "\t"+String.valueOf(each.getAmount())+"\n";
 86         }
 87         
 88         return frequentRenterPoints;
 89     }
 90 }
 91 
 92 //租赁类
 93 class Rental {
 94     
 95     private Movie _movie;
 96     private int _daysRented;
 97     
 98     public Rental(Movie movie,int daysRented)
 99     {
100         _movie = movie;
101         _daysRented = daysRented;
102     }
103     
104     public int getDaysRented()
105     {
106         return _daysRented;
107     }
108     
109     public Movie getMoive()
110     {
111         return _movie;
112     }
113     
114     public double getAmount()
115     {
116         return _movie.getAmount(getDaysRented());
117     }
118     
119     public int getFrequentRetenterPoints()
120     {
121         return _movie.getFrequentRetenterPoints(getDaysRented());
122     }
123 }
124 
125 class Movie
126 {
127     public static final int CHILDRENS = 2;
128     public static final int REGULAR = 0;
129     public static final int NEW_RELEASE = 1;
130     
131     private String _title;
132     private int _priceCode;
133     
134     public Movie(String title,int priceCode)
135     {
136         _title = title;
137         _priceCode = priceCode;
138     }
139     
140     public int getPriceCode(){
141         return _priceCode;
142     }
143     
144     public String getTitle()
145     {
146         return _title;
147     }
148     
149     public double getAmount(int days)
150     {
151         double amount=0;
152         switch(getPriceCode())
153         {
154         case Movie.REGULAR:
155             amount += 2;
156             if(days > 2)
157             {
158                 amount += (days-2)*1.5;
159             }
160             break;
161         case Movie.NEW_RELEASE:
162             amount += days*3;
163             break;
164         case Movie.CHILDRENS:
165             amount += 1.5;
166             if(days > 3)
167             {
168                 amount += (days-3)*1.5;
169             }
170             break;
171         default:
172                 break;
173         }
174         
175         return amount;
176     }
177     
178     public int getFrequentRetenterPoints(int days)
179     {
180         if((getPriceCode() == Movie.NEW_RELEASE)&& 
181                 days>1) return 1;
182         else 
183             return 0;
184     }
185 }
View Code

  重构前的应用代码(比上面多加了两行语句):

 1 package io.nelson;
 2 
 3 public class main {
 4 
 5     public static void main(String[] args) {
 6         
 7         System.out.println("Hello Moto!");
 8 
 9         //4部电影
10         Movie txwz = new Movie("天下无贼",Movie.REGULAR);
11         Movie gf = new Movie("功夫",Movie.NEW_RELEASE);
12         Movie gfxm = new Movie("功夫熊猫",Movie.CHILDRENS);
13         Movie bxqy = new Movie("冰雪奇缘",Movie.CHILDRENS);
14         
15         //顾客小明
16         Customer xiaoming = new Customer("xiaoming");
17         
18         //4个租赁
19         Rental r1 = new Rental(txwz,4);  //天下无贼租赁4天
20         Rental r2 = new Rental(gf,3);    //功夫租赁3天
21         Rental r3 = new Rental(gfxm,7);  //功夫熊猫租赁7天
22         Rental r4 = new Rental(bxqy,7);  //冰雪奇缘租赁7天
23         
24         xiaoming.addRental(r1);
25         xiaoming.addRental(r2);
26         xiaoming.addRental(r3);
27         xiaoming.addRental(r4);
28         
29         System.out.println(xiaoming.TextStatement());
30         System.out.println();
31         System.out.println(xiaoming.HtmlStatement());
32     }
33 }
View Code

  重构后的主体代码:

  1 package io.nelson;
  2 
  3 import java.util.Enumeration;
  4 import java.util.Vector;
  5 
  6 public class Customer {
  7     
  8     private String _name;
  9     private Vector<Rental> _rentals = new Vector<Rental>();
 10     HtmlStatement htmlStatement;
 11     TextStatement textStatement;
 12     
 13     public Customer(String name){
 14         _name = name;
 15         
 16         htmlStatement = new HtmlStatement();
 17         textStatement = new TextStatement();
 18     }
 19     
 20     public void addRental(Rental arg)
 21     {
 22         _rentals.addElement(arg);
 23     }
 24     
 25     public Vector<Rental> getRetentals()
 26     {
 27         return _rentals;
 28     }
 29     
 30     public String getName(){
 31         return _name;
 32     }
 33     
 34     public String HtmlStatement()
 35     {
 36         return htmlStatement.statement(this);
 37     }
 38     
 39     public String TextStatement()
 40     {
 41         return textStatement.statement(this);
 42     }
 43     
 44     public double getTotalAmount()
 45     {
 46         double totalAmount=0;
 47         Enumeration<Rental> rentals = _rentals.elements();
 48         
 49         while(rentals.hasMoreElements())
 50         {
 51             Rental each = (Rental) rentals.nextElement();
 52             
 53             totalAmount += each.getAmount();
 54         }
 55         
 56         return totalAmount;
 57     }
 58     
 59     public int getTotalfrequentRenterPoints()
 60     {
 61         int frequentRenterPoints=0;
 62         
 63         Enumeration<Rental> rentals = _rentals.elements();
 64         
 65         while(rentals.hasMoreElements())
 66         {
 67             frequentRenterPoints++;
 68             Rental each = (Rental) rentals.nextElement();
 69             
 70             frequentRenterPoints += each.getFrequentRetenterPoints();
 71         }
 72         
 73         return frequentRenterPoints;
 74     }
 75 }
 76 
 77 abstract class Statement
 78 {
 79     public String statement(Customer aCus){
 80         Enumeration<Rental> rentals = aCus.getRetentals().elements();
 81         String result = headerString(aCus);
 82         
 83         while(rentals.hasMoreElements())
 84         {
 85             Rental each = (Rental) rentals.nextElement();
 86             result += eachString(each);
 87         }
 88         
 89         result += footerString(aCus);
 90         
 91         return result;
 92     }
 93     
 94     abstract String headerString(Customer aCus);
 95     abstract String footerString(Customer aCus);
 96     abstract String eachString(Rental arent);
 97 }
 98 
 99 class HtmlStatement extends Statement
100 {
101     public String footerString(Customer aCus)
102     {
103         String result = "";
104         result += "<P>You owe  <EM>"+String.valueOf(aCus.getTotalAmount())+"</EM><P>\n";
105         result += "On this rental you earned <EM>"+String.valueOf(aCus.getTotalfrequentRenterPoints())+"</EM> frequent renter points<P>";
106         
107         return result;
108     }
109     
110     public String headerString(Customer aCus)
111     {
112         return ("<H1>Rental Record for <EM>"+aCus.getName()+"</EM><H1><P>\n");
113     }
114     
115     public String eachString(Rental arent)
116     {
117         return (arent.getMoive().getTitle()+ ":"+String.valueOf(arent.getAmount())+"<BR>\n");
118     }
119 }
120 
121 class TextStatement extends Statement
122 {
123     public String footerString(Customer aCus)
124     {
125         String result="";
126         result += "Amount owed is "+String.valueOf(aCus.getTotalAmount())+"\n";
127         result += "You earned "+String.valueOf(aCus.getTotalfrequentRenterPoints())+" frequent renter points";
128         return result;
129     }
130     
131     public String headerString(Customer aCus)
132     {
133         return ("Rental Record for "+aCus.getName()+"\n");
134     }
135     
136     public String eachString(Rental arent)
137     {
138         return ("\t"+arent.getMoive().getTitle()+ "\t"+String.valueOf(arent.getAmount())+"\n");
139     }
140 }
141 
142 //租赁类
143 class Rental {
144     
145     private Movie _movie;
146     private int _daysRented;
147     
148     public Rental(Movie movie,int daysRented)
149     {
150         _movie = movie;
151         _daysRented = daysRented;
152     }
153     
154     public int getDaysRented()
155     {
156         return _daysRented;
157     }
158     
159     public Movie getMoive()
160     {
161         return _movie;
162     }
163     
164     public double getAmount()
165     {
166         return _movie.getAmount(getDaysRented());
167     }
168     
169     public int getFrequentRetenterPoints()
170     {
171         return _movie.getFrequentRetenterPoints(getDaysRented());
172     }
173 }
174 
175 class Movie
176 {
177     public static final int CHILDRENS = 2;
178     public static final int REGULAR = 0;
179     public static final int NEW_RELEASE = 1;
180     
181     private String _title;
182     private int _priceCode;
183     
184     public Movie(String title,int priceCode)
185     {
186         _title = title;
187         _priceCode = priceCode;
188     }
189     
190     public int getPriceCode(){
191         return _priceCode;
192     }
193     
194     public String getTitle()
195     {
196         return _title;
197     }
198     
199     public double getAmount(int days)
200     {
201         double amount=0;
202         switch(getPriceCode())
203         {
204         case Movie.REGULAR:
205             amount += 2;
206             if(days > 2)
207             {
208                 amount += (days-2)*1.5;
209             }
210             break;
211         case Movie.NEW_RELEASE:
212             amount += days*3;
213             break;
214         case Movie.CHILDRENS:
215             amount += 1.5;
216             if(days > 3)
217             {
218                 amount += (days-3)*1.5;
219             }
220             break;
221         default:
222                 break;
223         }
224         
225         return amount;
226     }
227     
228     public int getFrequentRetenterPoints(int days)
229     {
230         if((getPriceCode() == Movie.NEW_RELEASE)&& 
231                 days>1) return 1;
232         else 
233             return 0;
234     }
235 }
View Code

  重构后的应用代码:

 1 package io.nelson;
 2 
 3 public class main {
 4 
 5     public static void main(String[] args) {
 6         
 7         System.out.println("Hello Moto!");
 8 
 9         //4部电影
10         Movie txwz = new Movie("天下无贼",Movie.REGULAR);
11         Movie gf = new Movie("功夫",Movie.NEW_RELEASE);
12         Movie gfxm = new Movie("功夫熊猫",Movie.CHILDRENS);
13         Movie bxqy = new Movie("冰雪奇缘",Movie.CHILDRENS);
14         
15         //顾客小明
16         Customer xiaoming = new Customer("xiaoming");
17         
18         //4个租赁
19         Rental r1 = new Rental(txwz,4);  //天下无贼租赁4天
20         Rental r2 = new Rental(gf,3);    //功夫租赁3天
21         Rental r3 = new Rental(gfxm,7);  //功夫熊猫租赁7天
22         Rental r4 = new Rental(bxqy,7);  //冰雪奇缘租赁7天
23         
24         xiaoming.addRental(r1);
25         xiaoming.addRental(r2);
26         xiaoming.addRental(r3);
27         xiaoming.addRental(r4);
28         
29         System.out.println(xiaoming.TextStatement());
30         System.out.println();
31         System.out.println(xiaoming.HtmlStatement());
32     }
33 }
View Code

  (代码贴到这里的是否发现上面计算积分函数里多了一个字符串语句,是拷贝的时候没有删除,上面已经贴了的就不改了,看最后这里就可以了。)

  这一部分的代码也算完了,Form Template Method主要针对的是Statement类中的statement函数,在这个函数中,有一个过程化的函数,函数中有几个步骤。Form Template Method就是说,将函数中变化的部分抽出来,供不同子类去重写,函数中固定的部分保持不便。

  “常见的一种情况是:两个函数以相同顺序执行大致相近的操作,但是各操作不完全相同。这种情况下我们可以将执行操作的序列移至超类,并借助多态保证各操作得以保持差异性,这样的函数被称为Template Method(模板函数)”。

  我的师傅给我讲过一句话,“在面向对象的语言里,一切对象化。”从这个重构手法里,可以看到把函数类化,处理过程化代码重用、差异化更有效。

posted @ 2017-10-19 22:36  kanite  阅读(394)  评论(0编辑  收藏  举报