有一批重构手法专门用来处理类的概括关系,即继承关系。其中主要是将函数上下移动于继承体系之中。
1、Pull Up Field(字段上移)
两个子类拥有相同的字段。将该字段移至超类。
2、Pull Up Method(函数上移)
有些函数,在各个类中产生完全相同的结果。将该函数移至超类。
3、Pull Up Constructor Body(构造函数本体上移)
你在各个子类中拥有一些构造函数,它们本体几乎完全一致。在超类中新建一个构造函数,并在子类构造函数中调用它。
代码1:
class Manager extends Employee...
public Manager(String name, String id, int grade){
_name = name;
_id = id;
_grade = grade;
}
代码2:
public Manager(String name, String id, int grade){
super(name, id);
_grade = grade;
}
4、Push Down Method(函数下移)
超类中的某个函数只与部分(而非全部)子类有关。将这个函数移到相关的那些子类去。
5、Push Down Field(字段下移)
超类中的某个字段只被部分(而非全部)子类用到。将这个字段移到需要它的那些子类去。
6、Extract Subclass(提炼子类)
类中的某些特性只被某些(而非全部)实例用到。新建一个子类,将上面所说的那一部分特性移到子类中。
7、Extract Superclass(提炼超类)
两个类有相似特性。为这两个类建立一个超类,将相同特性移至超类。
8、Extract Interface(提炼接口)
若干客户使用类接口中的同一子集,或者两个类的接口有部分相同。将相同的子集提炼到一个独立接口中。
9、Collapse Hierarchy(折叠继承体系)
10、Form Template Method(塑造模板函数)
动机:你有一些子类,其中相应的某些函数以相同顺序执行类似的操作,但各个操作的细节上有所不同。
做法:将这些操作分别放进独立函数中,并保持它们都有相同的签名,于是原函数也就变得相同。然后将原函数上移至超类。
代码1:
class Customer { public String statement(){ return new TextStatement().value(this); } public String htmlStatement(){ return new HtmlStatement().value(this); } } class TextStatement{ public String value(Customer aCustomer){ Enumeration<e> rentals = aCustomer.getRentals(); String result = "Rental Record for " + aCustomer.getName() + " "; while(rentals.hasMoreElements()){ Rental each = (Rental) rentals.nextElement(); //show figures for this rental result += " " + each.getMovie().getTitle() + " " + String.valueOf(each.getCharge()) + " "; } //add footer lines result += "Amount owed is " + String.valueOf(aCustomer.getTotalCharge()) + " "; result += "You earned " + String.valueOf(aCustomer.getTotalFrequentRenterPoints()) + " frequent renter points"; return result; } } class HtmlStatement { public String value(Customer aCustomer){ Enumeration<e> rentals = aCustomer.getRentals(); String result = "<H1>Rental Record for <EM>" + aCustomer.getName() + "</EM></H1><P> "; while(rentals.hasMoreElements()){ Rental each = (Rental) rentals.nextElement(); //show figures for this rental result += each.getMovie().getTitle() + ":" + String.valueOf(each.getCharge()) + "<BR> "; } //add footer lines result += "<P>You owed <EM>" + String.valueOf(aCustomer.getTotalCharge()) + "</EM><P> "; result += "On this rental you earned <EM> " + String.valueOf(aCustomer.getTotalFrequentRenterPoints()) + "</EM> frequent renter points<P>"; return result; } }
代码2:将上面两个类中不同的地方抽出来,分别放进独立函数中。
class TextStatement{ String headerString(Customer aCustomer){ return "Rental Record for " + aCustomer.getName() + " "; } String eachRentalString(Rental aRental){ return " " + aRental.getMovie().getTitle() + " " + String.valueOf(aRental.getCharge()) + " "; } String footerString(Customer aCustomer){ return "Amount owed is " + String.valueOf(aCustomer.getTotalCharge()) + " " +"You earned " + String.valueOf(aCustomer.getTotalFrequentRenterPoints()) + " frequent renter points"; } public String value(Customer aCustomer){ Enumeration<e> rentals = aCustomer.getRentals(); String result = headerString(aCustomer); while(rentals.hasMoreElements()){ Rental each = (Rental) rentals.nextElement(); //show figures for this rental result += eachRentalString(each); } //add footer lines result += footerString(aCustomer); return result; } } class HtmlStatement { String headerString(Customer aCustomer){ return "<H1>Rental Record for <EM>" + aCustomer.getName() + "</EM></H1><P> "; } String eachRentalString(Rental aRental){ return aRental.getMovie().getTitle() + ":" + String.valueOf(aRental.getCharge()) + "<BR> "; } String footerString(Customer aCustomer){ return "<P>You owed <EM>" + String.valueOf(aCustomer.getTotalCharge()) + "</EM><P> " + "On this rental you earned <EM> " + String.valueOf(aCustomer.getTotalFrequentRenterPoints()) + "</EM> frequent renter points<P>"; } public String value(Customer aCustomer){ Enumeration<e> rentals = aCustomer.getRentals(); String result = headerString(aCustomer); while(rentals.hasMoreElements()){ Rental each = (Rental) rentals.nextElement(); //show figures for this rental result += eachRentalString(each); } //add footer lines result += footerString(aCustomer); return result; } }
代码3:经过第二步处理,我们将相同的函数提炼到超类中。函数内容不同提炼为抽象类。
abstract class Statement{ abstract String headerString(Customer aCustomer); abstract String eachRentalString(Rental aRental); abstract String footerString(Customer aCustomer); public String value(Customer aCustomer){ Enumeration<e> rentals = aCustomer.getRentals(); String result = headerString(aCustomer); while(rentals.hasMoreElements()){ Rental each = (Rental) rentals.nextElement(); //show figures for this rental result += eachRentalString(each); } //add footer lines result += footerString(aCustomer); return result; } }
第四步:TextStatement、HtmlStatement 继承Statement,并实现抽象函数。
11、Replace Inheritance with Delegation(以委托取代继承)
动机:某个子类只使用接口中的一部分,或是根本不需要继承而来的数据。
代码1:只要看看MyStack的用户,我就会发现,用户只要它做4件事:push()、pop()、size()和isEmpty()。后两个函数就是从Vector继承来的。
class MyStack extends Vector{
public void push(Object element){
insertElementAt(element, 0);
}
public Object pop(){
Object result = firstElement();
removeElementAt(0);
return result;
}
}
代码2:跟封装模式有点类似,只不过只封装了需要用到的函数。
class MyStack{
private Vector _vector = new Vector();
public void push(Object element){
_vector.insertElementAt(element, 0);
}
public Object pop(){
Object result = _vector.firstElement();
_vector.removeElementAt(0);
return result;
}
public int size(){
return _vector.size();
}
public boolean isEmpty(){
return _vector.isEmpty();
}
}
12、Replace Delegation with Inheritance(以继承取代委托)
动机:需要用到受委托类的所有函数。