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

  重构前的主体代码:

 package io.nelson;

 import java.util.Enumeration;
import java.util.Vector; public class Customer { private String _name;
private Vector<Rental> _rentals = new Vector<Rental>(); public Customer(String name){
_name = name;
} public void addRental(Rental arg)
{
_rentals.addElement(arg);
} public String getName(){
return _name;
} public String TextStatement(){
double totalAmount = 0;
int frequentRenterPoints = 0;
Enumeration<Rental> rentals = _rentals.elements();
String result = "Rental Record for "+getName()+"\n"; while(rentals.hasMoreElements())
{
double thisAmount = 0;
Rental each = (Rental) rentals.nextElement(); switch(each.getMoive().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.getMoive().getPriceCode() == Movie.NEW_RELEASE)&&
each.getDaysRented()>1) frequentRenterPoints++; result += "\t" + each.getMoive().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;
}
} //租赁类
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 getMoive()
{
return _movie;
}
} 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;
}
}

  重构前的应用代码:

 package io.nelson;

 public class main {

     public static void main(String[] args) {

         System.out.println("Hello Moto!");

         //4部电影
Movie txwz = new Movie("天下无贼",Movie.REGULAR);
Movie gf = new Movie("功夫",Movie.NEW_RELEASE);
Movie gfxm = new Movie("功夫熊猫",Movie.CHILDRENS);
Movie bxqy = new Movie("冰雪奇缘",Movie.CHILDRENS); //顾客小明
Customer xiaoming = new Customer("xiaoming"); //4个租赁
Rental r1 = new Rental(txwz,4); //天下无贼租赁4天
Rental r2 = new Rental(gf,3); //功夫租赁3天
Rental r3 = new Rental(gfxm,7); //功夫熊猫租赁7天
Rental r4 = new Rental(bxqy,7); //冰雪奇缘租赁7天 xiaoming.addRental(r1);
xiaoming.addRental(r2);
xiaoming.addRental(r3);
xiaoming.addRental(r4); System.out.println(xiaoming.TextStatement());
}
}

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

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

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

运行结果图

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

 package io.nelson;

 import java.util.Enumeration;
import java.util.Vector; public class Customer { private String _name;
private Vector<Rental> _rentals = new Vector<Rental>(); public Customer(String name){
_name = name;
} public void addRental(Rental arg)
{
_rentals.addElement(arg);
} public String getName(){
return _name;
} public String TextStatement(){
double totalAmount = 0;
int frequentRenterPoints = 0;
Enumeration<Rental> rentals = _rentals.elements();
String result = "Rental Record for "+getName()+"\n"; while(rentals.hasMoreElements())
{
frequentRenterPoints++;
Rental each = (Rental) rentals.nextElement(); if((each.getMoive().getPriceCode() == Movie.NEW_RELEASE)&&
each.getDaysRented()>1) frequentRenterPoints++; result += "\t" + each.getMoive().getTitle()+ "\t"+String.valueOf(each.getAmount())+"\n";
totalAmount += each.getAmount();
} result += "Amount owed is "+String.valueOf(totalAmount)+"\n";
result += "You earned "+String.valueOf(frequentRenterPoints)+" frequent renter points"; return result;
}
} //租赁类
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 getMoive()
{
return _movie;
} public double getAmount()
{
double amount=0;
switch(getMoive().getPriceCode())
{
case Movie.REGULAR:
amount += 2;
if(getDaysRented() > 2)
{
amount += (getDaysRented()-2)*1.5;
}
break;
case Movie.NEW_RELEASE:
amount += getDaysRented()*3;
break;
case Movie.CHILDRENS:
amount += 1.5;
if(getDaysRented() > 3)
{
amount += (getDaysRented()-3)*1.5;
}
break;
} return amount;
}
} 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;
}
}

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

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

 package io.nelson;

 import java.util.Enumeration;
import java.util.Vector; public class Customer { private String _name;
private Vector<Rental> _rentals = new Vector<Rental>(); public Customer(String name){
_name = name;
} public void addRental(Rental arg)
{
_rentals.addElement(arg);
} public String getName(){
return _name;
} public String TextStatement(){
double totalAmount = 0;
int frequentRenterPoints = 0;
Enumeration<Rental> rentals = _rentals.elements();
String result = "Rental Record for "+getName()+"\n"; while(rentals.hasMoreElements())
{
frequentRenterPoints++;
Rental each = (Rental) rentals.nextElement(); if((each.getMoive().getPriceCode() == Movie.NEW_RELEASE)&&
each.getDaysRented()>1) frequentRenterPoints++; result += "\t" + each.getMoive().getTitle()+ "\t"+String.valueOf(each.getAmount())+"\n";
totalAmount += each.getAmount();
} result += "Amount owed is "+String.valueOf(totalAmount)+"\n";
result += "You earned "+String.valueOf(frequentRenterPoints)+" frequent renter points"; return result;
}
} //租赁类
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 getMoive()
{
return _movie;
} public double getAmount()
{
return _movie.getAmount(getDaysRented());
}
} 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 double getAmount(int days)
{
double amount=0;
switch(getPriceCode())
{
case Movie.REGULAR:
amount += 2;
if(days > 2)
{
amount += (days-2)*1.5;
}
break;
case Movie.NEW_RELEASE:
amount += days*3;
break;
case Movie.CHILDRENS:
amount += 1.5;
if(days > 3)
{
amount += (days-3)*1.5;
}
break;
default:
break;
} return amount;
}
}

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

 package io.nelson;

 import java.util.Enumeration;
import java.util.Vector; public class Customer { private String _name;
private Vector<Rental> _rentals = new Vector<Rental>(); public Customer(String name){
_name = name;
} public void addRental(Rental arg)
{
_rentals.addElement(arg);
} public String getName(){
return _name;
} public String TextStatement(){
double totalAmount = 0;
int frequentRenterPoints = 0;
Enumeration<Rental> rentals = _rentals.elements();
String result = "Rental Record for "+getName()+"\n"; while(rentals.hasMoreElements())
{
frequentRenterPoints++;
Rental each = (Rental) rentals.nextElement(); frequentRenterPoints += each.getFrequentRetenterPoints(); result += "\t" + each.getMoive().getTitle()+ "\t"+String.valueOf(each.getAmount())+"\n";
totalAmount += each.getAmount();
} result += "Amount owed is "+String.valueOf(totalAmount)+"\n";
result += "You earned "+String.valueOf(frequentRenterPoints)+" frequent renter points"; return result;
}
} //租赁类
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 getMoive()
{
return _movie;
} public double getAmount()
{
return _movie.getAmount(getDaysRented());
} public int getFrequentRetenterPoints()
{
if((getMoive().getPriceCode() == Movie.NEW_RELEASE)&&
getDaysRented()>1) return 1;
else
return 0;
}
} 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 double getAmount(int days)
{
double amount=0;
switch(getPriceCode())
{
case Movie.REGULAR:
amount += 2;
if(days > 2)
{
amount += (days-2)*1.5;
}
break;
case Movie.NEW_RELEASE:
amount += days*3;
break;
case Movie.CHILDRENS:
amount += 1.5;
if(days > 3)
{
amount += (days-3)*1.5;
}
break;
default:
break;
} return amount;
}
}

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

 package io.nelson;

 import java.util.Enumeration;
import java.util.Vector; public class Customer { private String _name;
private Vector<Rental> _rentals = new Vector<Rental>(); public Customer(String name){
_name = name;
} public void addRental(Rental arg)
{
_rentals.addElement(arg);
} public String getName(){
return _name;
} public String TextStatement(){
double totalAmount = 0;
int frequentRenterPoints = 0;
Enumeration<Rental> rentals = _rentals.elements();
String result = "Rental Record for "+getName()+"\n"; while(rentals.hasMoreElements())
{
frequentRenterPoints++;
Rental each = (Rental) rentals.nextElement(); frequentRenterPoints += each.getFrequentRetenterPoints(); result += "\t" + each.getMoive().getTitle()+ "\t"+String.valueOf(each.getAmount())+"\n";
totalAmount += each.getAmount();
} result += "Amount owed is "+String.valueOf(totalAmount)+"\n";
result += "You earned "+String.valueOf(frequentRenterPoints)+" frequent renter points"; return result;
}
} //租赁类
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 getMoive()
{
return _movie;
} public double getAmount()
{
return _movie.getAmount(getDaysRented());
} public int getFrequentRetenterPoints()
{
return _movie.getFrequentRetenterPoints(getDaysRented());
}
} 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 double getAmount(int days)
{
double amount=0;
switch(getPriceCode())
{
case Movie.REGULAR:
amount += 2;
if(days > 2)
{
amount += (days-2)*1.5;
}
break;
case Movie.NEW_RELEASE:
amount += days*3;
break;
case Movie.CHILDRENS:
amount += 1.5;
if(days > 3)
{
amount += (days-3)*1.5;
}
break;
default:
break;
} return amount;
} public int getFrequentRetenterPoints(int days)
{
if((getPriceCode() == Movie.NEW_RELEASE)&&
days>1) return 1;
else
return 0;
}
}

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

 package io.nelson;

 import java.util.Enumeration;
import java.util.Vector; public class Customer { private String _name;
private Vector<Rental> _rentals = new Vector<Rental>(); public Customer(String name){
_name = name;
} public void addRental(Rental arg)
{
_rentals.addElement(arg);
} public String getName(){
return _name;
} public String TextStatement(){
Enumeration<Rental> rentals = _rentals.elements();
String result = "Rental Record for "+getName()+"\n"; while(rentals.hasMoreElements())
{
Rental each = (Rental) rentals.nextElement();
result += "\t" + each.getMoive().getTitle()+ "\t"+String.valueOf(each.getAmount())+"\n";
} result += "Amount owed is "+String.valueOf(getTotalAmount())+"\n";
result += "You earned "+String.valueOf(getTotalfrequentRenterPoints())+" frequent renter points"; return result;
} private double getTotalAmount()
{
double totalAmount=0;
Enumeration<Rental> rentals = _rentals.elements(); while(rentals.hasMoreElements())
{
Rental each = (Rental) rentals.nextElement(); totalAmount += each.getAmount();
} return totalAmount;
} private int getTotalfrequentRenterPoints()
{
int frequentRenterPoints=0; Enumeration<Rental> rentals = _rentals.elements();
String result = "Rental Record for "+getName()+"\n"; while(rentals.hasMoreElements())
{
frequentRenterPoints++;
Rental each = (Rental) rentals.nextElement(); frequentRenterPoints += each.getFrequentRetenterPoints(); result += "\t" + each.getMoive().getTitle()+ "\t"+String.valueOf(each.getAmount())+"\n";
} return frequentRenterPoints;
}
} //租赁类
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 getMoive()
{
return _movie;
} public double getAmount()
{
return _movie.getAmount(getDaysRented());
} public int getFrequentRetenterPoints()
{
return _movie.getFrequentRetenterPoints(getDaysRented());
}
} 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 double getAmount(int days)
{
double amount=0;
switch(getPriceCode())
{
case Movie.REGULAR:
amount += 2;
if(days > 2)
{
amount += (days-2)*1.5;
}
break;
case Movie.NEW_RELEASE:
amount += days*3;
break;
case Movie.CHILDRENS:
amount += 1.5;
if(days > 3)
{
amount += (days-3)*1.5;
}
break;
default:
break;
} return amount;
} public int getFrequentRetenterPoints(int days)
{
if((getPriceCode() == Movie.NEW_RELEASE)&&
days>1) return 1;
else
return 0;
}
}

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

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

  重构前的主体代码:

 package io.nelson;

 import java.util.Enumeration;
import java.util.Vector; public class Customer { private String _name;
private Vector<Rental> _rentals = new Vector<Rental>(); public Customer(String name){
_name = name;
} public void addRental(Rental arg)
{
_rentals.addElement(arg);
} public String getName(){
return _name;
} public String TextStatement(){
Enumeration<Rental> rentals = _rentals.elements();
String result = "Rental Record for "+getName()+"\n"; while(rentals.hasMoreElements())
{
Rental each = (Rental) rentals.nextElement();
result += "\t" + each.getMoive().getTitle()+ "\t"+String.valueOf(each.getAmount())+"\n";
} result += "Amount owed is "+String.valueOf(getTotalAmount())+"\n";
result += "You earned "+String.valueOf(getTotalfrequentRenterPoints())+" frequent renter points"; return result;
} public String HtmlStatement(){
Enumeration<Rental> rentals = _rentals.elements();
String result = "<H1>Rental Record for <EM>"+getName()+"</EM><H1><P>\n"; while(rentals.hasMoreElements())
{
Rental each = (Rental) rentals.nextElement();
result += each.getMoive().getTitle()+ ":"+String.valueOf(each.getAmount())+"<BR>\n";
} result += "<P>You owe <EM>"+String.valueOf(getTotalAmount())+"</EM><P>\n";
result += "On this rental you earned <EM>"+String.valueOf(getTotalfrequentRenterPoints())+"</EM> frequent renter points<P>"; return result;
} private double getTotalAmount()
{
double totalAmount=0;
Enumeration<Rental> rentals = _rentals.elements(); while(rentals.hasMoreElements())
{
Rental each = (Rental) rentals.nextElement(); totalAmount += each.getAmount();
} return totalAmount;
} private int getTotalfrequentRenterPoints()
{
int frequentRenterPoints=0; Enumeration<Rental> rentals = _rentals.elements();
String result = "Rental Record for "+getName()+"\n"; while(rentals.hasMoreElements())
{
frequentRenterPoints++;
Rental each = (Rental) rentals.nextElement(); frequentRenterPoints += each.getFrequentRetenterPoints(); result += "\t" + each.getMoive().getTitle()+ "\t"+String.valueOf(each.getAmount())+"\n";
} return frequentRenterPoints;
}
} //租赁类
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 getMoive()
{
return _movie;
} public double getAmount()
{
return _movie.getAmount(getDaysRented());
} public int getFrequentRetenterPoints()
{
return _movie.getFrequentRetenterPoints(getDaysRented());
}
} 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 double getAmount(int days)
{
double amount=0;
switch(getPriceCode())
{
case Movie.REGULAR:
amount += 2;
if(days > 2)
{
amount += (days-2)*1.5;
}
break;
case Movie.NEW_RELEASE:
amount += days*3;
break;
case Movie.CHILDRENS:
amount += 1.5;
if(days > 3)
{
amount += (days-3)*1.5;
}
break;
default:
break;
} return amount;
} public int getFrequentRetenterPoints(int days)
{
if((getPriceCode() == Movie.NEW_RELEASE)&&
days>1) return 1;
else
return 0;
}
}

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

 package io.nelson;

 public class main {

     public static void main(String[] args) {

         System.out.println("Hello Moto!");

         //4部电影
Movie txwz = new Movie("天下无贼",Movie.REGULAR);
Movie gf = new Movie("功夫",Movie.NEW_RELEASE);
Movie gfxm = new Movie("功夫熊猫",Movie.CHILDRENS);
Movie bxqy = new Movie("冰雪奇缘",Movie.CHILDRENS); //顾客小明
Customer xiaoming = new Customer("xiaoming"); //4个租赁
Rental r1 = new Rental(txwz,4); //天下无贼租赁4天
Rental r2 = new Rental(gf,3); //功夫租赁3天
Rental r3 = new Rental(gfxm,7); //功夫熊猫租赁7天
Rental r4 = new Rental(bxqy,7); //冰雪奇缘租赁7天 xiaoming.addRental(r1);
xiaoming.addRental(r2);
xiaoming.addRental(r3);
xiaoming.addRental(r4); System.out.println(xiaoming.TextStatement());
System.out.println();
System.out.println(xiaoming.HtmlStatement());
}
}

  重构后的主体代码:

 package io.nelson;

 import java.util.Enumeration;
import java.util.Vector; public class Customer { private String _name;
private Vector<Rental> _rentals = new Vector<Rental>();
HtmlStatement htmlStatement;
TextStatement textStatement; public Customer(String name){
_name = name; htmlStatement = new HtmlStatement();
textStatement = new TextStatement();
} public void addRental(Rental arg)
{
_rentals.addElement(arg);
} public Vector<Rental> getRetentals()
{
return _rentals;
} public String getName(){
return _name;
} public String HtmlStatement()
{
return htmlStatement.statement(this);
} public String TextStatement()
{
return textStatement.statement(this);
} public double getTotalAmount()
{
double totalAmount=0;
Enumeration<Rental> rentals = _rentals.elements(); while(rentals.hasMoreElements())
{
Rental each = (Rental) rentals.nextElement(); totalAmount += each.getAmount();
} return totalAmount;
} public int getTotalfrequentRenterPoints()
{
int frequentRenterPoints=0; Enumeration<Rental> rentals = _rentals.elements(); while(rentals.hasMoreElements())
{
frequentRenterPoints++;
Rental each = (Rental) rentals.nextElement(); frequentRenterPoints += each.getFrequentRetenterPoints();
} return frequentRenterPoints;
}
} abstract class Statement
{
public String statement(Customer aCus){
Enumeration<Rental> rentals = aCus.getRetentals().elements();
String result = headerString(aCus); while(rentals.hasMoreElements())
{
Rental each = (Rental) rentals.nextElement();
result += eachString(each);
} result += footerString(aCus); return result;
} abstract String headerString(Customer aCus);
abstract String footerString(Customer aCus);
abstract String eachString(Rental arent);
} class HtmlStatement extends Statement
{
public String footerString(Customer aCus)
{
String result = "";
result += "<P>You owe <EM>"+String.valueOf(aCus.getTotalAmount())+"</EM><P>\n";
result += "On this rental you earned <EM>"+String.valueOf(aCus.getTotalfrequentRenterPoints())+"</EM> frequent renter points<P>"; return result;
} public String headerString(Customer aCus)
{
return ("<H1>Rental Record for <EM>"+aCus.getName()+"</EM><H1><P>\n");
} public String eachString(Rental arent)
{
return (arent.getMoive().getTitle()+ ":"+String.valueOf(arent.getAmount())+"<BR>\n");
}
} class TextStatement extends Statement
{
public String footerString(Customer aCus)
{
String result="";
result += "Amount owed is "+String.valueOf(aCus.getTotalAmount())+"\n";
result += "You earned "+String.valueOf(aCus.getTotalfrequentRenterPoints())+" frequent renter points";
return result;
} public String headerString(Customer aCus)
{
return ("Rental Record for "+aCus.getName()+"\n");
} public String eachString(Rental arent)
{
return ("\t"+arent.getMoive().getTitle()+ "\t"+String.valueOf(arent.getAmount())+"\n");
}
} //租赁类
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 getMoive()
{
return _movie;
} public double getAmount()
{
return _movie.getAmount(getDaysRented());
} public int getFrequentRetenterPoints()
{
return _movie.getFrequentRetenterPoints(getDaysRented());
}
} 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 double getAmount(int days)
{
double amount=0;
switch(getPriceCode())
{
case Movie.REGULAR:
amount += 2;
if(days > 2)
{
amount += (days-2)*1.5;
}
break;
case Movie.NEW_RELEASE:
amount += days*3;
break;
case Movie.CHILDRENS:
amount += 1.5;
if(days > 3)
{
amount += (days-3)*1.5;
}
break;
default:
break;
} return amount;
} public int getFrequentRetenterPoints(int days)
{
if((getPriceCode() == Movie.NEW_RELEASE)&&
days>1) return 1;
else
return 0;
}
}

  重构后的应用代码:

 package io.nelson;

 public class main {

     public static void main(String[] args) {

         System.out.println("Hello Moto!");

         //4部电影
Movie txwz = new Movie("天下无贼",Movie.REGULAR);
Movie gf = new Movie("功夫",Movie.NEW_RELEASE);
Movie gfxm = new Movie("功夫熊猫",Movie.CHILDRENS);
Movie bxqy = new Movie("冰雪奇缘",Movie.CHILDRENS); //顾客小明
Customer xiaoming = new Customer("xiaoming"); //4个租赁
Rental r1 = new Rental(txwz,4); //天下无贼租赁4天
Rental r2 = new Rental(gf,3); //功夫租赁3天
Rental r3 = new Rental(gfxm,7); //功夫熊猫租赁7天
Rental r4 = new Rental(bxqy,7); //冰雪奇缘租赁7天 xiaoming.addRental(r1);
xiaoming.addRental(r2);
xiaoming.addRental(r3);
xiaoming.addRental(r4); System.out.println(xiaoming.TextStatement());
System.out.println();
System.out.println(xiaoming.HtmlStatement());
}
}

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

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

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

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

Form Template Method的更多相关文章

  1. 设计模式(九): 从醋溜土豆丝和清炒苦瓜中来学习"模板方法模式"(Template Method Pattern)

    今天是五.四青年节,祝大家节日快乐.看着今天这标题就有食欲,夏天到了,醋溜土豆丝和清炒苦瓜适合夏天吃,好吃不上火.这两道菜大部分人都应该吃过,特别是醋溜土豆丝,作为“鲁菜”的代表作之一更是为大众所熟知 ...

  2. C#设计模式之十四模板方法模式(Template Method)【行为型】

    一.引言 “结构型”的设计模式已经写完了,从今天我们开始讲“行为型”设计模式.现在我们开始讲[行为型]设计模式的第一个模式,该模式是[模板方法],英文名称是:Template Method Patte ...

  3. C#设计模式之十三模板方法模式(Template Method Pattern)【行为型】

    一.引言 “结构型”的设计模式已经写完了,从今天我们开始讲“行为型”设计模式.现在我们开始讲[行为型]设计模式的第一个模式,该模式是[模板方法],英文名称是:Template Method Patte ...

  4. 行为型模式(一) 模板方法模式(Template Method)

    一.动机(Motivate) "模板方法",就是有一个方法包含了一个模板,这个模板是一个算法.在我们的现实生活中有很多例子可以拿来说明这个模式,就拿吃饺子这个事情来说,要想吃到饺子 ...

  5. C#设计模式系列:模板方法模式(Template Method)

    你去银行取款的时候,银行会给你一张取款单,这张取款单就是一个模板,它把公共的内容提取到模板中,只留下部分让用户来填写.在软件系统中,将多个类的共有内容提取到一个模板中的思想便是模板方法模式的思想. 模 ...

  6. java设计模式 模板方法模式Template Method

    设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了可重用代码.让代码更容易被他人理解.保证代码可靠性.毫无疑问,设计模式于己 ...

  7. 行为型模式之Template Method模式

    模板方法模式(Template Method Pattern) 又叫模板模式,通过定义一个操作的算法骨架,而将一些步骤延迟到子类中,可以不改变一个算法的结构,却又可以重新定义概算法的某些特定步骤. 应 ...

  8. 模板方法模式(Template Method Pattern)

    模板方法模式是一种基于继承的代码复用技术,定义一个操作中的算法的骨架,而将步骤延迟到子类中.模板方法使得子类可以不改变一个算法的结构即可重定义算法的某些特定步骤. 模式中的角色 抽象类(Abstrac ...

  9. 设计模式之美:Template Method(模板方法)

    索引 意图 结构 参与者 适用性 效果 相关模式 实现 实现方式(一):Template Method 模式结构样式代码. 意图 定义一个操作中的算法的骨架,而将一些步骤延迟到子类中. Templat ...

随机推荐

  1. 【Luogu】P1119灾后重建(Floyd)

    题目链接 见题解: feilongz. 这里只放代码. #include<cstdio> #include<cstring> #include<cstdlib> # ...

  2. 【Luogu】P1586四方定理(DP)

    题目链接 此题使用DP.设f[i][j]表示数i用j个数表示,则对于所有的k<=sqrt(i),有 f[i][j]=∑f[i-k*k][j-1] 但是这样会有重复情况.所以先枚举k,再枚举i和j ...

  3. BZOJ 2140 稳定婚姻 ——二分图

    论二分图的可行边与必须边. 考虑用dinic增广之后的图,一些是必要的割边,一些是可行的割边. 我们首先求出一组可行的最大匹配,那么这些变都是可行的. 然后我们求一遍强连通分量. 如果 scc[u]! ...

  4. K-lord #1

    Memories 布雷芙,和YYJ就那样在那个初夏相遇了. 她,一头蓬松的长发,简单可爱的短袖. 他,带着那么灿烂阳光的笑容. 于是一切就变得很微妙.眼神有了温度手心有了潮湿. 那些天空里匆忙盛开的夏 ...

  5. POJ 2279

    线性DP 本题的正解是杨氏矩阵与钩子定理 但是这道题用DP的思想非常好 但是这样会MLE... #include <iostream> #include <cstdio> #i ...

  6. 2017-10-29-morning-清北模拟赛

    T1 遭遇 #include <algorithm> #include <cstdio> #include <cmath> inline void read(int ...

  7. 关于button中设置文字不显示的问题

    这个因为使用的image加载方式是setimage而不是setbackgroundimage导致文字始终出不来.

  8. 【Android开发—智能家居系列】(四):UDP通信发送指令

    思路回顾 [1]手机连接WIFI模块 [2]UDP通信对WIFI模块发送指令,以和WIFI模块保持连接状态 [3]UDP通信对WIFI模块发送指令,让其搜索可用的无线网,返回WIFI列表 [4]发送指 ...

  9. [原创]安装Ubuntu Server 14.04后

    安装后许多软件都没有,需要进行安装. 官方指南:https://help.ubuntu.com/lts/serverguide/index.html 1.修改网络配置文件 用ifconfig查看本地网 ...

  10. Java安全之数字证书

    在前面说到.消息摘要用于验证数据完整性,对称与非对称加密用于保证数据保密性,数据签名用于数据的抗否认性,于是集这些安全手段于一身的终极武器--数字证书出现了.数字证书具备了加密/解密的必要信息.包括签 ...