Summary: 你有一个类型码,它会影响类的行为,但你无法通过继承手法消除它。以状态对象取代类型码。
动机:
本项重构和Replace Type Code with Subclasses很相似,但如果“类型码的值在对象生命期中发生变化”或“其他原因使得宿主类不能被继承”,你也可以使用本重构。本重构使用State模式或Strategy模式。
State模式和Strategy模式非常相似,因此无论你选择其中哪一种,重构过程都是相同的。“选择哪一个模式”并非问题关键所在,你只需要选择更适合特定情境的模式就行了。如果你打算在本项重构完成之后再以Replace Conditional with Polymorphism简化一个算法,那么选择Strategy模式比较合适;如果你打算搬移与状态相关的数据,而且你把新建对象视为一种变迁状态,就应该选择使用State模式。
做法:
1.使用Self Encapsulate Field将类型码自我封装起来。
2.新建一个类,根据类型码的用途为它命名。这就是一个状态对象。
3.为这个新类添加子类,每个子类对应一种类型码。
à比起逐一添加,一次性加入所有必要的子类可能更简单些。
4.在超类中建立一个抽象的查询函数,用以返回类型码。在每个子类中覆写该函数,返回确切的类型码。
5.编译。
6.在源类中建立一个字段,用以保存新建的状态对象。
7.调整源类中负责查询类型码的函数,将查询动作转发给状态对象。
8.调整源类中为类型码设值的函数,将一个恰当的状态对象子类赋值给“保存状态对象”的那个字段。
9.编译,测试。
范例:
和上一项重构一样,我们仍然使用“雇员/薪资”例子。同样的,我们以Employee表示“雇员”:
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;
}
}
下面的代码展示使用这些类型码的条件表达式:
int payAmount(){
switch (_type){
case ENGINEER:
return _monthlySalary;
case SALESMAN:
return _monthlySalary + _commission;
case MANAGER:
return _monthlySalary + _bonus;
default:
return new RuntimeException("Incorrect Employee");
}
}
假设这是一家激情四溢、积极进取的公司,他们可以将表现出色的工程师擢升为经理。因此,对象的类型码是可变的,所以我们不能使用继承的方式来处理类型码。和以前一样,我们的第一步还是使用Self Encapsulate Field将表示类型码的字段自我封装起来:
Employee(int type){
setType(type);
}
int getType(){
return _type;
}
void setType(int arg){
_type = type;
}
int payAmount(){
switch (getType()){
case ENGINEER:
return _monthlySalary;
case SALESMAN:
return _monthlySalary + _commission;
case MANAGER:
return _monthlySalary + _bonus;
default:
return new RuntimeException("Incorrect Employee");
}
}
现在,我们需要声明一个状态类。我们把它声明为一个抽象类,并提供一个抽象函数,用以返回类型码:
abstract class EmployeeType{
abstract int getTypeCode();
}
现在可以开始创造子类了:
class Engineer extends EmployeeType{
int getTypeCode(){
return Employee.ENGINEER;
}
}
class Manager extends EmployeeType{
int getTypeCode(){
return Employee.MANAGER;
}
}
class Salesman extends EmployeeType{
int getTypeCode(){
return Employee.SALESMAN;
}
}
现在进行一次编译。前面所做的修改实在太平淡了。现在我们开始修改类型码访问函数,实实在在地把这些子类和Employ类联系起来:
class Employee...
private EmployeeType _type;
int getType(){
return _type.getTypeCode();
}
void setType(int arg){
switch(arg){
case ENGINEER:
_type = new Engineer();
break;
case SALESMAN:
_type = new Salesman();
break;
case MANAGER:
_type = new Manager();
break;
default:
throw new IllegalArgumentException("Incorrect Employee Code");
}
}
这意味着我们将在这里拥有一个switch语句。完成重构之后,这将是代码中唯一的switch语句,并且只在对象类型发生改变时才会执行。我们也可以运用Replace Constructor with Factory Method针对不同的case子句建立相应的工厂函数。还可以立刻再使用Replace Conditional with Polymorphism,从而将其他的case子句完全消除。
最后,可以将所有关于类型码和子类的知识都移到新类,并依此结束Replace Type Code with State/Strategy。首先,我们把类型码的定义复制到EmployeeType去,在其中建立一个工厂函数以生成适当的EmployeeType对象,并调整Employee中为类型码赋值的函数:
class Employee...
void setType(int arg){
_type = EmployeeType.newType(arg);
}
calss EmployeeType...
static EmployeeType newType(int code){
switch(code){
case ENGINEER:
return new Engineer();
case SALESMAN:
return new Salesman();
case MANAGER:
return new Manager();
default:
throw new IllegalArgumentException("Incorrect Employee Code");
}
}
static final int ENGINEER = 0;
static final int SALESMAN = 1;
static final int MANAGER = 2;
然后,删掉Employee中的类型码定义,代之以一个纸箱EmployeeType对象的引用:
class Employee...
int payAmount(){
switch (getType()){
case EmployeeType.ENGINEER:
return _monthlySalary;
case EmployeeType.SALESMAN:
return _monthlySalary + _commission;
case EmployeeType.MANAGER:
return _monthlySalary + _bonus;
default:
return new RuntimeException("Incorrect Employee");
}
}
现在,完事具备,可以运用Replace Conditional with Polymorphism来处理payAmount()函数了。
来源:oschina
链接:https://my.oschina.net/u/134516/blog/212410