处理概括关系(继承关系)
字段上移
在所有子类中都具有的字段,将其提取到父类中。
函数上移
某些函数,在各个子类中产生完全相同的结果,将其提取到父类中。
构造函数本体上移
将构造函数相同的字段提取到父类中,如果子类有不同的字段需要实例化,在子类中先实例化父类,然后在添加对应的字段。
class Manager extends Employee {
public Manager(String name, String id, int grade) {
super(name, id);
this.grade = grade;
}
}
上述代码示例中, name 和 id 为父类共有的字段。
字段下移
与字段上移相反,如果只是某个子类才有的特定字段,应该将其从父类中剥离出来,添加到需要该字段的子类中。
函数下移
与字段下移道理相同,如果该函数只是某个子类才拥有的函数,将该函数从父类剥离出来,添加到对应的子类中。
提炼子类
类中的某些特性只被某些实例用到,那么就可以将一部分用到的特性提取出来,添加到子类中。
提炼超类
两个类以相同的方式做类似的事情,或者以不同的方式做类似的事情。就可以在这中间提取出一个公共的父类,然后继承该父类。
提炼接口
若干客户使用类接口中的同一子集,或者两个类的接口有部分相同。将相同的子集提炼到一个独立接口中。
// 书中的示例
double charge(Employee emp, int days) {
int base = emp.getRate() * days;
if (emp.hashSpecialSkill()) {
return base * 1.05;
} else {
return base;
}
}
// 本例中,只关注 Employee 的 getRate() 和 hashSpecialSkill() 两个功能,而 Employee 可能还会有很多其他功能,但是并不关注它。这样就可以定义一个接口,从而强高“我所需要的功能”
interface Billable {
int getRate();
boolean hashSpecialSkill();
}
class Employee implements Billable {
...
// 覆盖接口中的方法
}
double charge(Billable emp, int days) {
int base = emp.getRate() * days;
if (emp.hashSpecialSkill()) {
return bae * 1.05;
} else {
return base;
}
}
折叠继承体系
通过前面的重构手法,如果产生了某个子类未无价值,那么可以考虑将子类与超类进行合并。
塑模板函数
有一些子类,其中相应的某些函数以相同顺序执行类似的操作,但各个操作的细节上有所不同。将这些操作分别放进独立函数中,并保持它们都有相同的签名,于是原函数也就变得相同了。然后将原函数上移至超类。
public String statement() {
Enumeration rentals = _rentals.elements();
String result = "Rental Record for" + getName() +"\n";
while (rentals.hasMoreElements()) {
Rental each = (Rental) rentals.nextElement();
result += "\t" + each.getMove().getTitle() +"\t" + String.valueOf(each.getCharge) + "\n";
}
result += "Amount owed is" + getTotalCharge() + "\n";
result += "You earned" + getTotalFrequentRenterPoints() + " frequent renter points";
return result;
}
public String htmlStatement() {
// html 模板输出
}
// 继承体系
class Statement{}
class TextStatement extends Statement {
public String value(Customer customer) {
//.... 输出的代码
}
}
class HtmlStatement extends Statement {
public String value(Customer customer) {
//... 输出的代码
}
}
// 将行为迁移到子类中
class Customer {
public String Statement() {
return new TextStatement().value(this);
}
public String htmlStatement() {
return new HtmlStatement().value(this);
}
}
// 按照这个步骤,不断的将重复行为提取
// 最终的形态
class abstract Statement {
public String value(Customer customer) {
Enumeration rentals = customer.getRentals();
String result = hearString(customer);
while(rentals.hasMoreElements()) {
Rental each = (Rental) rentals.nextElement();
result += eachRentalString(each);
}
result += footerString(customer);
return result;
}
abstract String headerString(Customer customer);
abstract String eachRentalString(Rental rental);
abstract String footerString(Customer customer);
}
以委托取代继承
使用成员变量保存某个字段,然后在对外提供的方法中重新调用即可。
class MyStatck extends Vector {
public void push(Object element) {
insertElementAt(element, 0);
}
public Object pop() {
Object result = firstElement();
removeElementAt(0);
return result;
}
}
// 重构后
class MyStack {
private Vector verctor = new Vector();
public void push(Object element) {
vector.insertElementAt(element);
}
public Object pop() {
Object result = vector.firstElement();
vector.removeElementAt(0);
return result;
}
public int size() {
return vector.size();
}
public boolean isEmpty() {
return vector.isEmpty();
}
}
以继承取代委托
与上面的重构手法正好相反,将委托改为使用继承。
作者的两条告诫:
- 如果你没有使用受委托类的所有函数,那么 就不应该使用“以继承取代委托”
- 受委托对象不止一个其他对象共享,而且受委托对象是可变的。这种情况也不应该使用“以继承取代委托”