朱雀的IT世界

每天进步一点点,努力做好自己

 

一点重构心得

昨天,要写一段程序完成一个定时任务,是有关Socket 发送的。胖子给我发了一段现成的程序,这段程序基本上的功能是实现了,但是表达的并不是那么清晰,因此我想重构一下。没想到重构的过程竟然花了一个多小时,从晚上八点多,一下就写到了十点,但是重构完后,感觉清晰多了。仔细想想收获颇多,因此在这里写写经验进行总结。

重构程序的目的,不是因为程序不能用才要你去重构,重构的目的是因为一、你的代码,被人看的次数,远比它用到的次数多;二、重构有利于你发现问题,让你的程序结构优化,因此可复用性更强,遵守了知识的唯一性,DRY 原则;三、如果你要改动这段代码,那么先重构,使得你的代码好改,这实际是在为你的未来减少工作量,而且一段优秀的代码,带给你的价值,远比你每次都要Ctrl+C,Ctrl+V 大得多。

写代码,要让你的代码第一次呈现在别人面前的时候,像读英语一般,那么你的代码功底是足够了。你的代码就可以称作你最好的文档了,其余什么文档,大可不必!

基于昨天的经验,我新总结了两条:
一、经常使用重构方法extract method 的人,会发现,总是可以省掉一些临时变量。这是好事,但这可能会造成如下的结果:

method_one(method_two(method_three(method_four())))

也就是说,很可能会导致这种长串的嵌套,导致程序可读性的下降,使人看的晕头转向。那么如何解决呢,其实是一个度的问题。我给自己定了一个规矩,临界点是三个函数这样级联起来,如果超过三个,我就将它们拆开。比如说上面这个小例子,我会拆成:

arg = method_three(method(four));
method_one(method_two(arg));

虽然浪费了一个临时变量,但是这样就可以让人一眼看懂我的意思,可读性提升,修改起来自然也会容易些。

二、写过Java I/O 的人,肯定看到过这样的程序:

Reader in = null;
Writer out = null;
try
{
    in = new InputStreamReader(socket.getInputStream(),"utf8");
    out = new OutputStreamWriter(socket.getOutputStream(),"utf8");

/**
 * some TODOs here
 *
**/
}catch(IOException ioe)
{
    System.err.println("error message");
    ioe.printStackTrace();
}
finally
{
   try
    {
        if(in != null)
           in.close();
        if(out != null)
           out.close();
    }catch(IOException ioe2)
    {
       System.err.println("some error message");
       ioe2.printStackTrace();
    }
}


怎么说呢,这段代码看上去,其实是够好了,其实不重构也是可以的。也许我偏执吧,我认为它不够好,因为:首先,大段的try catch 的确会捕获异常,但是这段代码至少有好几段是会独立抛出异常的,这里包含了四个IO 实例的创建和销毁,这四段代码如果出错都会抛出异常,那么你捕获的到底是哪个呢?其次,没有把功能段合理分开,这段代码的逻辑功能实际上是两个,一个是读,一个是写,那么合并在一起,首先顺序很乱,其次容易让阅读的人产生困惑,从而造成代码可读性差。我是这样做的:

private void writeFile(String fileName, String outStr)
        {
            Writer writer = null;
            try
            {
                writer = new OutputStreamWriter(new FileOutputStream(fileName),
                        "utf8");
            }
            catch (UnsupportedEncodingException e)
            {
                System.err.println("不支持的编码方式");
                e.printStackTrace();
            }
            catch (FileNotFoundException e)
            {
                System.err.println("初始化文件失败,或路径不存在:" + fileName);
                e.printStackTrace();
            }
            try
            {
                writer.write(outStr);
                writer.flush();
            }
            catch (IOException e)
            {
                System.err.println("写文件失败");
                e.printStackTrace();
            }
            finally
            {
                try
                {
                    if(writer != null)
                        writer.close();
                }
                catch (IOException e)
                {
                    System.err.println("关闭文件失败");
                    e.printStackTrace();
                }

            }
        }


类似的,也将读的逻辑独立抽出来,虽然,这不但没使代码的量减少,却增加了很多try catch 模块,不过逻辑上很完整,而且发挥了每个try catch 的最佳功效。我把它起名曰,我个人的偏执情节吧。

困了,要睡觉了,本来还想将代码从最初模样,到最后模样的过程复述一遍,改天有机会再说,精华都已经说了。嘿嘿

posted on 2008-01-08 23:56 朱雀 阅读(338) 评论(0)  编辑  收藏 所属分类: java 基础应用


只有注册用户登录后才能发表评论。


网站导航:
 

导航

统计

常用链接

留言簿(5)

随笔分类

随笔档案

搜索

最新评论

阅读排行榜

评论排行榜