《重构》读书笔记(一)

类别:软件工程 点击:0 评论:0 推荐:
前几天,从同事那里借来Matin Fowler的《重构》一书,看得我很是过瘾。尽管看后印象颇深,但要作到过目不忘是不可能的,故将书中自己认为重要的东西记录下来。俗话说的好:好记性不如烂笔头。

注:很多直观的重构方法没有做解释,从其名字就可以猜出其用法。


重新组织你的函数
==========================================================

1.Extract Method
2.Inline Method
3.Inline Temp
4.Replace Temp with Query

临时变量的问题在于:它们是暂时的,只能在函数内部使用,这驱使你写出更长的函数,
因为只有这样你才能访问到你要访问的临时变量。如果采用该方法,那么同一个Class中
的所有函数都可以获得这份信息。这将给你极大的帮助,使你能够为这个Class编写更清
晰的代码。
将只被赋值一次的临时变量的赋值操作提炼到独立函数中,然后在该临时变量上实施
Inline Temp.

例:
// 原函数
double getPrice(){
int basePrice = _quatity * _itemPrice;
double discountFactor;
if (basePrice > 1000) discountFactor = 0.95;
else discountFactor = 0.98;
return basePrice * discountFactor;
}

// 替换basePrice
double getPrice(){
double dicountFactor;
if (basePrice() > 1000) discountFactor = 0.95;
else discountFactor = 0.98;
return basePrice() * discountFactor;
}

int basePrice(){
return _quatity * _itemPrice;
}

// 替换discountFactor
double getPrice(){
return basePrice() * discountFactor();
}

int basePrice(){
return _quatity * _itemPrice;
}

double discountFactor(){
if (basePrice() > 1000) return 0.95; // 从上次替换中获得好处
else return 0.98;
}


5.Introduce Explaining Variable
6.Split Temperary Variable
7.Remove Assignments to Parameters
不要对参数赋值
8.Replace Method with Method Object
将这个函数放进一个单独对象中,如此一来局部变量就成了对象内的值域(field)。
然后你可以在同一个对象中将这个大型函数分解为小型函数。
9.Substitue Algorithm
替换你的算法


在对象之间搬移特性
==========================================================

1.Move Method
在你的程序中,有个函数与其所在class之外的另一个class进行更多交流:调用后者,
或被后者调用。
减小类之间的耦合。

步骤:
考虑source class定义的source method所使用的一切特性是否也应该搬移。
如果source class的subclass和superclass有其声明,或许无法搬移,除非
target class也同样表现出多态性。
在target class中新建函数,然后将source method的代码复制到target method
决定如何从source正确引用target object。
修改source method,使之成为一个delegating method。
决定是否删除source method,看是否方便。
如果删除,则将对source method的调用替换为对targetobject.targetmethod的
调用。

2.Move Field
3.Extract Class
4.Inline Class
5.Hide Delegate
将调用Server.delegate.someMethod()变为Server.someMethod()。
去除调用方对Server.delegate的依赖。
6.Remove Middle Man
7.Introduce Foreign Method
时机:你所使用的server class需要一个额外函数,但你无法修改这个class。
如果你发现自己已经为server class建立了大量的外加函数,或有许多class都需要
同样的外加函数,你就不应该使用本方法,而应该使用Introduce Local Extesion。
步骤:
在client class中建立一个函数,用来提供你的功能。该函数不应该去用client class
的任何特性,如果需要一个值,就将其当作参数传给它。
以server class实体为该函数的第一个参数。
将该函数注释为:外加函数,应在server class实现。

8.Introduce Local Extension
时机:见7.Introduce Foreign Method
方法:建立新类,其中包括那些额外函数,使该类成为source class的subclass或wrapper。



重新组织数据
==========================================================

1.Self Encapsulate Field
封装的好处:subclass可以通过覆写读取、写入函数来改变数据读取、写入的逻辑方式
不封装的好处:简单
2.Replace Data Value with Object
比如说:一开始你会用字符串来表示『电话号码』这个概念,但随后你会发现,电话号码需要『格式化』、
『抽取区号』之类的特殊行为。如果这样的数据项只有一二个,你还可以把相关函数放进数据项所属的对象
里头;但是Duplicate Code臭味和Feature Envy臭味很快就会从代码中散发出来。当这些臭味开始出现,
你就应该将数据值变成对象。
3.Change Value to Reference
将这个value object(实值对象)变成一个Reference object(引用对象)
4.Change Reference to Value
5.Replace Array with Object
6.Duplicate Observed Data
重复的意思:界面类中有的数据,逻辑类也有相同的数据。
其中运用了Observer模式来同步界面类和逻辑类的数据。
可以用于界面与逻辑分开。

需要进一步研究
7.Change Unidirectional Association to Bidirectional
8.Change Bidirectional Association to Unidirectional
9.Replace Magic Number with Symbolic Constant
10.Encapsulate Field
11.Encapsulate Collection
12.Replace Record with DataClass
13.Replace Type Code with Class
枚举型数据的替代方案

class BloodGroup{ // 血型
public static final BloodGroup O = new BloodGroup(0);
public static final BloodGroup A = new BloodGroup(1);
public static final BloodGroup B = new BloodGroup(2);
public static final BloodGroup AB = new BloodGroup(3);

private final int _code;
private BloodGroup(int code){
_code = code;
}

}

14.Replace Type Code with Subclasses
你有一个不变的type code,它会影响class的行为。
类似于状态处理机模式

15.Replace Type Code with State/Strategy
你有一个type code,它会影响class的行为,但你无法使用subclassing.
比如:type code 在对象生命周期中发生变化。
state object对应的Field可以替换成其他state object
16.Replace Subclass with Fields


简化条件表达式
==========================================================

1.Decompose Conditional(分解条件式)
将其分解成多个独立函数
2.Consolidate Conditional Expression(合并条件式)
合并不必单独存在的条件式
3.Consolidate Duplicate Conditional Fragments(合并重复的条件片断)
在条件式的每个分支上有着相同的一段代码。进行该重构,可以更好的表明那些东西
随条件的变化而变化。
4.Remove Control Flag
循环语句中,最好用Break或Exit取代控制标记(用来判断是否继续循环)
5.Replace Nested Conditional with Guard Clauses
以卫语句取代嵌套条件式
函数中的条件式使人难以看清正常的执行路径。
给某一条分支以特别的重视。
条件式通常有两种形式:一是所有分支都是正常行为;二是只有一种是正常行为,其他
都是不常见的情况。
卫语句最好返回一个明确值,以便看得更清楚。

// 原代码
double getPayAmount(){
double result;
if (_isDead) result = deadAmout();
else {
if (_isSeparated) result = separatedAmount();
else {
if (_isRetired) result = retiredAmount();
else result = normalPayAmount();
}
}
}

// 新代码
double getPayAmount(){
if (_isDead) return deadAmount();
if (_isSeparated) return separatedAmount();
if (_isRetired) return retiredAmount();
return normalPayAmount();
}

6.Replace Conditional with Polymorphism
以多态取代条件式
多态最根本的好处是:如果你需要根据对象的不同型别而采取不同的行为,多态使你不必编写
明显的条件式。
7.Introduce Null Object
将null value替换为null object

比如想显示一个Persion对象信息,它大约有20个instance变量。如果这些变量可被设为null,
那么打印一个Person对象的工作将非常复杂。如果将null value换成null object(他们都知道如
何正常地显示自己),Person类就可以减少很多代码。

比Null Object模式更大的模式:Specail Case模式。

步骤:
1.为source class建立一个subclass,其行为像source class的null版本。在source class
和subclass中都加上isNull()函数,前者返回false, 后者返回true。
2.找出所有取得source object却取得null的地方。修改它,使其获得null object。
3.找出所有『将source object与null比较』的地方,使他们调用isNull()函数。
4.找出这样的程序点:如果对象不是null, 做A动作,否则做B动作。
5.对于每一个这样的地方,在null class中覆写A动作,使其行为和B动作相同。
6.删除isNull()的条件判断。

8.Introduce Assertion
以断言明确表现这种假设。

本文地址:http://com.8s8s.com/it/it32805.htm