重构方法
重新组织函数
提炼函数 Extract Method
function printOwing($amount){
printBanner();
// print details
echo NAME;
echo $amount;
}
/** Refactor **/
function printOwing($amount){
printBanner();
printDetails($amount);
}
function printDetails($amount){
echo NAME;
echo $amount;
}
动机: 当遇到过长的函数或者一段需要注释才能让人理解的代码时使用。
当遇到局部变量时,要小心!
当发现源函数的参数被赋值时,应该使用Remove Assignments to Parameters
方法。
内联函数-Inline Method
function getRating(){
return moreThanFiveLateDeliveries() ? 2 : 1;
}
function moreThanFiveLateDeliveries(){
return NUMBER_OF_LATE_DELIVERIES > 5;
}
/** R **/
int getRating(){
return NUMBER_OF_LATE_DELIVERIES > 5 ? 2 : 1;
}
注意:重构函数不应具有多态性
内联临时变量-Inline Temp
$basePrice = AnOrder->basePrice();
return $basePrice > 1000;
/** R **/
return AnOrder->basePrice() > 1000;
动机:Inline Temp多半是作为
Replace Temp with Query
的一部分使用的。
查询渠道临时变量-Replace Temp with Query
$basePrice = $quantity * $itemPrice;
if($basePrice > 1000){
return $basePrice * 0.95;
}else{
return $basePrice * 0.98;
}
/** R **/
function basePrice(){
return $quantity * $itemPrice;
}
if(basePrice() > 1000){
return basePrice() * 0.95;
}else{
return basePrice() * 0.98;
}
动机:将临时变量作为查询,同一个类中的所有函数均可以访问
注意:要确定下来final
型的变量,为了更好的编写后续程序。
引入解释性变量-Introduce Explaining Variable
if((Platform->toUpperCase()->indexOf("MAC") > -1)
&& (Browser->toUpperCase()->indexOf("IE") > -1)
&& wasIntialized() && $resize > 0)
{
// Do sth
}
/** R **/
final boolean isMacOs = Platform->toUpperCase()->indexOf("MAC") > -1;
final boolean isIEBrowser = Browser->toUpperCase()->indexOf("IE") > -1;
final boolean wasResized = resize > 0;
if(isMacOs && isIEBrowser && wasIntialized() && wasResized){
// Do sth
}
动机:消除复杂表达式,用临时变量解释、归总每一步的意义
分解临时变量-Split Temporary Variable
拒绝临时变量的多次赋值,导致不同位置而具有不同意义。
移除对参数的赋值-Remove Assignments to Parameters
function discount($inputVal, $quantity, $yearToDate){
if($inputVal > 50){
$inputVal -= 2;
}
}
/** R **/
function discount($inputVal, $quantity, $yearToDate){
$result = $inputVal;
if($inputVal > 50){
$result -= 2;
}
}
拒绝对参数赋值,保持对参数入出不变。
以函数对象取代类中函数-Replace Method with Method Object
如果一个函数过大(问题函数),考虑新建一个类来代替这个问题函数。
在对象之间搬移特性-决定责任归属
搬移函数-Move Method
如果一个类与另一个类有太多合作而形成高度耦合,则需要搬移函数,构建新类。
搬移字段-Move Field
同理上面的方法。
提炼类-Extract Class
如果某个类做了两个类做的事情,考虑新建一个类来处理。
将类内联化-Inline Class
如果某个类没有做太多事情,可以将这个类的特性搬移到另一个类中,移除原类。
隐藏委托关系-Hide Delegate
客户通过一个委托类来调用另一个对象。在服务类上建立客户所需的所有函数,用以隐藏委托关系。
移除中间人-Remove Middle Man
某个类做了过多的简单委托动作。这个时候可以考虑让客户直接调用受托类。
引入外加函数-Introduce Foreign Method
你需要位提供服务的类增加一个函数,但你无法修改这个类时。
可以在客户类中建立一个函数,并以第一参数形式传入一个服务类实例。
$newStart = new Date(PreviousEnd->getYear(), PreviousEnd->getMonth(), PreviousEnd->getDate() + 1);
/** R **/
$newStart = nextDay(PreviousEnd);
function nextDay($date){
return new Date($date->getYear(), $date->getMonth(), $date->getDate() + 1);
}
引入本地扩展-Introduce Local Extension
你需要为服务类提供一些额外函数,但你无法修改这个类。
那么就新建一个类,使它包含这些额外函数。让这个扩展类称为源类的子类或者包装类。
本地扩展的两种方式:
就是通过
继承
-构建子类来作出对源类的扩展。通过构建包装类:将源类通过构造函数传入包装类,包装类对源类的每一个函数都进行再处理,通过包装类调用源类。
重新组织数据
通过对象来表示数据类型,即Replace Value with Object
,即通过函数访问每一个数据。
把数组变成对象,可以更好的展露数组的数据结构。
自封装字段-Self Encapsulate Field
private $_low, $_high;
function includes($arg){
return $arg >= self::$_low && $arg <= self::$_high;
}
/** R **/
private $_low, $_high;
function includes($arg){
return $arg >= getLow() && $arg <= getHigh();
}
function getLow(){
return self::$_low;
}
function getHigh(){
return self::$_high;
}
以对象取代数据值-Replace Data Value with Object
动机:开发初期,往往决定以简单的数据表示简单的情况。但是随着开发深入,发现简单数据项也需要一些其它的概念或特殊行为。此时就可以把数据值抽象出来,变成一个可操作的对象。
将值对象改为引用对象-Change Value to Reference
当你从一个类衍生出许多彼此相等的实例时,希望将他们替换为同一个对象进行操作
那么就将这个值对象变成引用对象。这样来保证所有操作的实物是同一个实物(往往是真实世界中同一个物体)。
将引用对象改为值对象-Change Reference to Value
在分布式系统和并发系统中,不可变的值对象特别有用。因为你无需考虑他们的同步问题。
值对象本身应有一个特性:他们是不可变的。如果保证了这一点,就可以以多个对象表示同一个事物。如果是可变的,则考虑使用引用对象。
以对象取代数组-Replace Array with Object
复制‘被监视数据’-Duplicate Observed Data
view层数据和controller逻辑数据是需要拆分出来的。如果两层系统使用了同样的数据,那么view层可以通过观察者模式(Observer/Observable模式)
来获得领域类的数据。
也可以使用事件监听器
来取代观察者模型,每当领域对象发生变化,就向监听器发送一个事件。view层系统在适当时候调用update。
双向关联 & 单向关联
使用字面常量
封装字段
如果类中存在public字段,则将它声明为private,并提供相应的访问函数。
以类和子类取代类型码(Type Code)
参考
重构-改善既有代码的设计