<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>随.心.所.记 &#187; 重构</title>
	<atom:link href="http://www.realdodo.com/tag/%e9%87%8d%e6%9e%84/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.realdodo.com</link>
	<description>享受生活的乐趣与烦恼</description>
	<lastBuildDate>Fri, 25 Jun 2010 18:49:41 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>一段丑陋代码的诞生记</title>
		<link>http://www.realdodo.com/2009/05/07/337/</link>
		<comments>http://www.realdodo.com/2009/05/07/337/#comments</comments>
		<pubDate>Wed, 06 May 2009 16:32:48 +0000</pubDate>
		<dc:creator>realdodo</dc:creator>
				<category><![CDATA[技术文章]]></category>
		<category><![CDATA[KISS]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[变化]]></category>
		<category><![CDATA[设计]]></category>
		<category><![CDATA[重构]]></category>
		<category><![CDATA[需求]]></category>

		<guid isPermaLink="false">http://www.realdodo.com/?p=337</guid>
		<description><![CDATA[话说某年某月某日我在用PHP为我的服务器程序写测试代码，为了方便判断服务器的输出返回，我写下了下面的代码。

function assert_rpc_call($cmd, $input, $logid, $expected_err_code = 0) {
    $ret = rpc_call($cmd, $input, $logid);
    return $ret['result'] === $expected_err_code;
}

在这里，rpc_call()是我测试用的一个函数，可以用来访问我的服务器，它的返回值是一个数组，其中一定有&#8217;result&#8217;域存放一个整型的返回值。
这个函数实在是太好用了，以至于我的测试函数每个地方都用这个函数而不用rpc_call()。我非常满意，觉得已经万事OK了。
世界上唯一不变的是变化。
后来，我发现学xUnit那样每个测试用例打个点表示成功实在是无趣，而且如果真出了什么错就完全没法调试，于是我增强了这个函数。

function assert_rpc_call($cmd, $input, $logid, $expected_err_code = 0) {
    $ret = rpc_call($cmd, $input, $logid);
这样一来，默认情况下我的assert_rpc_call每次都会把返回的信息打印出来，成功输出绿色的PASS，失败返回红色的FAIL，煞是好看。我非常满意。
    $output = &#8220;&#8221;;
    if ($ret['result'] === $expected_err_code) {
        $output .= &#8220;\033[1;32mPASS\033[m\n";
    } else {
        $output .= "\033[1;31mFAIL\033[m\n";
    }
    $output .= print_r($ret, true);
    echo $output;
    return $ret;
}
工具如果太好用了，它一般会被滥用。
可是需求还在变化。不久，我发现有时候我为了初始化一个用于自动测试的环境需要调用很多肯定不会失败的接口，每次把它们的返回打印出来实在是扰乱视听，我需要有一个开关切换回原来的assert_rpc_call()行为。于是我加了一个参数和一些丑陋的代码实现这个功能。再接着，我又想在某些情况下获得返回的数组，在另外一些情况下返回bool值，而且还要在某些情况下打印输入的参数……我知道把这些矛盾的功能全部加到一个函数里面是非常丑陋的，也许应该拆成几个函数更为合适，但因为这个函数已经在很多地方调用，我实在懒得把每个地方都改好。
最终，代码变成了这样。

function [...]]]></description>
			<content:encoded><![CDATA[<p>话说某年某月某日我在用PHP为我的服务器程序写测试代码，为了方便判断服务器的输出返回，我写下了下面的代码。<br />
<code><br />
function assert_rpc_call($cmd, $input, $logid, $expected_err_code = 0) {<br />
    $ret = rpc_call($cmd, $input, $logid);<br />
    return $ret['result'] === $expected_err_code;<br />
}<br />
</code><br />
在这里，rpc_call()是我测试用的一个函数，可以用来访问我的服务器，它的返回值是一个数组，其中一定有&#8217;result&#8217;域存放一个整型的返回值。</p>
<p>这个函数实在是太好用了，以至于我的测试函数每个地方都用这个函数而不用rpc_call()。我非常满意，觉得已经万事OK了。</p>
<blockquote><p>世界上唯一不变的是变化。</p></blockquote>
<p>后来，我发现学xUnit那样每个测试用例打个点表示成功实在是无趣，而且如果真出了什么错就完全没法调试，于是我增强了这个函数。<br />
<code><br />
function assert_rpc_call($cmd, $input, $logid, $expected_err_code = 0) {<br />
    $ret = rpc_call($cmd, $input, $logid);</code><br />
这样一来，默认情况下我的assert_rpc_call每次都会把返回的信息打印出来，成功输出绿色的PASS，失败返回红色的FAIL，煞是好看。我非常满意。</p>
<p>    $output = &#8220;&#8221;;</p>
<p>    if ($ret['result'] === $expected_err_code) {<br />
        $output .= &#8220;\033[1;32mPASS\033[m\n";<br />
    } else {<br />
        $output .= "\033[1;31mFAIL\033[m\n";<br />
    }</p>
<p>    $output .= print_r($ret, true);<br />
    echo $output;<br />
    return $ret;<br />
}</p>
<blockquote><p>工具如果太好用了，它一般会被滥用。</p></blockquote>
<p>可是需求还在变化。不久，我发现有时候我为了初始化一个用于自动测试的环境需要调用很多肯定不会失败的接口，每次把它们的返回打印出来实在是扰乱视听，我需要有一个开关切换回原来的assert_rpc_call()行为。于是我加了一个参数和一些丑陋的代码实现这个功能。再接着，我又想在某些情况下获得返回的数组，在另外一些情况下返回bool值，而且还要在某些情况下打印输入的参数……我知道把这些矛盾的功能全部加到一个函数里面是非常丑陋的，也许应该拆成几个函数更为合适，但因为这个函数已经在很多地方调用，我实在懒得把每个地方都改好。</p>
<p>最终，代码变成了这样。<br />
<code><br />
function assert_rpc_call($cmd, $input, $logid, $expected_err_code = 0, $return = false, $just_need_bool_value = false) {<br />
    $ret = rpc_call($cmd, $input, $logid, !$return);<br />
    $check = array();<br />
    $output = "";</code><br />
最终这个函数非常的丑陋，它变成现在的模样实际上经历了三步：</p>
<p>    if ($ret['result'] === $expected_err_code) {<br />
        $output .= &#8220;\033[1;32mPASS\033[m\n";<br />
        $check['passed'] = true;<br />
    } else {<br />
        $output .= &#8220;\033[1;31mFAIL\033[m\n";<br />
        $check['passed'] = false;<br />
    }</p>
<p>    $output .= print_r($ret, true);<br />
    $check['output'] = $output;</p>
<p>    if (!$return) {<br />
        echo $output;<br />
    }</p>
<p>    if ($just_need_bool_value) {<br />
        return $check["passed"];<br />
    } else {<br />
        if (!$check["passed"]) {<br />
            return false;<br />
        }<br />
    }</p>
<p>    return $check;<br />
}</p>
<ol>
<li>函数太方便了，很多地方都开始引用</li>
<li>需求变化，恰好这个代码通过简单的修改就能基本满足要求</li>
<li>需求继续变化，代码已经发出bad smell，但因为牵扯过多，无法重构</li>
</ol>
<p>具有讽刺意味的是，万恶之源竟然是函数一开始写的太方便了，这本来应是件好事。真正造成问题的则是第二步，需求缓慢的变化、不断积累，就像温水煮青蛙一样，最终使我失去重构的动力，一错再错，终究写出丑陋的代码。</p>
<p>幸好，任何时候都不算晚，就算今天开始也行。不过，我考虑一会之后还是觉得直接把它commit到cvs里面去——我要遵循KISS原则，何必对这种测试用例代码那么计较呢。</p>
<p>有了这种阿Q式的KISS精神之后，这世界上就很少有不能忍的丑陋了。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.realdodo.com/2009/05/07/337/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>
