关于重构的一点体会

来源:互联网 发布:webservice返回json 编辑:程序博客网 时间:2024/05/23 07:24

本篇就简单的写对代码重构一些自己的体会,总的来说应该是比较凌乱毫无章法的一篇文章,写到哪儿算哪儿吧。
为什么会想起写这方面的东西呢?虽然我自己在工作中经常阅读和重构自己的代码,但是从来没有想过要写出点什么东西,因为《重构,改善既有代码设计》这本书本身就很牛逼了,自己确实没这个必要在写,不过几天在整合另外一个项目代码的时候着实让我蛋疼了一把,这也是我想写这篇心得的原因之一。

项目中有如下代码段,在用了继承的情况下,我发现各个子类居然还能把该代码段都重写了一遍:

        mTitleView = new TextView(mContext);        mTitleView.setMaxEms(15);        mTitleView.setSingleLine();        // 设置省略号末尾结束        mTitleView.setGravity(Gravity.CENTER);// 设置居中        mTitleView.setMinWidth(DeviceUtils.dip2px(mContext, 40));        mTitleView.setTextColor(Color.WHITE);        mTitleView.setTextSize(14);        mTitleView.setPadding(DeviceUtils.dip2px(mContext, 25),                DeviceUtils.dip2px(mContext, 3),                DeviceUtils.dip2px(mContext, 10),                DeviceUtils.dip2px(mContext, 3));        FrameLayout.LayoutParams mTitleParams = new FrameLayout.LayoutParams(                FrameLayout.LayoutParams.MATCH_PARENT,                FrameLayout.LayoutParams.MATCH_PARENT);        mTitleView.setLayoutParams(mTitleParams);

上面的很简单,也即是创建一个TextView对象,然后设置TextView的字体,颜色等等各种属性,但是既然有基类的情况下,基类的各个子类中还存在这种完全一模一样的代码,这就很让人蛋疼了,估计ctrl+c,ctrl+v的毛病吧。于是乎我花了一下午的时间,各种测试,各种提炼,把几个子类中共同的代码全部弄到基类中去,才算松了一口气。git add ,git commit,git push,然后是git merge几个git命令提交了代码之后,就想写一点自己在项目中遇到的几处值得重构代码的地方。算是做个笔录。

其实首次读《重构 改善既有代码的设计》这本书好像是两年前的事儿了,当时看了感觉很受触动,上面的代码页跟着敲了个遍。但是遗憾的我当时并没有把重构太当回事儿,后来由于某些我已经忘却的原因又把这本书重温了下,然后才有意识的在平常开发的过程中对代码进行重构。

有时候没有开发任务的时候,我会翻看自己当前项目写过代码,进行第一步重构!
把类中那些很长的方法尽可能提炼出子方法,甚至有的代码段能提炼出一个小子类就提炼出一个子类出来!估计每个程序员都有ctrl+c,ctrl+v的习惯,殊不知当你下意识的ctrl+c的时候,此时也许正是代码重构的契机呢?说不定你重构后的子方法也能在别的地方用到。从这点来看的话,就算是复制代码,与其复制一大堆很长的方法,倒不如复制重构过后的子方法来的简洁。

代码中临时变量的处理

有时候一个方法里面定义一个临时变量,但是用到这个临时变量的地方距离定义该变量的地方相差的行数太多,我也会把此变量位置移动到距离使用此变量最近的地方去。这样做有时候会为你下一步提炼子方法的操作更方便一些。
下面一段代码应该都比较熟悉:

int size = lists.size();for(int i=0;i<size;i++){   dosomething()}

在遇到这样的代码或者写这样的代码的时候我会想这个size别的地方能否用到,如果用不到的话,我就会对上面的代码改成如下所示的情况:

for(int i=0,size=lists.size();i<size;i++){  dosomething();}

当然,两段代码前后相比较的话也就是减少了一行代码的量!

Handler需要注意的地方

在自己手头的项目,有天我再次撸一遍代码的时候发现有好些个页面的逻辑里面都有Handler出现,用它来发送(延迟)消息,然后处理消息。然而我发现对于发送的延迟消息,在页面关闭的时候并没有对这些延迟消息做删除处理,导致页面关闭的时候发送延迟消息的handler仍然在工作,于是我就开始了代码重构!首先,为项目的代码中一个个类都添加了cancelMsg(int what)方法,在页面关闭的时候调用handler的handler.removeMessages(what)来对消息进行处理。但是当我在三个类里面重复写了cancelMsg方法的时候,我就感觉不太对劲,每个方法里面都写cancelMsg并且好多个类里面都有Handler,这不是有重造轮子的嫌疑?于是又再次重构,把Handler用一个叫HandlerMsgController的类把Handler封装起来,凡是用到Handler的地方都用HandlerMsgController来完成,该类的主要代码如下:

//消息处理接口,处理具体的消息private HandleMseageListener handleMessageListener;    private Handler handler = new Handler() {        public void handleMessage(android.os.Message msg) {            if (null != handleMessageListener) {                //把消息给HandleMseageListener 对象来处理之。                handleMessageListener.handleMessage(msg);            }        };    };/***消息处理接口,由客户端设置自己对消息的处理*/public interface HandleMseageListener {    void handleMessage(Message msg);}/***取消发送的消息,在页面关闭的时候调用*/public void cancelMsg(int what) {        if (handler.hasMessages(what)) {            handler.removeMessages(what);        }    }

这样我在使用Handler的时候只需要初始化HandlerMsgController对象,并调用相关的sendMsg方法,然后在页面关闭时调用cancelMsg即可;虽然这次重构也没什么技术含量,但是给我的感觉上来说,职责上比较干净整洁。

“万恶”的switch
switch是个好东西,但是用的不当的话,那就很让人蛋疼。项目中有一个4000多行的类,其中有一个方法有1000多行,就是因为充斥着大量的switch,而且switch里面还嵌套这switch,刚接手项目的时候硬着头皮往添加一些新功能的时候都小心翼翼的。有次因为嵌套这switch没有分清case分支是哪个switch(当时以为我添加的位置对了),然后有个bug纠结了一段时间,然后我就下决定得好好把这个类重构一番。
1.把switch的各个case分支提炼出一个个子程序。
在各个case语句提炼出子方法的时候,会遇到各个子方法充斥着若干相同的变量;这个暂且放在一边,然后如果子方法里面还有switch,那么继续把该switch的case分支继续提炼子方法。
然后这是我的第一次对这个类重构,然后就开始继续新功能的开发。在该类原有4000+行代码即将突破5000行的时候我没查询一个原来一个逻辑的时候各中ctrl+o,或者ctrl+f;没办法类太臃肿了,或者说该类承担的职责太多了。于是乎继续重构。

2.把case提炼的子方法提炼出单个的类;
好像设计模式类和一些其他相关书籍上提到switch语句case分支用单个类来表示的说法。反正在我自己在身边的本子上写写画画分析之后我也觉得把case语句提到的子方法作为提炼到一个个类中去,上面说道case提炼的子方法会有若干相同的变量,那么就把这些变量提到作为类的成员变量提炼到基类中去。就这样一天的时间又被我捯饬过去了,修改测试在修改在测试,然后整整提炼出八个子类(当然我还在有很大的细化空间如果要达到单一职责的话,这也是我写完本篇博文之后要做的)。各个子类从大的类别上各司其职,算是初步完成了类的单一职责。原来的将近5000多行的代码量,也被我压缩到了2000多行。但是职责上看起来清晰多了,添加新功能的时候也很方便。

一个方法太长的问题

熟悉设计模式的攻城狮都应该知道类的设计模式之一就是单一职责,其实个人认为这个单一职责的粒度也可以更小一点:就是方法上的单一职责!一个方法不要做太多的东西。从直观上说就是说,一个方法不需要太长,能提炼出子方法的就提炼出子方法,让子方法来完成一个小职责,各个子方法的职责在原始方法中调用。这样看起来代码也很清晰。

在项目中看到一段初始化控件的方法,有好几百行那么长,得翻几页才能阅读到方法的结尾,此类初始化工作的代方法是最好重构也是应该有意识重构的地方,经过我的整理–提炼子方法,该几百行的代码被调用了五六个方法就搞定:

//子方法名仅供参考void init(){  init1();  init2();  init3();  ....  init4();}

这样在排查问题的时候可以迅速定位问题出现在哪一个方法上。所以我在遇到较长方法的时候能提炼出子方法的我通常都会弄一个private子方法出来,简化原方法的代码量,这样对以后的维护提供了方便。

清晰的包分类

其实单一职责运用到包上也是个不错的注意,这样可以很明确知道各个包下的类大致是在干什么样的工作。所以我某个包下面类比较多而且功能比较多的情况下,我就会把具有功能功能的几个类,比如几个不同风格的投票类都添加到新创建的vote包里面去。

单例模式的小应用
在我开发的过程中我发现一个A对象在应用启动该对象的各种属性只初始化了一次,然后有很多对象都用了A对象,在最初代码里是通过如下形式来使用的:

public class B{  private A a;  public void setA(A a){    this.a =a;  } public void doSomething(){    a.doSomething(); }}

然后我就想既然A对象仅仅初始化了一次,然后在初始化好以后各个对象在使用A,那为什么还采用如上所示的代码呢?直接把A设置成单例模式不就行了!!!说道做到,经过此重构,使用A对象的类诸如上面的B代码就发生如下的变化:

public class B{  public void doSomething(){    A.getInstance().doSomething(); }}

到此为止就写这么多吧,写的有点粗糙,关于重构如果以后还有新的体会,会继续更新该篇心得。

最后建议业余的时候阅读《重构,改善既有代码设计》这本书。

1 0
原创粉丝点击