前言:关于缩减参数列的重构手法,DougLea对我提出了一个警告:并发编程往往需要使用较长的参数列,因为这样你可以保证传递给函数的参数都是不可被修改的,例如内置型对象和值对象一定是不可变的。通常,你可以使用不可变对象取代这样的长参数列,但另一方面你也必须对此类重构保持谨慎。
多年来,我一直坚守一个很有价值的习惯:明确地将“修改对象状态”的函数(修改函数)和“查询对象状态”的函数(查询函数)分开设计。
1、RenameMethod(函数改名)
动机:函数的名称未能揭示函数的用途。
2、AddParameter(添加参数)
动机:某个函数需要从调用端得到更多信息。
3、RemoveParameter(移除参数)
动机:函数本体不再需要某个参数
4、SeparateQueryfromModifier(将查询函数和修改函数分离)
动机:某个函数既返回对象状态值,有修改对象状态。
做法:建立两个不同的函数,其中一个负责查询,另一个负责修改。
5、ParameterizeMethod(令函数携带参数)
动机:若干函数做了类似的工作,但在函数本体中却包含了不同的值。
做法:建立单一函数,以参数表达那些不同的值。
代码1:
voidtenPercentRaise(){
salary*=1.1;
}
voidfivePercentRaise(){
salary*=1.05;
}
代码2:上述代码可以替换如下
voidraise(doublefactor){
salary*=(1+factor);
}
6、ReplaceParameterwithExplicitMethods(以明确函数取代参数)
代码1:
staticfinalintENGINEER=0;
staticfinalintSALESMAN=1;
staticfinalintMANAGER=2;
staticEmployeecreate(inttype){
switch(type){
caseENGINEER:
returnnewEngineer();
}
caseENGINEER:
returnnewSalesman();
}
caseENGINEER:
returnnewManager();
}
default:
thrownewIllegalArgumentException("Incorrecttypecodevalue");
}
代码2:
staticEmployeecreateEngineer(){
returnnewEngineer();
}
staticEmployeecreateSalesman(){
returnnewSalesman();
}
staticEmployeecreateManager(){
returnnewManager();
}
7、PreserveWholeObject(保持对象完整)
动机:你从某个对象中取出若干值,将它们作为某一次函数调用时的参数。
做法:改为传递整个对象。
代码1:intlow=daysTempRange().getLow();
inthigh=daysTempRange().getHigh();
withinPlan=plan.withinRange(low,high);
代码2:withinPlan=plan.withinRange(daysTempRange());
8、ReplaceParameterWithMethods(以函数取代参数)
动机:对象调用某个函数,并将所得结果作为参数,传递给另一个函数。而接受该参数的函数本身也能够调用前一个函数。
做法:让参数接受者去除该项参数,并直接调用前一个函数。
代码1:
intbasePrice=_quantity*_itemPrice;
discountLevel=getDiscountLevel();
doublefinalPrice=discountedPrice(basePrice,discountLevel);
代码2:
intbasePrice=_quantity*_itemPrice;
doublefinalPrice=discountedPrice(basePrice);
9、IntroduceParameterObject(引入参数对象)
某些参数总是很自然地同时出现,这时可以以一个对象取代这些参数。价值在于缩短参数列。
做法:新建一个类,用以表现你想替换的一组参数。将这个类设为不可变的。
比如:
privatefinalDate_start;
privatefinalDate_end;
DateRange(Datestart,Dateend){
_start=start;
_end=end;
}
DategetStart(){
return_start;
}
DategetEnd(){
return_end;
}
10、RemoveSettingMethod(移除设值函数)
动机:类中的某个字段应该在对象创建时被设值,然后就不可再改变。
做法:去掉该字段的所有设值函数。
11、HideMethod(隐藏函数)
动机:有一个函数,从来没有被其他任何类用到。应该将这个函数修改为private。
12、ReplaceConstructorwithFactoryMethod(以工厂函数取代构造函数)
动机:你希望在创建对象时不仅仅是做简单的构建动作。
范式1:根据整数(实际是类型码)创建对象
classEmployee{
privateint_type;
staticfinalintENGINEER=0;
staticfinalintSALESMAN=1;
staticfinalintMANAGER=2;
Employee(inttype){
_type=type;
}
staticcreate(inttype){
returnnewEmployee(type);
}
staticEmployeecreate(inttype){
switch(type){
caseENGINEER:
returnnewEngineer();
caseSALESMAN:
returnnewSalesman();
caseMANAGER:
returnnewManager();
default:
thrownewIllegalArgumentException("Incorrecttypecodevalue");
}
}
}
调用:Employeeeng=Employee.create(Employee.ENGINEER);
范式2:根据字符串创建子类对象
staticEmployeecreate(Stringname){
try{
return(Employee)Class.forName(name).newInstance();
}catch(Exceptione){
thrownewIllegalArgumentException("Unabletoinstantiate"+name);
}
}
调用:Employee.create(“Engineer”);
范式3:以明确函数创建子类
classPerson...
staticPersoncreateMale(){
returnnewMale();
}
staticPersoncreateFemale(){
returnnewFemale();
}
调用:Personkent=Person.createMale();
13、EncapsulateDowncase(封装向下转型)
动机:某个函数返回的对象,需要调用者执行向下转型
做法:将向下转型动作转移到函数中。
ObjectlastReading(){
returnreadings.lastElement();
}
ReadinglastReading(){
return(Reading)readings.lastElement();
}
14、ReplaceErrorCodewithException(以异常取代错误码)
动机:某个函数返回一个特定的代码,用以表示某种错误情况。
代码1:intwithdraw(intamount){
if(amount>_balance){
return-1;
}
else{
_balance-=amount;
return0;
}
}
代码2:voidwithdraw(intamount)throwsBalanceException{
if(amount>_balance)thrownewBalanceException();
_balance-=amount;
}
15、ReplaceExceptionwithTest(以测试取代异常)
代码1:
doublegetValueForPeriod(intperiodNumber){
try{
return_values[periodNumber];
}catch(ArrayIndexOutofBoundsExceptione){
return0;
}
}
代码2:
doublegetValueForPeriod(intperiodNumber){
if(periodNumber>=_values.length)return0;
return_values[periodNumber];
}
代码3:
ResourcegetResource(){
try{
result=(Resource)_available.pop();
_allocated.push(result);
returnresult;
}catch(EmptyStackExceptione){
result=newResource();
_allocated.push(result);
returnresult;
}
}
代码4:
ResourcegetResource(){
Resourceresult;
if(_available.isEmpty()){
result=newResource();
_allocated.push(reuslt);
returnresult;
}
else{
result=(Resource)_available.pop();
_allocated.push(result);
returnresult;
}
}