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())
{
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的更多相关文章
- 设计模式(九): 从醋溜土豆丝和清炒苦瓜中来学习"模板方法模式"(Template Method Pattern)
今天是五.四青年节,祝大家节日快乐.看着今天这标题就有食欲,夏天到了,醋溜土豆丝和清炒苦瓜适合夏天吃,好吃不上火.这两道菜大部分人都应该吃过,特别是醋溜土豆丝,作为“鲁菜”的代表作之一更是为大众所熟知 ...
- C#设计模式之十四模板方法模式(Template Method)【行为型】
一.引言 “结构型”的设计模式已经写完了,从今天我们开始讲“行为型”设计模式.现在我们开始讲[行为型]设计模式的第一个模式,该模式是[模板方法],英文名称是:Template Method Patte ...
- C#设计模式之十三模板方法模式(Template Method Pattern)【行为型】
一.引言 “结构型”的设计模式已经写完了,从今天我们开始讲“行为型”设计模式.现在我们开始讲[行为型]设计模式的第一个模式,该模式是[模板方法],英文名称是:Template Method Patte ...
- 行为型模式(一) 模板方法模式(Template Method)
一.动机(Motivate) "模板方法",就是有一个方法包含了一个模板,这个模板是一个算法.在我们的现实生活中有很多例子可以拿来说明这个模式,就拿吃饺子这个事情来说,要想吃到饺子 ...
- C#设计模式系列:模板方法模式(Template Method)
你去银行取款的时候,银行会给你一张取款单,这张取款单就是一个模板,它把公共的内容提取到模板中,只留下部分让用户来填写.在软件系统中,将多个类的共有内容提取到一个模板中的思想便是模板方法模式的思想. 模 ...
- java设计模式 模板方法模式Template Method
设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了可重用代码.让代码更容易被他人理解.保证代码可靠性.毫无疑问,设计模式于己 ...
- 行为型模式之Template Method模式
模板方法模式(Template Method Pattern) 又叫模板模式,通过定义一个操作的算法骨架,而将一些步骤延迟到子类中,可以不改变一个算法的结构,却又可以重新定义概算法的某些特定步骤. 应 ...
- 模板方法模式(Template Method Pattern)
模板方法模式是一种基于继承的代码复用技术,定义一个操作中的算法的骨架,而将步骤延迟到子类中.模板方法使得子类可以不改变一个算法的结构即可重定义算法的某些特定步骤. 模式中的角色 抽象类(Abstrac ...
- 设计模式之美:Template Method(模板方法)
索引 意图 结构 参与者 适用性 效果 相关模式 实现 实现方式(一):Template Method 模式结构样式代码. 意图 定义一个操作中的算法的骨架,而将一些步骤延迟到子类中. Templat ...
随机推荐
- Java 学习(3):java 对象和类
目录: --- 对象 --- 类 --- 源文件的声明规则 --- Java 包 对象: 对象是类的一个实例(对象不是找个女朋友),有状态和行为.例如,一条狗是一个对象,它的状态有:颜色.名字.品种: ...
- 洛谷 [P1939] 矩阵加速数列
矩阵快速幂模版 #include <iostream> #include <cstring> #include <cstdlib> #include <alg ...
- 星球大战 BZOJ 1015
星球大战 [问题描述] 很久以前,在一个遥远的星系,一个黑暗的帝国靠着它的超级武器统治者整个星系.某一天,凭着一个偶然的机遇,一支反抗军摧毁了帝国的超级武器,并攻下了星系中几乎所有的星球.这些星球通过 ...
- python--爬取http://www.kuaidaili.com/并保存为xls
代码如下: 复制在python3上先试试吧^_^ # -*- coding: utf-8 -*- """ Created on Mon Jun 12 13:27:59 2 ...
- php——两种无限级分类
/** * 无级递归分类 TP框架 * @param int $assortPid 要查询分类的父级id * @param mixed $tag 上下级分类之间的分隔符 * @return strin ...
- JFinal Weixin 1.6发布【转】
原文:http://www.oschina.net/news/69495/jfinal-weixin-1-6-released#rd 继JFinal 2.1发布之后,再来一发JFinal Weixin ...
- 实现TTCP (检测TCP吞吐量)
实现TTCP (检测TCP吞吐量) 应用层协议 为了解决TCP粘包问题以及客户端阻塞问题 设计的应用层协议如下: //告知要发送的数据包个数和长度 struct SessionMessage { in ...
- DFS与BFS对比
前面已经说过了深搜和广搜了,是不是有点还不是很好的分清他们?(其实分不分的请都没大有关系) 下面我们来看一看广搜与深搜的区别吧. 算法步骤上的区别 深度优先遍历图算法步骤: 1.访问顶点v 2,.依次 ...
- DTD概述
1. 什么是XML文件 可扩展标记语言,标准通用标记语言的子集,是用于标记电子文件使其具有结构性的标记语言. 2. 什么是dtd文件 DTD(文档类型定义)的作用是定义XML文档的合法构建模块.它使用 ...
- centos下开启htaccess
不知道原本 centOS是否默认支持 .htaccess 可能是因为我总弄配置文件无意中给搞坏了 今天要用到就查了下怎么开启 想要顺利开启需注意以下几点, 这几点都是在httpd.conf 这个配置文 ...