﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-朱雀的IT世界-随笔分类-应用</title><link>http://www.blogjava.net/phoenixtoday/category/25066.html</link><description>每天进步一点点，努力做好自己</description><language>zh-cn</language><lastBuildDate>Wed, 09 Jan 2008 22:55:45 GMT</lastBuildDate><pubDate>Wed, 09 Jan 2008 22:55:45 GMT</pubDate><ttl>60</ttl><item><title>一点重构心得</title><link>http://www.blogjava.net/phoenixtoday/archive/2008/01/08/173844.html</link><dc:creator>朱雀</dc:creator><author>朱雀</author><pubDate>Tue, 08 Jan 2008 15:56:00 GMT</pubDate><guid>http://www.blogjava.net/phoenixtoday/archive/2008/01/08/173844.html</guid><wfw:comment>http://www.blogjava.net/phoenixtoday/comments/173844.html</wfw:comment><comments>http://www.blogjava.net/phoenixtoday/archive/2008/01/08/173844.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/phoenixtoday/comments/commentRss/173844.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/phoenixtoday/services/trackbacks/173844.html</trackback:ping><description><![CDATA[昨天，要写一段程序完成一个定时任务，是有关Socket 发送的。胖子给我发了一段现成的程序，这段程序基本上的功能是实现了，但是表达的并不是那么清晰，因此我想重构一下。没想到重构的过程竟然花了一个多小时，从晚上八点多，一下就写到了十点，但是重构完后，感觉清晰多了。仔细想想收获颇多，因此在这里写写经验进行总结。<br />
<br />
重构程序的目的，不是因为程序不能用才要你去重构，重构的目的是因为一、你的代码，被人看的次数，远比它用到的次数多；二、重构有利于你发现问题，让你的程序结构优化，因此可复用性更强，遵守了知识的唯一性，DRY 原则；三、如果你要改动这段代码，那么先重构，使得你的代码好改，这实际是在为你的未来减少工作量，而且一段优秀的代码，带给你的价值，远比你每次都要Ctrl+C，Ctrl+V 大得多。<br />
<br />
写代码，要让你的代码第一次呈现在别人面前的时候，像读英语一般，那么你的代码功底是足够了。你的代码就可以称作你最好的文档了，其余什么文档，大可不必！<br />
<br />
基于昨天的经验，我新总结了两条：<br />
一、经常使用重构方法extract method 的人，会发现，总是可以省掉一些临时变量。这是好事，但这可能会造成如下的结果：<br />
<br />
<span style="background-color: #b8c2af;">method_one(method_two(method_three(method_four())))</span><br />
<br />
也就是说，很可能会导致这种长串的嵌套，导致程序可读性的下降，使人看的晕头转向。那么如何解决呢，其实是一个度的问题。我给自己定了一个规矩，临界点是三个函数这样级联起来，如果超过三个，我就将它们拆开。比如说上面这个小例子，我会拆成：<br />
<br />
<span style="background-color: #b8c2af;">
arg = method_three(method(four));</span><br />
<span style="background-color: #b8c2af;">method_one(method_two(arg));</span><br />
<br />
虽然浪费了一个临时变量，但是这样就可以让人一眼看懂我的意思，可读性提升，修改起来自然也会容易些。<br />
<br />
二、写过Java I/O 的人，肯定看到过这样的程序：<br />
<br />
<span style="background-color: #b8c2af;">
Reader in = null;<br />
Writer out = null;<br />
try<br />
{<br />
&nbsp;&nbsp;&nbsp; in = new InputStreamReader(socket.getInputStream(),"utf8");<br />
&nbsp;&nbsp;&nbsp; out = new OutputStreamWriter(socket.getOutputStream(),"utf8");<br />
<br />
/**<br />
&nbsp;* some TODOs here<br />
&nbsp;*<br />
**/<br />
}catch(IOException ioe)<br />
{<br />
&nbsp;&nbsp;&nbsp; System.err.println("error message");<br />
&nbsp;&nbsp;&nbsp; ioe.printStackTrace();<br />
}<br />
finally<br />
{<br />
&nbsp;&nbsp; try<br />
&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; if(in != null)<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;  &nbsp;&nbsp;&nbsp; in.close();<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; if(out != null)<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;  &nbsp;&nbsp;&nbsp; out.close();<br />
&nbsp;&nbsp;&nbsp; }catch(IOException ioe2)<br />
&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;  System.err.println("some error message");<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;  ioe2.printStackTrace();<br />
&nbsp;&nbsp;&nbsp; }<br />
}</span><br />
<br />
怎么说呢，这段代码看上去，其实是够好了，其实不重构也是可以的。也许我偏执吧，我认为它不够好，因为：首先，大段的try catch 的确会捕获异常，但是这段代码至少有好几段是会独立抛出异常的，这里包含了四个IO 实例的创建和销毁，这四段代码如果出错都会抛出异常，那么你捕获的到底是哪个呢？其次，没有把功能段合理分开，这段代码的逻辑功能实际上是两个，一个是读，一个是写，那么合并在一起，首先顺序很乱，其次容易让阅读的人产生困惑，从而造成代码可读性差。我是这样做的：<br />
<br />
<span style="background-color: #b8c2af;">
private void writeFile(String fileName, String outStr)<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;{<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; Writer writer = null;<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;try<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;{<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;writer = new OutputStreamWriter(new FileOutputStream(fileName),<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;"utf8");<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;catch (UnsupportedEncodingException e)<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;{<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;System.err.println("不支持的编码方式");<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;e.printStackTrace();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;catch (FileNotFoundException e)<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;{<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;System.err.println("初始化文件失败，或路径不存在：" + fileName);<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;e.printStackTrace();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;try<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;{<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;writer.write(outStr);<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;writer.flush();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;catch (IOException e)<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;{<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;System.err.println("写文件失败");<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;e.printStackTrace();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;finally<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;{<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;try<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;{<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;  &nbsp;&nbsp;  &nbsp;&nbsp;  &nbsp;&nbsp;  &nbsp;&nbsp;&nbsp;  if(writer != null)<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; writer.close();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;catch (IOException e)<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;{<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;System.err.println("关闭文件失败");<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;e.printStackTrace();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}</span><br />
<br />
类似的，也将读的逻辑独立抽出来，虽然，这不但没使代码的量减少，却增加了很多try catch 模块，不过逻辑上很完整，而且发挥了每个try catch 的最佳功效。我把它起名曰，我个人的偏执情节吧。<br />
<br />
困了，要睡觉了，本来还想将代码从最初模样，到最后模样的过程复述一遍，改天有机会再说，精华都已经说了。嘿嘿<br />
<br />
<img src ="http://www.blogjava.net/phoenixtoday/aggbug/173844.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/phoenixtoday/" target="_blank">朱雀</a> 2008-01-08 23:56 <a href="http://www.blogjava.net/phoenixtoday/archive/2008/01/08/173844.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>