学习重构(3)-在对象之间搬移特性
1. Move Method(搬移函数)
在该函数最常用的勒种简历一个有着类似行为的新函数,将就函数变成一个单纯的委托函数,或是将就函数完全移除。
应用场景:搬移函数是重构理论的支柱,如果一个类有太多行为,或者一个类与另一个类有太多合作而形成高度耦合,我们就需要搬移函数。通过这种手段,我们可以使系统中的类更简单,这些类最终也将更干净利落的实现系统交付的任务。
示例:
class A...
void a1()...
void a2() {
B b = new B();
b.b1();
b.b2();
}
class B...
void b1()...
void b2()...
重构为:
class A...
void a2() {
B b = new B();
b.b3();
b.b2();
b.a1();
}
class B...
void b1()...
void b2()...
void a1()...
2. Move Field(搬移值域)
在目标类新建一个字段,修改元字段的所有用户,令他们改用新字段。
应用场景:在类之间移动状态和行为,是重构过程中必不可少的措施。随着系统发展,你会发现自己需要新的类,并需要将原本的工作职责拖到新的类中,当前合理而正确的设计决策,在未来可能不在正确,这没问题,如果你从来没遇到过这种情况,那才有问题。
示例:
class A...
private int id;
private String name;
class B...
private String name;
重构为:
class A...
private String name;
class B...
private int id;
private String name;
3.Extract Class(提炼类)
建立一个新类,将相关的字段和函数从旧类搬移到新类
应用场景:你也许听过类似的教诲:一个类应该是一个清楚的抽象,处理一些明确的责任。但是实际工作中,类会不断成长扩张,你会在这加入一些功能,在那加入一些数据。给某个类添加一项新责任时,你会觉得不值得为这项新责任分离出一个单独的类。于是,随着责任不断增加,这个类变得过分复杂,成为一团乱麻。这样的类往往含有大量函数和数据,因为太大而不易理解。此时需要考虑将哪些部分可以分离出去,放到一个单独的类中。如果某些数据和某些方法总是一起出现,如果某些数据经常同时变化甚至彼此相依,这就表示它们应该被分离出去。
示例:
class Team...
private String engineer1;
private String engineer2;
private String tester1;
private String tester2;
重构为:
class Team...
Engineer engineerTeam;
Tester testerTeam;
class Engineer ...
private String engineer1;
private String engineer2;
class Tester...
private String tester1;
private String tester2;
4. Inline Class(将类内联化)
将这个类的所有特性搬移到另一个类种,然后移除原类。
应用场景:如果一个类不在承担足够的责任,不在有单独存在的理由(这通常是因为此前的重构动作移走了这个类的责任),此时就需要将这个萎缩类塞到其他的类中。
示例:
class Person...
private String name;
private Telephone telephone;
class Telephone...
private String areaCode;
private String number;
String getTelephoneNumber()...
重构为:
class Person...
private String name;
private String areaCode;
private String number;
String getTelephoneNumber()...
5. Hide Delegate(隐藏“委托关系”)
在服务类上建立客户所有的所有函数,用以隐藏委托关系。
应用场景:这个核心思想就是封装。如果某个客户调用了建立与服务对象某个值域基础之上的函数,那么客户就必须维护这层关系,一旦这层委托关系发生变化,比如此服务对象的值域有变化,那么客户也要去响应这个变化才能继续使用原来的函数。我们可以在服务端防止一个简单的委托函数,将这层委托关系隐藏起来,从而去除这个一寸性。这样即使将来委托关系发生变化,变化会被限定在服务端,客户不会受到影响。
示例:
class Client...
void func1() {
Person p = new Person();
String manager = p.getDepartment().getManager();
class Person...
Department getDepartment();
class Department...
String getManager();
重构为:
void func1() {
Person p = new Person();
String manager = p.getManager();
class Person...
Department getDepartment();
String getManager() {
return getDepartment().getManager();
}
class Department...
String getManager();
6. Remove Middle Man (移除中间人)
让客户直接调用委托类。
应用场景:与 第5条Hide Delegate(隐藏“委托关系”)正好相反,隐藏“委托关系”的代价是每当客户要使用受委托类的新特性时,就必须在服务端添加一个简单的委托函数,随着特性越来越多,带来的代价不断攀升。所以两种方法存在一个折中的关系,说不上绝对要使用哪一种,结合实际情况合理决策。
示例:
void func1() {
Person p = new Person();
String manager = p.getManager();
class Person...
Department getDepartment();
String getManager() {
return getDepartment().getManager();
}
class Department...
String getManager();
重构为:
class Client...
void func1() {
Person p = new Person();
String manager = p.getDepartment().getManager();
class Person...
Department getDepartment();
class Department...
String getManager();
7. Introduce Foreign Method(引入外加函数)
在client class中建立一个函数,兵役一个server class实体作为第一引数。
应用场景:当我们正在使用一个类,它为我们提供了想要的所有服务,突然有一天,我们需要一个新功能,但是这个类无法提供,如果可以修改源码,那么万事大吉,否则我们就只能在调用的地方去补齐我们的功能。如果只使用一次这个功能,那么额外编码工作没什么大不了,然后如果需要多次使用这个函数,就得不断重复这些代码。重复代码是软件万恶之源,这些重复代码应该被抽出来放进同一个函数中。进行本项重构时,如果以外加函数实现一项功能,那就是一个明确信号:这个函数原本应该在提供服务的类中加以实现。如果需要大量外加函数,那么这种方法就不建议使用了。外加函数终归是权宜之计,如果有可能,应该把这些函数放到他们的理想家园。
示例:
Date newStart = new Date(previousEnd.getYear(), previousEnd.getMonth(), previousEnd.getDate()+1);
重构为:
Date newStart = nextDay(previousEnd);
private static Date nextDay(Date date) {
return new Date(date.getYear(), date.getMonth(), date.getDate()+1);
8. Introduce Local Extension(引入本地扩展)
建立一个新class,使它包含这些额外函数。让这个扩展品成为source class的subclass(子类)或wrapper(外覆类)。
应用场景:类的作者无法预知未来,他们提供的函数可能不够用,如果我们可以修改源码,那么就可以很容易的增加或者修改已有类来满足使用;或者我们调用的地方少,增加的函数少时,可以使用第7条(Introduce Foreign Method(引入外加函数))来满足诉求。为了解决这个问题,我们需要将这些函数组织到一起,放到一个恰当的地方,此时,标准对象技术subclassing和wrapping是不错的选择。两种方法做选择时,首选subclass,这样的工作量比较少。
subclass或wrapper示例:
class SubDate extends Date {
....
class DateWrapper {
private Date _date;
}