两周前,我们团队坐在一起进行了一次Team Pair Programming,大约花了两个小时.
在这两个小时里,整个团队的开发人员坐在一个小会议室里,用一台电脑,一个投影仪,对大家认为需要重构的代码进行了重构.
在这两个小时里,整个团队的开发人员坐在一个小会议室里,用一台电脑,一个投影仪,对大家认为需要重构的代码进行了重构.
这种方式带来的好处就是:所有人都很容易理解这种重构方法以及相应的代码变更,并将其应用于接下来的开发工作中.
(在两个小时内能够重构这么多的内容,还有两个因素,功不可没.一个强大重构功能的开发环境[IDEA],以及全面的单元测试.)
总结出一个重构实践(尽管我不是develper),就是
通过分析对象的生命周期,找到缺失的Domain对象.
-------------------------------
重构之前,所有的代码都可以工作.可是,随着Feature的增加,每当需要修改这部分的代码时,大家都感到很别扭.
原始的代码是
public class A {
@Autowired private BService b;
public A() {
}
public doWork() {
.....
C c = b.doSomething ();
.....
}
}
这是个Domain对象,却被注入了Service对象.显然,这是不对的,但是问题在哪里呢?
(1)提问:这个对象的生命周期是怎样的呢?
这是个Domain对象,却被注入了Service对象.显然,这是不对的,但是问题在哪里呢?
(1)提问:这个对象的生命周期是怎样的呢?
从两个类的命名上看,并没有什么必然的联系,追溯代码发现,在A的doWork()中调用了BService的某个方法.
(2)提问:为什么在这里调用了BService的这个方法呢?
因为A的doWork()中需要调用BService的doSomethong()来得到其提供的对象C.
(3)提问:为什么注入BService,而不直接注入C呢?
看来找到问题了.那么就注入这个对象吧.(重构开始了...)
(3)提问:A的doWork()方法中为什么要使用对象C呢?从类名上看,这个对象C应该负责A所要达到的目的吗?
A需要做XXX,而C可以提供XXX,但同时,C还有一些其它方法,提供了YYY和ZZZ,而这些似乎与A毫不相关.
(4)提问:那么究竟是C只应该提供XXX呢?还是应该有其它的对象来负责提供XXX?
从命名上看,C应该负责YYY和ZZZ,而另一个对象D才应该负责提供XXX.(创建这个对象吧)
(5)提问:这个新的对象D是不是应该由这个BService来提供呢?
似乎应该是由BService来提供的,因为目前除了BService,没有任何对象拥有这个能力提供对象D.
(6)提问:检验对象BService,查看其所有Public方法,观察方法的命名,是否发现所有方法名称中的异样了呢?
在两个月前,该对象的责任单一.但自从一个月前加入一个新Feature以后,BService就对外提供了不同的Domain对象,而这两个Domain对象并不在一个层次上.
所以,我们增加了新的Service对象"EService".
(7)提问:那么我们要在A中注入EService吗?
不应该注入EService,而应该是对象D.
自此,我们找到了两个缺失的对象,重构之后,代码看起来有如行云流水,让人感觉心旷神怡.
public class A {
public A(D d) {
}
public void doWork() {
d.doXXX ();
}
}
public class D {
public D() {
}
public boolean doXXX() {
........
return m=null? true:faulse;
}
}
public class EService {
public EService(.....) {
}
public D findDByID(int id) {
........
return d;
}
}
调用的方法也新类似于:
A a= new A(d);
a.doWork();