重构—改善既有代码的设计007:在对象之间搬移特性(Moving Features Between Objects)
一:MOVE MOTHOD
二:MOVE FIELD
三:EXTRACT CLASS
四:INLINE CLASS
五:HIDE DELEGATE
六:REMOVE MIDDLE MAN
七:INTRODUCE FOREIGN METHOD
八:INTRODUCE LOCAL METHOD
一:MOVE MOTHOD
你的程序中,有个函数一起所驻类之外的另一个类进行更多的交流:即调用后者或被后者调用
1:动机
MOVE METHOD作为重构理论的支柱,如果一个类有太多的行为,或如果一个类与另一个类有太多合作而形成高度耦合,就可用此方法。
利用此方法可以是系统中的类更简单,这些类最终也将更干净利落的实现系统交付的任务。
2:做法
.检查源类定义的源函数所有用的一切特性,考虑他们是否也该被搬移。
.检查源类的子类和超类,看看是否有函数的其他声明
.在目标类中声明该函数
.将源方法的代码拷贝到目标方法中,调整后者,时期能在新家中正常运行
.决定如何从源正确引用目标对象
.修改源方法,使之成为一个纯委托方法
3:范例
class AccountType
{
}
class Account
{
private AccountType _type;
private int _daysOverdrawn;
double overdraftCharge( )
{
if (!_type.isPreminm())
{
double result = -10;
if (_daysOverdrawn > 7)
result += (_daysOverdrawn - 7) * 0.85;
return result;
}
else
return _daysOverdrawn * 1.75;
}
double bankCharge()
{
double result = 4.5;
if (_daysOverdrawn > 0)
result += overdraftCharge();
return result;
}
}
改为:
class AccountType
{
double overdraftCharge(int daysOverdrawn)
{
if (!isPreminm())
{
double result = -10;
if (daysOverdrawn > 7)
result += (daysOverdrawn - 7) * 0.85;
return result;
}
else
return daysOverdrawn * 1.75;
}
}
class Account
{
private AccountType _type;
private int _daysOverdrawn;
double overdraftCharge( )
{
return _type.overdraftCharge(_daysOverdrawn);
}
double bankCharge()
{
double result = 4.5;
if (_daysOverdrawn > 0)
result += overdraftCharge();
return result;
}
}
如果删除源函数的话,则可改为
class AccountType
{
double overdraftCharge(int daysOverdrawn)
{
if (!isPreminm())
{
double result = -10;
if (daysOverdrawn > 7)
result += (daysOverdrawn - 7) * 0.85;
return result;
}
else
return daysOverdrawn * 1.75;
}
}
class Account
{
private AccountType _type;
private int _daysOverdrawn;
double bankCharge()
{
double result = 4.5;
if (_daysOverdrawn > 0)
result += _type.overdraftCharge();
return result;
}
}
当参数多的时候,可以考虑传入对象
class AccountType
{
double overdraftCharge(Account account)
{
if (!isPreminm())
{
double result = -10;
if (account.getDaysOverdrawn() > 7)
result += (account.getDaysOverdrawn() - 7) * 0.85;
return result;
}
else
return account.getDaysOverdrawn() * 1.75;
}
}
class Account
{
private AccountType _type;
private int _daysOverdrawn;
double bankCharge()
{
double result = 4.5;
if (_daysOverdrawn > 0)
result += _type.overdraftCharge();
return result;
}
}
二:MOVE FIELD
1:你的程序中,某个值域被其所驻类之外的另一个类更多的用到
在目标类建立一个新的值域,修改源域的所有用户,令他们改用新的值域
2:动机
在类之间移动状态和行为,是重构过程中必不可少的措施。
随着系统发展,你会发现自己需要新的类,并需要将原本工作责任拖到新的类中。
3:做法
.如果值域的属性是PUBLIC,首先使用ENCAPSULATE FIELD将它封装起来
.编译,测试
.在目标类中建立和源值域相同的值域,并同时建立相应的设值/取值函数
.编译目标类
.决定如何在源对象中引用目标对象
.删除源值域
.将所有对源值域的引用替换为对目标适当函数的调用
.编译,测试
4:范例
class AccountType
{
}
class Account
{
private AccountType _type;
private int _interestRate;
double interestForAccount_days(double amount, int days)
{
return _interestRate * amount * days / 365;
}
}
改为:
class AccountType
{
private double _interestRate;
void setInterestRate(double arg)
{
_interestRate = arg;
}
double getInterestRate()
{
return _interestRate;
}
}
class Account
{
private AccountType _type;
double interestForAccount_days(double amount, int days)
{
return _type.getInterestRate() * amount * days / 365;
}
}
三:EXTRACT CLASS
1:某个类做了应该由两个类做的事
建立一个新类,将相关的值域和函数从旧的类搬移到新的类中
2:动机
一个类应该是一个清楚地抽象,处理一些明确的责任。
有些类往往含有大量函数和数据,让人不太容易理解。
3:做法
.决定如何分解类所负责任
.建立一个新类,用以表现从旧类中分离出来的责任
.建立从旧类访问新类的连接关系
.对于你想移动的每一个值域,用MOVE FIELD移动
.每次搬移后,编译,测试
.使用MOVE METHOD将必要的函数搬移到新类中,先搬移较底层函数,即被其他函数调用多于调用其他函数的,再搬移较高层函数
.每次搬移之后,编译,测试
.检查,精简每个类接口
.决定是否让新类曝光。
4:范例
class Person
{
private string _name;
private string _officeAreaCode;
private string _officeNumber;
public string getName()
{
return _name;
}
public string getTelephoneNumber
{
return _officeAreaCode + _officeNumber;
}
string getOfficeAreaCode()
{
return _officeAreaCode;
}
string setOfficeAreaCode(string arg)
{
_officeAreaCode = arg;
}
string getOfficeNumber()
{
return _officeNumber;
}
string setOfficeNumber(string arg)
{
_officeNumber = arg;
}
}
改为:
class TelephoneNumber
{
private string _areaCode;
private string _number;
public string getTelephoneNumber()
{
return _areaCode + _number;
}
string getOfficeAreaCode()
{
return _areaCode;
}
string setOfficeAreaCode(string arg)
{
_areaCode = arg;
}
string getOfficeNumber()
{
return _number;
}
string setOfficeNumber(string arg)
{
_number = arg;
}
}
class Person
{
private string _name;
private TelephoneNumber _officeTelephone = new TelephoneNumber();
public string getName()
{
return _name;
}
public string getTelephoneNumber()
{
return _officeTelephone.getTelephoneNumber();
}
TelephoneNumber getOfficeTelephone()
{
return _officeTelephone;
}
}
四:INLINE CLASS
1:某个类没有做太多的事情,即没有承担足够的责任
可以将类的所有特性搬移到另一个类中,然后移除原来类
2:动机
和EXTRACT CLASS相反。
五:HIDE DELEGATE
1:可以直接调用其服务对象的代理类
在服务端某个类建立客户所需的所有函数,用以隐藏委托关系
2:动机
封装意味着每个对象都应该尽可能少了解系统的其他部分,如此一来,一旦发生变化,需要了解这一变化的对剑就会比较少。
3:做法
.对于每一个委托关系中的函数,在服务端建立一个简单的委托函数
.调整客户,令它只调用服务提供的函数
.每次调整后,编译并测试
.如果将来不再有任何客户需要取用委托类,便可移除服务中的相关访问函数
.编译,测试
4:范例
class Department
{
private string _chargeCode;
private Persion _manager;
public Department(Person manager)
{
_manager = manager;
}
public Persion getManager()
{
return _manager;
}
}
class Person
{
Department _department;
public Department getDepartment()
{
return _department;
}
public void setDepartment(Department arg)
{
_department = arg;
}
}
改为:
class Department
{
private string _chargeCode;
private Persion _manager;
public Department(Person manager)
{
_manager = manager;
}
public Persion getManager()
{
return _manager;
}
}
class Person
{
Department _department;
public Department getDepartment()
{
return _department;
}
public void setDepartment(Department arg)
{
_department = arg;
}
public Person getManager()
{
return _department.getManager();
}
}
六:REMOVE MIDDLE MAN
1:某个类做了过多简单的委托动作
让客户直接调用受托类
2:动机
3:做法
.建立一个函数,用以取用受托对象
.对于每个受托函数,在服务中删除该函数,并将客户对该函数的调用替换为受托对象的调用
.处理每个受托函数后,编译,测试
4:范例
七:INTRODUCE FOREIGN METHOD
DateTime newStart = new DateTime(previousEnd.getYear(), previousEnd.getMonth(), previousEnd.getDay() - 1);
DateTime newStart = nextDay(previousEnd);
private static DateTime nextDay(DateTime arg)
{
new DateTime(arg.getYear(), arg.getMonth(), arg.getDay() - 1);
}
八:INTRODUCE LOCAL METHOD
建立一个新类,使它包含这些额外函数,让这个扩展品成为源类的子类或外覆类