从if、then、else三个段落中分别提炼出独立函数
if (date.before(SUMMER_START) || date.after(SUMMER_END))
charge = quantity * _winterRate + _winterServiceCharge;
else charge = quantity * _summerRate;
=>
if (notSummer(date))
charge = winterCharge(quantity);
else charge = summerCharge(quantity);
动机
将条件分支的代码分解成多个独立函数,根据每个小块代码的用途,为分解而得的新函数命名,并将原函数中对应的代码改为调用新建函数,可以更清楚地表达自己的意图
做法
如果有一系列条件测试,都得到相同结果,将这些测试合并为一个条件表达式,并将这个条件表达式提炼成为一个独立函数
double disabilityAmount(){
if(_seniority < 2) return 0;
if(_monthsDisabled > 12) return 0;
if(_isPartTime) return 0;
}
=>
double disabilityAmount(){
if(isNotEligableForDisability()) return 0;
}
动机
如果一串条件检查:检查条件各不相同,最终行为却一致,就应该将它们合并为一个条件表达式,之所以要合并条件代码,有两个重要原因,首先,合并后的条件代码用意更清晰,其次,这项重构往往可以为使用Extract Method做好准备
做法
在条件表达式的每个分支上有着相同的一段代码,将这段重复代码搬移到条件表达式之外
if(isSpecialDeal()){
total = price * 0.95;
send();
}
else{
total = price * 0.98;
send();
}
=>
if(isSpecialDeal())
total = price * 0.95;
else
total = price * 0.98;
send();
动机
有助于清楚地表明哪些东西随条件的变化而变化、哪些东西保持不变
做法
在一系列布尔表达式中,某个变量带有”控制标记”的作用,以break语句或return语句取代控制标记
动机
用break语句和continue语句跳出复杂的条件语句
做法
范例:以break取代简单的控制标记
void checkSecurity(String[] people){
boolean found = false;
for(int i = 0; i < people.length; i++){
if(!found){
if(people[i].equals("Don")){
sendAlert();
found = true;
}
if(people[i].equals("John")){
sendAlert();
found = true;
}
}
}
}
=>
void checkSecurity(String[] people){
for(int i = 0; i < people.length; i++){
if(people[i].equals("Don")){
sendAlert();
break;
}
if(people[i].equals("John")){
sendAlert();
break;
}
}
}
范例:以return返回控制标记
void checkSecurity(String[] people){
String found = "";
for(int i = 0; i < people.length; i++){
if(found.equals("")){
if(people[i].equals("Don")){
sendAlert();
found = "Don";
}
if(people[i].equals("John")){
sendAlert();
found = "John";
}
}
}
someLaterCode(found);
}
=>
void checkSecurity(String[] people){
String found = foundMiscreant(people);
someLaterCode(found);
}
String foundMiscreant(String[] people){
String found = "";
for(int i = 0; i < people.length; i++){
if(found.equals("")){
if(people[i].equals("Don")){
sendAlert();
return "Don";
}
if(people[i].equals("John")){
sendAlert();
return "John";
}
}
}
return "";
}
函数中的条件逻辑使人难以看清正常的执行路径,使用卫语句表现所有特殊情况
double getPayAmount(){
double result;
if(_isDead) result = deadAmount;
else{
if(_isSeparated) result = separatedAmount();
else{
if(_isRetired) result = retiredAmount();
else result = normalPayAmount();
}
}
return result;
}
=>
double getPayAmount(){
if(_isDead) return deadAmount();
if(_isSeparated) return separatedAmount();
if(_isRetired) return retiredAmount;
return normalPayAmount();
}
动机
条件表达式通常有两种表现形式,第一种是:所有分支都属于正常行为,第二种是:条件表达式提供的答案中只有一种是正常行为,其他都是不常见的情况
如果某个条件极其罕见,就应该单独检查该条件,并在该条件为真时立刻从函数中返回,这样的单独检查常常被称为”卫语句”
做法
范例:将条件反转
public double getAdjustedCapital(){
double result = 0.0;
if(_capital > 0.0){
if(_intRate > 0.0 && _duration > 0.0){
result = (_income / _duration) * ADJ_FACTOR;
}
}
return result;
}
=>
public double getAdjustedCapital(){
double result = 0.0;
if(_capital <= 0.0) return 0.0;
if(_intRate <= 0.0 || _duration <= 0.0) return 0.0;
return (_income / _duration) * ADJ_FACTOR;
}
将条件表达式的每个分支放进一个子类内的覆写函数中,然后将原始函数声明为抽象函数
double getSpeed(){
switch(_type){
case EUROPEAN:
return getBaseSpeed();
case AFRICAN:
return getBaseSpeed() - getLoadFactory() * _numberOfCoconuts;
case NORWEGIAN_BLUE:
return (_isNailed) ? 0 : getBaseSpeed(_voltage);
}
throw new RuntimeException("Should be unreachable");
}
=>
动机
多态最根本的好处就是:如果需要根据对象的不同类型而采取不同的行为,多态使你不必编写明显的条件表达式
做法
范例
class Employee...
int payAmount(int type){
switch(type){
case Employee.ENGINEER:
return _monthlySalary;
case Employee.SALESMAN:
return _monthlySalary + _commission;
case Employee.MANAGER:
return _monthlySalary + _bonus;
default:
throw new IllegalArgumentException("Incorrect type code value");
}
}
private Employee _type;
int getType(){
return _type.getTypeCode()
}
abstract class EmployeeType...
abstract int getTypeCode();
class Engineer extends EmployeeType...
int getTypeCode(){
return Employee.ENGINEER:
}
...and other subclasses
=>
class Employee...
int payAmount(){
return _type.payAmount(this);
}
class EmployeeType...
int payAmount(Employee emp){
switch(getTypeCode()){
case Employee.ENGINEER:
return emp.getMonthlySalary();
case Employee.SALESMAN:
return emp.getMonthlySalary() + emp.getCommission();
case Employee.MANAGER:
return emp.MonthlySalary() + emp.getBonus();
default:
throw new IllegalArgumentException("Incorrect type code value");
}
}
=> 把条件分支语句放入子类函数中
class EmployeeType...
abstract int payAmount(Employee emp);
class Engineer...
int payAmount(Employee emp){
return emp.getMonthlySalary();
}
...
将null值替换为null对象
动机
当实例变量的某个字段内容允许为null时,在进行操作时往往要进行非空判断,这个工作是非常繁杂的,所以不让实例变量被设为null,而是插入各式各样的空对象——它们都知道如何正确地显示自己,这样就可以摆脱大量过程化的代码
空对象一定是常量,它们的任何成分都不会发生变化,因此可以使用Singleton模式来实现它们
做法
范例
class Site...
Customer getCustomer(){
return _customer;
}
Customer _customer;
class Customer...
public String getName(){...}
public BillingPlan getPlan(){...}
public PaymentHistory getHistory(){...}
public class PaymentHistory...
int getWeesDelingquentInLastYear()
Customer customer = site.getCustomer();
BillingPlan plan;
if(customer == null) plan = BillingPlan.basic();
else plan = customer.getPlan();
...
=>
class NullCustomer extens Customer{
public boolean isNull(){
return true;
}
}
class Customer...
public boolean isNull(){
return false;
}
static Customer new Null(){
return new NullCustomer();
}
class Site...
Customer getCustomer(){
return (_customer == null) ? Customer.newNull() : _customer;
}
Customer customer = site.getCustomer();
BillingPlan plan;
if(customer.isNull()) plan = BillingPlan.basic();
else plan = customer.getPlan();
如果某一段代码需要对程序状态做出某种假设,以断言明确表现这种假设
double getExpenseLimit(){
return (_expenseLimit != NULL_EXPENSE) ? _expenseLimit:_primaryProject.getMemberExpenseLimit();
}
=>
double getExpenseLimit(){
Assert.isTrue(_expenseLimit != NULL_EXPENSE || _primaryProject != null);
return (_expenseLimit != NULL_EXPENSE) ? _expenseLimit:_primaryProject.getMemberExpenseLimit();
}
动机
常常会有这样一段代码:只有当某个条件为真时,该段代码才能正常运行,这时应该使用断言,把不符合条件的假设标明出来
断言可以作为交流与调试的辅助,在交流的角度上,断言可以帮助程序阅读者理解代码所做的假设;在调试的角度上,断言可以在距离bug最近的地方抓住它们
在一段逻辑中加入断言是有好处的,因为它迫使你重新考虑这段代码的约束条件,如果不满足这些约束条件,程序也可以正常运行,断言就不会带给你任何帮助,只会把代码变得混乱,并且有可能妨碍以后的修改
做法
如果函数的名称未能揭示函数的用途,就应该修改函数名称
动机
将复杂的处理过程分解成小函数,并给这些函数起一个好名称
做法
某个函数需要从调用端得到更多信息,为此函数添加一个对象参数,让该对象带进函数所需信息
做法
当函数本体不再需要某个参数时,将该参数去除
做法
如果某个函数既返回对象状态值,又修改对象状态,就应该建立两个不同的函数,其中一个负责查询,另一个负责修改
动机
任何有返回值的函数,都不应该有看得到的副作用,如果遇到一个”既有返回值又有副作用”的函数,就应该试着将查询动作从修改动作中分割出来
做法
范例
String foundMiscreant(String[] people){
for(int i = 0; i < people.length; i++){
if(people[i].equals("Don")){
sendAlert();
return "Don";
}
if(people[i].equals("John")){
sendAlert();
return "John";
}
}
return "";
}
void checkSecurity(String[] people){
String found = foundMiscreant(people);
someLaterCode(found);
}
=>
void sendAlert(String[] people){
if(!foundPerson(people).equals(""))
sendAlert();
}
String foundPerson(String[] people){
for(int i = 0; i < people.length; i++){
if(people[i].equals("Don")){
return "Don";
}
if(people[i].equals("John")){
return "John";
}
}
return "";
}
若干函数做了类似的工作,但在函数本体中却包含了不同的值,应该建立单一函数,以参数表达那些不同的值
动机
如果两个函数做着类似的工作,但因少数几个值致使行为略有不同,这种情况下, 应该将这些各自分离的函数统一起来,并通过参数来处理那些变化情况,用以简化问题,这样可以去除重复代码,并提高灵活性
做法
范例
protected Dollars baseCharge(){
double result = Math.min(lastUsage(), 100) * 0.03;
if (lastUsage() > 100){
result += (Math.min(lastUsage(), 200) - 100) * 0.05;
}
if (lastUsage() > 200){
result += (lastUsage() - 200) * 0.07;
}
return new Dollars(result);
}
=>
protected Dollars baseCharge(){
double result = usageInRange(0, 100) * 0.03;
result += usageInRange(100, 200) * 0.05;
result += usageInRange(200, Integer.MAX_VALUE) * 0.07;
return new Dollars(result);
}
protected int usageInRange(int start, int end){
if (lastUsage() > start) return Math.min(lastUsage(), end) - start;
else return 0;
}
如果一个函数,其中完全取决于参数值而采取不同行为,应该针对该参数的每一个可能值建立一个独立函数
void setValue(String name, int value){
if(name.equals("height")){
_height = value;
return;
}
if(name.equals("width")){
_width = value;
return;
}
}
=>
void setHeight(int arg){
_height = arg;
}
void setWidth(int arg){
_width = arg;
}
动机
如果某个参数有多种可能的值,而函数内又以条件表达式检查这些参数值,并根据不同参数值做出不同的行为,那么就应该使用本项重构
如果参数值不会对函数行为有太多影响,就不应该使用Replace Parameter with Explicit Methods,如果的确需要条件判断的行为,可考虑使用Replace Conditional with Polymorphism
做法
如果从某个对象中取出若干值,将它们作为某一次函数调用时的参数,应该改为传递整个对象
int low = daysTempRange().getLow();
int high = daysTempRange().getHigh();
withinPlan = plan.withinRange(low, high);
=>
withinPlan = plan.withinRange(daysTempRange());
动机
本项重构不仅能够使参数列更稳固外,还能提高代码的可读性,但是如果传递的对象会使依赖结构恶化,就不该使用本项重构
做法
对象调用某个函数,并将所得结果作为参数,传递给另一个函数。而接受该参数的函数本身也能够调用前一个函数,就应该让参数接收者去除该项参数,并直接调用前一个函数
int basePrice = _quantity * _itemPrice;
discountLevel = getDiscountLevel();
double finalPrice = discountedPrice(basePrice, discountLevel);
=>
int basePrice = _quantity * _itemPrice;
double finalPrice = discountedPrice(basePrice);
动机
如果函数可以通过其他途径获得参数值,那么它就不应该通过参数取得该值
做法
范例
public double getPrice(){
int basePrice = _quantity * _itemPrice;
int discountLevel;
if(_quantity > 100) discountLevel = 2;
else discountLevel = 1;
double finalPrice = discountedPrice(basePrice, discountLevel);
return finalPrice;
}
private double discountedPrice(int basePrice, int discountLevel){
if(discountLevel == 2) return basePrice * 0.1;
else return basePrice * 0.05;
}
=>
public double getPrice(){
if(getDiscountLevel() == 2) return getBasePrice() * 0.1;
else return getBasePrice() * 0.05;
}
private double getBasePrice(){
return _quantity * _itemPrice;
}
private int getDiscountLevel(){
if(_quantity > 100) return 2;
else return 1;
}
某些参数总是很自然地同时出现,应该以一个对象取代这些参数
动机
缩短参数列,减少重复代码
做法
范例
class Entry...
Entry(double value, Date chargeDate){
_value = value;
_chargeDate = chargeDate;
}
Date getDate(){
return _chargeDate;
}
double getValue(){
return _value;
}
private Date _chargeDate;
private double _value;
class Account...
double getFlowBetween(Date start, Date end){
double result = 0;
Enumeration e = _entries.elements();
while(e.hasMoreElements()){
Entry each = (Entry)e.nextElement();
if(each.getDate().equals(start) || each.getDate().equals(end) || (each.getDate().after(start) && each.getDate().before(end))){
result += each.getValue();
}
}
return result;
}
private Vector _entries = new Vector();
client code...
double flow = anAccount.getFlowBetween(startDate, endDate);
=>
class DateRange{
DateRange(Date start, Date end){
_start = start;
_end = end;
}
Date getStart(){
return _start;
}
Date getEnd(){
return _end;
}
private final Date _start;
private final Date _end;
}
class Account...
double getFlowBetween( DateRange range){
double result = 0;
Enumeration e = _entries.elements();
while(e.hasMoreElements()){
Entry each = (Entry) e.nextElement();
if(each.getDate().equals(range.getStart()) || each.getDate().equals(range.getEnd()) ||
(each.getDate().after(range.getStart()) && each.getDate().before(range.getEnd()))){
result += each.getValue();
}
}
return result;
}
client code...
double flow = anAccount.getFlowBetween(new DateRange(startDate, endDate));
=>
class Account...
double getFlowBetween(DateRange range){
double result = 0;
Enumeration e = _entries.elements();
while(e.hasMoreElements()){
Entry each = (Entry) e.nextElement();
if(range.includes(each.getDate())){
result += each.getValue();
}
}
return result;
}
class DateRange...
boolean includes(Date arg){
return (arg.equals(_start) || arg.equals(_end) || (arg.after(_start) && arg.before(_end)));
}
类中的某个字段应该在对象创建时被设值,然后就不再改变,应该去掉该字段的所有设值函数
动机
如果为某个字段提供设值函数,就暗示这个字段可以被改变
做法
范例
class Account{
private String _id;
Account(String id){
setId(id);
}
void setId(String arg){
_id = arg;
}
}
=>
class Account{
private final String _id;
Account(String id){
_id = id;
}
}
有一个函数,从来没有被其他任何类用到,就将这个函数修改为private
做法
如果你希望在创建对象时不仅仅是做简单的建构动作,就应该将构造函数替换为工厂函数
Employee (int type){
_type = type;
}
=>
static Employee create(int type){
return new Employee(type);
}
动机
使用本项重构的最显而易见的动机,就是在派生子类的过程中以工厂函数取代类型码,还可以令工厂函数根据参数的个数和类型,选择不同的创建行为
做法
范例:根据整数(实际是类型码)创建对象
class Employee{
private int _type;
static final int ENGINEER = 0;
static final int SALESMAN = 1;
static final int MANAGER = 2;
Employee(int type){
_type = type;
}
}
=>
static Employee create(int type){
return new Employee(type);
}
client code...
Employee eng = Employee.create(Employee.ENGINEER);
范例:根据字符串创建子类对象
static Employee create(int type){
switch(type){
case ENGINEER:
return new Engineer();
case SALESMAN:
return new Salesman();
case MANAGER:
return new Manager();
default:
throw new IllegalArgumentException("Incorrect type code value");
}
}
=>
static Employee create(String name){
try{
return (Employee) Class.forName(name).newInstance();
}catch(Exception e){
throw new IllegalArgumentException("Incorrect type code value");
}
}
Employee.create("Engineer")
范例:以明确函数创建子类
// 只有少数几个子类,而且它们都不再变化
class Person...
static Person createMale(){
return new Male();
}
static Person createFemale(){
return new Female();
}
Person kent = Person.createMale();
某个函数返回的对象,需要由函数调用者执行向下转型,应该将向下转型动作移到函数中
Object lastRead(){
return readings.lastElement();
}
=>
Reading lastRead(){
return (Reading)readings.lastElement();
}
做法
将某个函数返回一个特定的代码,用以表示某种错误情况,改为使用异常
int withdraw(int amount){
if(amount > _balance)
return -1;
else{
_balance -= amount;
return 0;
}
}
=>
void withdraw(int amount) throws BalaceException{
if(amount > _balance) throw new BalanceException();
_balance -= amount;
}
做法
范例
class Account...
int withdraw(int amount){
if(amount > _balance)
return -1;
else{
_balance -= amount;
return 0;
}
}
private int _balance;
// 非受控异常
class Account...
void withdraw(int amount){
Assert.isTrue("sufficient funds", amount <= _balance);
_balance -= amount;
}
class Assert...
static void isTrue(String comment, boolean test){
if(!test){
throw new RuntimeException("Assertion failed:" + comment);
}
}
// 受控异常
class BalanceException extends Exception{}
try{
account.newWithdraw(amount);
doTheUsualThing();
}catch(BalanceException e){
handleOverdrawn();
}
void withdraw(int amount) throws BalanceException{
try{
newWithdraw(amount);
return 0;
}catch(BalanceException e){
return -1;
}
}
void newWithdraw(int amount) throws BalanceException{
if(amount > _balance) throws new BalanceException();
_balance -= amount;
}
修改调用者,使它在调用函数之前先做检查
double getValueForPeriod(int periodNumber){
try{
return _values[periodNumber];
} catch(ArrayIndexOutOfBoundsException e){
return 0;
}
}
=>
double getValueForPeriod(int periodNumber){
if(periodNumber >= _values.length) return 0;
return _values[periodNumber];
}
动机
异常可协助我们避免很多复杂的错误处理逻辑,但是异常应该用于那些产生意料之外的错误的行为,而不应该成为条件检查的替代品
做法
如果两个子类拥有相同的字段,应该将该字段移至超类
动机
去除子类重复的数据声明和重复的行为
做法
有些函数,在各个子类中产生完全相同的结果,就应该将该函数移至超类
动机
避免重复的行为
做法
如果在各个子类中拥有一些构造函数,它们的本体几乎完全一致,应该在超类中新建一个构造函数,并在子类构造函数中调用它
class Manager extens Employee...
public Manager(String name, String id, int grade){
_name = name;
_id = id;
_grade = grade;
}
=>
public Manager(String name, String id, int grade){
super(name, id);
_grade = grade;
}
做法
超类中的某个函数只与部分(而非全部)子类有关,将这个函数移到相关的那些子类去
做法
超类中的某个字段只被部分(而非全部)子类用到,将这个字段移到需要它的那些子类去
做法
类中的某些特性只被某些(而非全部)实例用到,新建一个子类,将上面所说的那一部分特性移到子类中
动机
当类中的某些行为只被一部分实例用到,其他实例不需要它们
做法
范例
class JobItem...
public JobItem(int unitPrice, int quantity, boolean isLabor, Employee employee){
_unitPrice = unitPrice;
_quantity = quantity;
_isLabor = isLabor;
_employee = employee;
}
public int getTotalPrice(){
return getUnitPrice() * _quantity;
}
public int getUnitPrice(){
return (_isLabor) ? _employee.getRate():_unitPrice;
}
public int getQuantity(){
return _quantity;
}
public Employee getEmployee(){
return _employee;
}
private int _unitPrice;
private int _quantity;
private Employee _employee;
private boolean _isLabor;
class Employee...
public Employee(int rate){
_rate = rate;
}
public int getRate(){
return _rate;
}
private int _rate;
=> 上述某些行为和数据只在按工时(labor)收费的情况下才需要
class LaborItem extends JobItem{
public LaborItem(int unitPrice, int quantity){
super(0, quantity, true);
_employee = employee;
}
public int getUnitPrice(){
return _employee.getRate();
}
protected boolean isLabor(){
return true;
}
public Employee getEmployee(){
return _employee;
}
}
class JobItem...
protected JobItem(int unitPrice, int quantity, boolean isLabor){
_unitPrice = unitPrice;
_quantity = quantity;
_isLabor = isLabor;
}
public JobItem(int unitPrice, int quantity){
this(unitPrice, quantity, false, null);
}
protected boolean isLabor(){
return true;
}
public int getUnitPrice(){
return _unitPrice;
}
protected Employee _employee;
JobItem j1 = new LaborItem(0, 5);
两个类有相似特性,为这两个类建立一个超类,将相同特性移至超类
做法
若干客户使用类接口中的同一子集,或者两个类的接口有部分相同,应该将相同的子集提炼到一个独立接口中
做法
超类和子类之间无太大区别,就将它们合为一体
动机
继承体系很容易变得过分复杂,如果某个子类并未带来该有的价值,就把超类与子类合并起来
做法
有一些子类,其中相应的某些函数以相同顺序执行类似的操作,但各个操作的细节上有所不同,可以将这些操作分别放进独立函数中,并保持它们都有相同的签名,于是原函数也就变得相同了,然后将原函数上移至超类
动机
继承是避免重复行为的一个强大工具,只要两个子类中有类似的函数,就可以把它们提升到超类
若两个函数以相同顺序执行大致相近的操作,但是各操作不完全相同,这种情况下可以将执行操作的序列移至超类,并借助多态保证各操作仍得以保持差异性,这样的函数被称为Template Method(模板函数)
做法
范例
class Statement...
public String statement(){
Enumeration rentals = _rentals.elements();
String result = "Rental Record for " + getName() + "\n";
while(rentals.hasMoreELements()){
Rental each = (Rental)rentals.nextElement();
// show figures for this rental
result += "\t" + each.getMovie().getTitle() + "\t" + String.valueOf(each.getCharge()) + "\n";
}
// add footer lines
result += "Amount owed is " + String.valueOf(getTotalCharge()) + "\n";
result += "You earched " + String.valueOf(getTotalFrequentRenterPoints()) + " frequent renter points";
return result;
}
public String htmlStatement(){
Enumeration rentals = _rentals.elements();
String result = "Rentals for" + getName() + "
\n";
while(rentals.hasMoreElements()){
Rental each = (Rental) rentals.nextElement();
// show figures for each rental
result += each.getMovie().getTitle() + ": " + String.valueOf(each.getCharge()) + "
\n";
result += "On this rental you earned " + String.valueOf(getTotalFrequentRenterPoints()) + " frequent renter points
";
return result;
}
}
=>
class Statement...
public String value(Customer aCustomer){
Enumeration rentals = aCustomer.getRentals();
String result = headerString(aCustomer);
while(rentals.hasMoreElements()){
Rental each = (Reantal) rentals.nextElement();
result += eachRentalString(each);
}
result += footerString(aCustomer);
return result;
}
abstract String headerString(Customer aCustomer);
abstract String eachRentalString(Rental aRental);
abstract String footerString(Customer aCustomer);
class TextStatement...
String eachRentalString(Rental aRental){
return "\t" + aRental.getMovie().getTitle() + "\t" + String.valueOf(aRental.getCharge()) + "\n";
}
String footerString(Customer aCustomer){
return "Amount owed is " + String.valueOf(aCustomer.getTotalCharge()) + "\n" + "Yout earned " + String.valueOf(aCustomer.getTotalFrequentRenterPoints()) + " frequent renter points";
}
某个子类只使用超类接口中的一部分,或者根本不需要继承而来的数据,应该在子类中新建一个字段用以保存超类,调整子类函数,令它改而为委托超类,然后去掉两者之间的继承关系
动机
如果以委托取代继承,可以更清楚的表明:你只需要受托类的一部分功能,接口中的哪一部分应该被使用,哪一部分应该被忽略,完全由你主导控制
做法
范例
class MyStack extends Vector{
public void push(Object element){
insertElementAt(element, 0);
}
public Object pop(){
Object result = firestElement();
removeElementAt(0);
return result;
}
}
=>
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;
}
}
如果在两个类之间使用委托关系,并经常为整个接口编写许多极简单的委托函数,可以让委托类继承受委托类
动机
如果委托类没有使用受托类的所有函数,就不应该使用本重构
如果受托对象被不止一个其他对象共享,而且受托对象是可变的,在这种情况下就不能将委托关系替换成继承关系,因为这样就无法再共享数据了
做法
如果某个继承体系同时承担两项责任,就应该建立两个继承体系,并通过委托关系让其中一个可以调用另一个
动机
混乱的继承体系是一个严重的问题,因为它会导致重复代码,使修改变得困难,因为特定问题的解决策略被分散到了整个继承体系
要指出继承体系是否承担了两项不同的责任并不困难:如果继承体系中的某一特定层级上的所有类,其子类名称都以相同的形容词开始,那么这个体系很有可能就是承担着两项不同的责任
做法
范例
如果有一些传统过程化风格的代码,可以将数据记录变成对象,将大块的行为分成小块,并将行为移入相关对象之中
做法
如果某些GUI类之中包含了领域逻辑,应该将领域逻辑分离出来,为它们建立独立的领域类
动机
MVC(模型-视图-控制器)模式的最核心价值在于:它将用户界面代码(即视图)和领域逻辑(即模型)分离了,这样会使程序的修改变得更加容易,同时也使同一业务逻辑的多展现方式成为可能
做法
如果某个类做了太多工作,其中一部分工作是以大量条件表达式完成的,就建立继承体系,以一个子类表示一种特殊情况
做法
如果非常清楚原始类会有哪些变化情况,可以使用另一种做法