在面向对象的编程中,把责任放在哪几乎是最重要的事情之一。在我写代码的时候,唯有当写完了,才会发现初期思考的设计在责任分配上还是有问题。相信没有多少人就能保证不出错,这时候我们就需要重构,将责任和特性进行搬移。这一部分作者就主要讲了于此相关的重构手法。
1.Move Method(搬移函数)
原文解释的非常好,清晰易懂:
例子,有一个账户类。
class Account
double overdraftCharge()
{
if(_type.isPremium())
{
double result=10;
if(_daysOverdrawn>7) result+=(_daysOverdrawn-7)*0.85;
return result;
}
else
{
return _dayOverdrawn*1.75;
}
}
假设每一种账户都有自己的“透支金额计费规则”,这时候上面这个方法则应该放到AccountType类里去。那首先得观察每一个特性和字段是否要跟着方法一起移动。在这个例子里,_daysOverdrawn会随着不同类型的账户而变化,那么它则应该是账户的特性,所以这里应该保留这个字段。
移到另外一个类的时候假如有字段需要保留在旧类,那么则需要注意,如果是一个,则可以当作参数传递,否则需要在两者之间建立一种引用关系。
2.Move Field(搬移字段)
当一个类的某个字段被另一个类更多的用到,则需要把这个字段移到那个类去。当字段搬移以后,旧类中数个对其的引用则会报错,那么旧类则需要有这个字段的引用,这里可以使用C#的属性机制也可以写类似get set的方法。
另外,当我们搬移以后一个个去修改可能会不太方便,这时候可以使用SelfEncapsulateField(自我封装,应该是作者在后文会说到)。什么意思呢,就是可以先把要转移的字段进行一层封装变成属性或者方法统一引用的指向,转移之后就只需要更改属性或者方法中的实际关联操作了。
3.Extract Class(提炼类)
使用情景:当一个类做了应该由两个类做的事,那么则需要把相关的字段函数提炼到新的类去。
有时候我们会因为需求的增加而不断扩张类。但是某个地方你觉得又不值得为它分离出一个单独的类,那么慢慢地这个类就会变得非常乱。而这种情况下,我们就要根据自己的分析来判断哪些是需要提炼出去的。
这个就不用举例了,只要配合上面的Move Method和Move Field来将相关特性也一并搬移出去。
另外搬移后的类究竟要设定一个什么样的公开性是值得考虑的,比如C#中我们可以将新类作为旧类的内部类,或者私有的并且只被旧类访问。具体还是看情况而定。
4.Inline Class(将类内联化)
当一个类不再承担足够的责任,可以选择将其内联到使用它最多的用户类去。
5.Hide Delegate(隐藏委托关系)
这种方法的定义是 在服务类上建立客户所需的所有函数,用以隐藏委托关系。单看定义很难理解,甚至可能会想到C#中的委托,现在来看个书中的例子:
class Person
{
Department _department;
public Department getDepartment()
{
return _department;
}
public void SetDepartment(Department arg)
{
_department=arg;
}
}
class Department
{
private String _chargeCode;
private Person _manager;
public Department(Person manager)
{
_manager=manager;
}
public Person getManager()
{
return _manager;
}
...
}
像上面这样,我们要知道一个人的部门经理,就得 person.getDepartment).getManager().客户这样调用就说明他已经知道了这一层工作原理,得通过Department对象来访问某人的经理。但是假如我们在Person类中建立一个简单的委托函数:
public Person getManager()
{
return _department.getManager();
}
此时,Person类就成了个服务对象,所有的修改都可以只在它之中完成,这样即使实现细节改变,客户中依然是通过person.getManager()来访问,去除了之前必须访问Department的依赖。
6.Remove Middle Man(移除中间人)
上面是通过添加委托函数来封装一些实现细节,但是问题在于,假如受托类的特性功能越来越多,那每添加一个特性,就要增加一个受托函数,服务类完全变成了一个中间人,此时就应该让客户直接调用受托类。很难说什么程度才算是最合适的,但是我们只要发现问题去修改就好了。
这里这个方法的实现细节就是把上面方法的操作去掉就好了。
7.Introduce Foreign Method(引入外加函数)
当一个类可以为你提供需要的服务,而随后你又需要一个新服务它却无法供应,这时候你假如客户端帮助这个类实现了这个业务,那么其他的地方则无法使用。所以这里可以通过类似C#扩展方法的方式,为这个类补足业务。
但当一个服务类建立了大量外加函数,或者发现许多类都需要同样的外加函数,就不应该使用这个方法,而使用下面这个Introduce Local Extension。
8.Introduce Local Extension(引入本地扩展)
这种方式其实就是单独建立一个新的类作为某个类的扩展类。里面包含的是这个类的扩展方法。在C#中,这样应该很常见了。