重构—改善既有代码的设计100:简化函数调用(Making Method Calls Simpler)
一:重新命名函数(Rename Method)
二:添加参数(Add Parameter)
三:移除参数(Remove Parameter)
四:将查询函数和修改函数分离(Seperate Query from Modifier)
五:令函数携带参数(Parameterize Method)
六:以明确函数取代参数(Replace Parameter with Explicit Methods)
七:保持对象完整(Preserve Whole Object)
八:以参数取代参数(Replace Parameter with Method)
九:引入参数对象(Introduce Parameter Object)
十:移除设置函数(Remove Setting Method)
十一:隐藏某个函数(Hide Method)
十二:以工厂函数取代构造函数(Replace Constructor with Factory Method)
十三:封装向下转型动作(Encapsulate Downcast)
十四:以异常取代错误码(Replace Error Code with Exception)
十五:以测试取代异常(Replace Exception with Test)
一:重新命名函数(Rename Method)
1:如果函数的名字未能揭示函数的用途:修改函数的名字
2:将复杂的处理过程分解成小函数,并给函数取一个好名称,利用函数名称准确表达它的用途
代码首先是写给人读的,其次才是为机器写的。
二:添加参数(Add Parameter)
1:某个函数需要从调用端得到更多的信息
为此函数添加一个对象参数,让该对象带进函数所需信息。
注:过长的参数列是不好的味道,很难记住那么多参数,而且长参数列往往会伴随着坏味道。
三:移除参数(Remove Parameter)
1:将函数体中不再需要的某个参数去除
2:动机
程序员可能经常添加参数,却往往不愿意去掉它们,他们打的如意算盘是无论如何多余的参数都不会引起任何问题,而且以后还可能用上它。
四:将查询函数和修改函数分离(Seperate Query from Modifier)
1:当某个函数既返回对象状态值,又修改对象状态
建立两个不同的函数,其中一个负责查询,另一个负责修改
2:动机
如何有返回值的函数,都不应该有看得到的副作用。
五:令函数携带参数(Parameterize Method)
1:若干函数做了类似的工作,但在函数本体中却包含了不同的值
建立单一函数,以参数表达那些不同的值
fivePercentRaise();
tenPercentRaise();
修改为:
raise(percentage);
2:
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 = usageInRarge(0,100)*0.03;
result += usageInRarge(100,200)*0.05;
result += usageInRarge(100,Int32.MaxValue)*0.07;
return new Dollars(result);
}
protected double usageInRarge(int start, int end)
{
if (lastUsage() > start)
{
return Math.Min(lastUsage(),end)-start;
}
else
return 0;
}
六:以明确函数取代参数(Replace Parameter with Explicit Methods)
1:如果有一个函数,其内完全取决于参数值而采取不同反应
针对参数的每一个可能值,建立一个独立函数
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;
}
七:保持对象完整(Preserve Whole Object)
1:从某个对象中取出若干值,将它们作为某一次函数调用的参数:改使用传递整个对象
int low = daysTempRange().getLow();
int high = daysTempRange().getHigh();
withinPlan = plan.withinRange(low, high);
改为:
withinPlan = plan.withinRange(daysTempRange());
2:动机
有时候,会将来自同一对象的若干项数据作为参数,传递给某个函数。
这样做的问题在于万一将来被调用的函数需要新的数据项,你就必须查找并修改次函数的左右调用。
如果吧这些数据所属的整个对象传给函数,就可以避免这种尴尬的处境。
注意:如果所传参数之间没有依存关系,就不能再传递对象了,否则会使依存结构恶化
八:以参数取代参数(Replace Parameter with Method)
1:对象调用某个函数,并将所得结果作为参数,传递给另一个函数。而接受该参数的函数也可以调用前一个函数。
让参数接受者去除该项参数,并直接调用前一个函数
int basePrice = _quantity * _itemPrice;
discountLevel = getDiscountLevel();
double finalPrice = discountedPrice(basePrice, discountLevel);
改为:
int basePrice = _quantity * _itemPrice;
double finalPrice = discountedPrice(basePrice);
2:可以通过其他途径而非参数列表获得参数值,就不应该通过参数取得该值。通常长的参数列表会增加程序的理解难度。
九:引入参数对象(Introduce Parameter Object)
1:某些参数总是很自然的同时出现,以一个对象取代这些参数
amountInvoicedIn(start:Date, end:Date);
amountReceivedIn(start:Date, end:Date);
amountOverdueIn(start:Date, end:Date);
改为:
amountInvoicedIn(DateRange);
amountReceivedIn(DateRange);
amountOverdueIn(DateRange);
2:动机
常常会看到特定的一组参数总是一起被传递,且可能好几个函数都使用这一组参数。
十:移除设置函数(Remove Setting Method)
1:如果类中某个值域在对象初始时被设值,然后就不再改变:去掉该值域的所有设值函数
如果你为某个值提供了设值函数,这就暗示这个值值域可以被改变。
如果你不希望在对象初创之后此值域还有机会被改变,那就不要为它提供设值函数。
十一:隐藏某个函数(Hide Method)
1:有个函数,从来没有被其他任何类用到,将这个函数修改为PRIVATE
十二:以工厂函数取代构造函数(Replace Constructor with Factory Method)
1:希望在创建对象时不仅仅是对它做简单的构建动作,将CONSTRUCTOR替换为FACTORY METHOD
Employee(int type)
{
_type = type;
}
改为:
static Employee create(int type)
{
return new Employee(type);
}
十三:封装向下转型动作(Encapsulate Downcast)
1:如果某个函数返回的对象,需要由函数调用者执行向下转型动作:将向下转型动作移到函数中
Object lastReading()
{
return readings.lastElement();
}
改为:
Reading lastReading()
{
return (Reading)readings.lastElement();
}
十四:以异常取代错误码(Replace Error Code with Exception)
1:某个函数返回一个特定的代码,用以表示某种错误情况:改用异常
2:可以清楚的将普通程序和错误处理分开,使得程序更容易理解
这条需要比较认真的方式来处理
十五:以测试取代异常(Replace Exception with Test)
1:面对一个调用者可预先加以检查的条件,你就抛出一个异常,修改调用者,使它在调用函数之前先做检查。
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];
}
2:避免异常被滥用
异常只应该用于异常的,罕见的行为,就是那些长生意料外的错误的行为,而不应该成为条件检查的替代品。