<?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>Behind the Code &#187; TDD</title>
	<atom:link href="http://yinwm.com/tag/tdd/feed/" rel="self" type="application/rss+xml" />
	<link>http://yinwm.com</link>
	<description>Just Do It</description>
	<lastBuildDate>Wed, 04 Jan 2012 03:54:33 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3</generator>
		<item>
		<title>封闭笔记——第十八天，收官</title>
		<link>http://yinwm.com/2010/03/project-mossad-day-18/</link>
		<comments>http://yinwm.com/2010/03/project-mossad-day-18/#comments</comments>
		<pubDate>Thu, 18 Mar 2010 21:52:57 +0000</pubDate>
		<dc:creator>yinwm</dc:creator>
				<category><![CDATA[TDD]]></category>
		<category><![CDATA[程序设计]]></category>
		<category><![CDATA[项目管理]]></category>
		<category><![CDATA[程序开发]]></category>

		<guid isPermaLink="false">http://yinwm.com/?p=185</guid>
		<description><![CDATA[2010-03-18 晴 今天应该是封闭的最后一天，我们要在最后一天把这次封闭的成果上线展示出来。这次封闭其实更多的是一个练兵，是让兄弟姐妹们能够学习和提高。 今天是我们的收官之战，同志们还在前沿阵地奋战。一拨人公司配合上线的，另外一拨人测试这次开发的功能，还有继续改代码的，还有我这样记录的。 今天大家都很辛苦，几拨人都是到了中午下午才把代码全部搞定提测。需要上线的有两部分，一部分人是这次封闭做的新的小的任务，我们姑且叫做团队A。一部分是和公司一起搞的一个大版本升级，我们不妨叫做团队B。两个任务同时上线，刚刚好可以做一个不是那么精准的比较。 人员组成 A组是一个中级程序员和三个初级程序员组成的团队，每人认领几个指令任务进行开发。 B组是一个半高级程序员，而那一个全人的水平是很不错的。 任务类型 A组是一个组合指令的任务，可以明确的拆分任务，而且B组的负责人前期已经制作好了一个框架。 B组是一个现有系统的升级，牵扯到新老平台的切换，而且要修改的代码很难界定范围。 开发模式 A组采用的是这次封闭尝到的单元测试先行的开发模式。 B组由于是修改现有系统，而且以前没有任何的测试类代码，所以还是传统的开发模式。 任务难度 A组的每一个细分的任务都不是很难，但是比较庞杂，想要做好了必须完全吃透需求和无数的场景。 B组的难度比较大，是公司的一个重点项目，牵扯的系统都很多。 任务强度 A组是这次封闭的主要项目。 B组时间非常紧张，本周才开始。 测试人员数量 A组一个半人，最后这半个测试人员也并入到B组的测试中。 B组的专职测试人员4+人，还有很多不明真相的志愿者。 测试时间 A组采用单元测试，从第一天可以说就在测试，从全部提测到测试结束大约3天。 B组由于任务重，所以测试时间并不多，但是从开发开始没多长时间就开始测试了。 Bug A组的Bug不能算少，但是严重的bug并不多，只是几个。大多数的bug集中在类似文案调整的类型。 B组的Bug并不是很多，但是每一个bug都很纠结。 Bug的修复 A组的Bug由于存在测试用例，所以很容易定位，基本上每一个Bug修复的时间都不长。 B组由于没有测试用例，加之系统复杂，所以每一个bug都无法快速定位，必须依靠经验和对代码的熟悉程度。而且中间还出现了相关人员集中在白板前思索可能的坑在哪的问题。 上面是一个比较不靠谱的比较，也许并不公平。但是我们从中还是能看到一些问题的。比如有了测试用例可以快速的定位bug，比如如果没有我们会很纠结。比如我们每次修改的系统如果牵扯的非常多我们就很抓狂（应该把系统隔离的更好）。 现在已经早上6点了，我们还在准备上线。两个组都还纠结，但是看到曙光了。凡是我们用例定义的好的，测试覆盖的好的，很快就过了，否则都很缓慢。我们还有很长的路要走。 记得第一天我说，行百里者半九十，我们现在还远没有走到九十里。前面的路还很长。 前方的路不知是洒满阳光还是布满荆棘，我都会掸掸身上的土，继续走下去。]]></description>
			<content:encoded><![CDATA[<p>2010-03-18 晴</p>
<p>今天应该是封闭的最后一天，我们要在最后一天把这次封闭的成果上线展示出来。这次封闭其实更多的是一个练兵，是让兄弟姐妹们能够学习和提高。</p>
<p>今天是我们的收官之战，同志们还在前沿阵地奋战。一拨人公司配合上线的，另外一拨人测试这次开发的功能，还有继续改代码的，还有我这样记录的。</p>
<p>今天大家都很辛苦，几拨人都是到了中午下午才把代码全部搞定提测。需要上线的有两部分，一部分人是这次封闭做的新的小的任务，我们姑且叫做团队A。一部分是和公司一起搞的一个大版本升级，我们不妨叫做团队B。两个任务同时上线，刚刚好可以做一个不是那么精准的比较。</p>
<ol>
<li>人员组成
<ul>
<li>A组是一个中级程序员和三个初级程序员组成的团队，每人认领几个指令任务进行开发。</li>
<li>B组是一个半高级程序员，而那一个全人的水平是很不错的。</li>
</ul>
</li>
<li>任务类型
<ul>
<li>A组是一个组合指令的任务，可以明确的拆分任务，而且B组的负责人前期已经制作好了一个框架。</li>
<li>B组是一个现有系统的升级，牵扯到新老平台的切换，而且要修改的代码很难界定范围。</li>
</ul>
</li>
<li>开发模式
<ul>
<li>A组采用的是这次封闭尝到的单元测试先行的开发模式。</li>
<li>B组由于是修改现有系统，而且以前没有任何的测试类代码，所以还是传统的开发模式。</li>
</ul>
</li>
<li>任务难度
<ul>
<li>A组的每一个细分的任务都不是很难，但是比较庞杂，想要做好了必须完全吃透需求和无数的场景。</li>
<li>B组的难度比较大，是公司的一个重点项目，牵扯的系统都很多。</li>
</ul>
</li>
<li>任务强度
<ul>
<li>A组是这次封闭的主要项目。</li>
<li>B组时间非常紧张，本周才开始。</li>
</ul>
</li>
<li>测试人员数量
<ul>
<li>A组一个半人，最后这半个测试人员也并入到B组的测试中。</li>
<li>B组的专职测试人员4+人，还有很多不明真相的志愿者。</li>
</ul>
</li>
<li>测试时间
<ul>
<li>A组采用单元测试，从第一天可以说就在测试，从全部提测到测试结束大约3天。</li>
<li>B组由于任务重，所以测试时间并不多，但是从开发开始没多长时间就开始测试了。</li>
</ul>
</li>
<li>Bug
<ul>
<li>A组的Bug不能算少，但是严重的bug并不多，只是几个。大多数的bug集中在类似文案调整的类型。</li>
<li>B组的Bug并不是很多，但是每一个bug都很纠结。</li>
</ul>
</li>
<li>Bug的修复
<ul>
<li>A组的Bug由于存在测试用例，所以很容易定位，基本上每一个Bug修复的时间都不长。</li>
<li>B组由于没有测试用例，加之系统复杂，所以每一个bug都无法快速定位，必须依靠经验和对代码的熟悉程度。而且中间还出现了相关人员集中在白板前思索可能的坑在哪的问题。</li>
</ul>
</li>
</ol>
<p>上面是一个比较不靠谱的比较，也许并不公平。但是我们从中还是能看到一些问题的。比如有了测试用例可以快速的定位bug，比如如果没有我们会很纠结。比如我们每次修改的系统如果牵扯的非常多我们就很抓狂（应该把系统隔离的更好）。</p>
<p>现在已经早上6点了，我们还在准备上线。两个组都还纠结，但是看到曙光了。凡是我们用例定义的好的，测试覆盖的好的，很快就过了，否则都很缓慢。我们还有很长的路要走。</p>
<p>记得第一天我说，行百里者半九十，我们现在还远没有走到九十里。前面的路还很长。<br />
前方的路不知是洒满阳光还是布满荆棘，我都会掸掸身上的土，继续走下去。</p>
]]></content:encoded>
			<wfw:commentRss>http://yinwm.com/2010/03/project-mossad-day-18/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>封闭笔记——第十四天，上线</title>
		<link>http://yinwm.com/2010/03/project-mossad-day-14/</link>
		<comments>http://yinwm.com/2010/03/project-mossad-day-14/#comments</comments>
		<pubDate>Sun, 14 Mar 2010 17:07:19 +0000</pubDate>
		<dc:creator>yinwm</dc:creator>
				<category><![CDATA[TDD]]></category>
		<category><![CDATA[程序设计]]></category>
		<category><![CDATA[项目管理]]></category>
		<category><![CDATA[程序开发]]></category>

		<guid isPermaLink="false">http://yinwm.com/?p=172</guid>
		<description><![CDATA[2010-03-14 大雪 难道真的2012？现在还下雪，还是鹅毛大雪。娘列~~ 今天重温了一下进度，感觉还是有点紧张的，实在不行就只能砍掉一些需求了。现在给兄弟们的压力太大了，工作很辛苦。不过按照我的说法第三周效率会有一定的提高，因为测试用例大家已经熟悉一些了。不过这个还有观后效。 中午围观了一下上线流程，真是帅呆，累呆。 上线应该是包含哪些呢？ 各小组把代码提交到自己的分支 把不同分支的代码（不同小组的代码）合并到上线分支（有些是从分支上线，在此并不讨论，这两种方式没有好坏之分） 把上线分支的代码同步到线上环境 把上线分支的代码合并到其他的分支 这里面说起来挺简单的，而且第三步我们已经有了上线工具可以做到，按道理应该是不难的。但是我们却花了至少一个小时才完成。真是杯具啊。 说起来我们在哪里费时间了呢？ 各小组代码提交到自己分支，很快。 把不同分支的代码合并到上线分支，这个费时间很长。 原因是上次的第四步没做好。而且我们每次都做这两步都是基于文件的（而不是基于提交或者 ChangeSet），每次挑选到底哪个文件（甚至哪个部分）要上到上线代码是一项人类体力与智力的考验。如果没有拜过春哥或者你不是从娜美克星来的你可得小心。 使用上线工具，很快。问题也是基于文件的，但是现在还没有遇到问题。 貌似这次又忘了？ 这里面最耽误时间的是第二步。今天的花销其中95%以上都是消耗在这里。 个人觉得这里面的问题并不容易解决。比如我们总有一些文件不得不提交，但是这些文件却不需要上线。 这里如何解决呢？ 最简单的办法当然是一次性用新的代码合并上去最好，但是如何保证合并后的代码没有问题呢？尤其是一些比如负责分发判断等代码，贸然的合并一定出现问题。而其一些常量定义的文件在多团队开发下，也一定会出现冲突的情况。 根本上我觉得现在上线和多团队开发还不是那么的爽，还需要大量的人力参与，可能还是开发模式的一些有待提高的地方。试想，如果linux也有我们这样的问题，那就真的没法混了。 这个我会好好的考虑一下如何推进。既要让效率提升，又要每次改动都让现行的开发人员平滑的升级，这并不简单。 程序开发是一个团队作战，我们需要明星战士，但是我们更需要一个能捏合整体的有效的方法。而且还要考虑到产品和运营的需求，在市场、产品、研发、积累等等方面做到平衡。管理，则应该是这个粘合剂，我也应该是这个粘合剂。]]></description>
			<content:encoded><![CDATA[<p>2010-03-14 大雪</p>
<p>难道真的2012？现在还下雪，还是鹅毛大雪。娘列~~</p>
<p>今天重温了一下进度，感觉还是有点紧张的，实在不行就只能砍掉一些需求了。现在给兄弟们的压力太大了，工作很辛苦。不过按照我的说法第三周效率会有一定的提高，因为测试用例大家已经熟悉一些了。不过这个还有观后效。</p>
<p>中午围观了一下上线流程，真是帅呆，累呆。</p>
<p>上线应该是包含哪些呢？</p>
<ol>
<li>各小组把代码提交到自己的分支</li>
<li>把不同分支的代码（不同小组的代码）合并到上线分支（有些是从分支上线，在此并不讨论，这两种方式没有好坏之分）</li>
<li>把上线分支的代码同步到线上环境</li>
<li>把上线分支的代码合并到其他的分支</li>
</ol>
<p>这里面说起来挺简单的，而且第三步我们已经有了上线工具可以做到，按道理应该是不难的。但是我们却花了至少一个小时才完成。真是杯具啊。</p>
<p>说起来我们在哪里费时间了呢？</p>
<ol>
<li>各小组代码提交到自己分支，很快。</li>
<li>把不同分支的代码合并到上线分支，这个费时间很长。<br />
原因是上次的第四步没做好。而且我们每次都做这两步都是<span style="color: #ff0000;"><strong>基于文件</strong></span>的（而不是基于提交或者 ChangeSet），每次挑选到底哪个文件（甚至哪个部分）要上到上线代码是一项人类体力与智力的考验。如果没有拜过春哥或者你不是从娜美克星来的你可得小心。</li>
<li>使用上线工具，很快。问题也是基于文件的，但是现在还没有遇到问题。</li>
<li>貌似这次又忘了？</li>
</ol>
<p>这里面最耽误时间的是第二步。今天的花销其中95%以上都是消耗在这里。<br />
个人觉得这里面的问题并不容易解决。比如我们总有一些文件不得不提交，但是这些文件却不需要上线。 这里如何解决呢？<br />
最简单的办法当然是一次性用新的代码合并上去最好，但是如何保证合并后的代码没有问题呢？尤其是一些比如负责分发判断等代码，贸然的合并一定出现问题。而其一些常量定义的文件在多团队开发下，也一定会出现冲突的情况。</p>
<p>根本上我觉得现在上线和多团队开发还不是那么的爽，还需要大量的人力参与，可能还是开发模式的一些有待提高的地方。试想，如果linux也有我们这样的问题，那就真的没法混了。<br />
这个我会好好的考虑一下如何推进。既要让效率提升，<span style="color: #0000ff;"><strong>又要每次改动都让现行的开发人员平滑的升级</strong></span>，这并不简单。</p>
<p>程序开发是一个团队作战，我们需要明星战士，但是我们更需要一个能捏合整体的有效的方法。而且还要考虑到产品和运营的需求，在市场、产品、研发、积累等等方面做到平衡。管理，则应该是这个粘合剂，我也应该是这个粘合剂。</p>
]]></content:encoded>
			<wfw:commentRss>http://yinwm.com/2010/03/project-mossad-day-14/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>封闭笔记——第十一天，团队</title>
		<link>http://yinwm.com/2010/03/project-mossad-day-11/</link>
		<comments>http://yinwm.com/2010/03/project-mossad-day-11/#comments</comments>
		<pubDate>Thu, 11 Mar 2010 14:54:46 +0000</pubDate>
		<dc:creator>yinwm</dc:creator>
				<category><![CDATA[TDD]]></category>
		<category><![CDATA[项目管理]]></category>
		<category><![CDATA[程序开发]]></category>

		<guid isPermaLink="false">http://yinwm.com/?p=162</guid>
		<description><![CDATA[2010-03-11 晴，开始暖和 今天大家已经开始进入到一种稍微憋闷的状态了。有些兄弟开始出现了身体不适的反应，我很担忧。早上没有叫大家，让大家自然地醒来。工作中也第一次允许开放音乐了，必须开始调整气氛了，已经近乎癫狂了。 某个哥们也开始说像他这样不爱说话的人也开始有想见人就拉住聊天的冲动了。 现在我已经开始轰人回去睡觉了。 状态的调整是一个很重要的事情，如何让团队保证一个持续的战斗力是一个很高深的话题。 今天也第一次透彻的感觉到团队的力量。以往看到很多的人说，在一切的资源中，团队是最重要的，但是从来没有过这么直接和透彻的感受。 一个合理的团队搭配是保证高质量战斗力的前提条件。这个团队必须有教练的角色，也必须有核心队员和明星队员。但是最重要的则是梯队的合理。试想，当年风头最劲的黄色潜水艇就是因为其他球员太弱，就算里克尔梅赔上老命也无法杀入欧冠决赛。 现在我的团队就是梯队有些不合理，导致几个兄弟十分疲劳但是任务其他人也无法分担，看得我心焦又心疼。 兄弟们，快快成长起来吧。 晚上和组里的一个童鞋一起看他的测试用例和代码，写的还算不错。和他一起又探讨了一些关于TDD、重构的思维方式以及最佳实践。一次可能灌得有点多了，慢慢来，成长也是一个过程的。 我准备在这几天单独给他开小灶了，必须拔苗助长了，会很辛苦，但是也会很有收获。 有这样的一个机会，我能够近距离的，透彻的观察我的团队，思考团队应该如何进行人员配比、如何建设、团队的弱点、团队的潜力等。这是一个最佳的实践机会，也让我自身提高去更好地履行教练的职责。 知难行易，任重道远。加油。]]></description>
			<content:encoded><![CDATA[<p>2010-03-11 晴，开始暖和</p>
<p>今天大家已经开始进入到一种稍微憋闷的状态了。有些兄弟开始出现了身体不适的反应，我很担忧。早上没有叫大家，让大家自然地醒来。工作中也第一次允许开放音乐了，必须开始调整气氛了，已经近乎癫狂了。<br />
某个哥们也开始说像他这样不爱说话的人也开始有想见人就拉住聊天的冲动了。<br />
现在我已经开始轰人回去睡觉了。</p>
<p>状态的调整是一个很重要的事情，如何让团队保证一个持续的战斗力是一个很高深的话题。<br />
今天也第一次透彻的感觉到团队的力量。以往看到很多的人说，在一切的资源中，团队是最重要的，但是从来没有过这么直接和透彻的感受。 一个合理的团队搭配是保证高质量战斗力的前提条件。这个团队必须有教练的角色，也必须有核心队员和明星队员。但是最重要的则是梯队的合理。试想，当年风头最劲的黄色潜水艇就是因为其他球员太弱，就算里克尔梅赔上老命也无法杀入欧冠决赛。<br />
现在我的团队就是梯队有些不合理，导致几个兄弟十分疲劳但是任务其他人也无法分担，看得我心焦又心疼。<br />
兄弟们，快快成长起来吧。</p>
<p>晚上和组里的一个童鞋一起看他的测试用例和代码，写的还算不错。和他一起又探讨了一些关于TDD、重构的思维方式以及最佳实践。一次可能灌得有点多了，慢慢来，成长也是一个过程的。<br />
我准备在这几天单独给他开小灶了，必须拔苗助长了，会很辛苦，但是也会很有收获。</p>
<p>有这样的一个机会，我能够近距离的，透彻的观察我的团队，思考团队应该如何进行人员配比、如何建设、团队的弱点、团队的潜力等。这是一个最佳的实践机会，也让我自身提高去更好地履行教练的职责。</p>
<p>知难行易，任重道远。加油。</p>
]]></content:encoded>
			<wfw:commentRss>http://yinwm.com/2010/03/project-mossad-day-11/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>封闭笔记——第九天，测试用例反复</title>
		<link>http://yinwm.com/2010/03/project-mossad-day-9/</link>
		<comments>http://yinwm.com/2010/03/project-mossad-day-9/#comments</comments>
		<pubDate>Tue, 09 Mar 2010 16:15:48 +0000</pubDate>
		<dc:creator>yinwm</dc:creator>
				<category><![CDATA[TDD]]></category>
		<category><![CDATA[项目管理]]></category>

		<guid isPermaLink="false">http://yinwm.com/?p=155</guid>
		<description><![CDATA[2010-03-09  昨天雪，今天冷 昨天已经有童鞋提测了，今天测试MM开始了基于单元测试的测试流程，明天我们就可以拿到一个对比的报告了，很期待。第一个完整流程指日可待。 今天遇到的一个情况是测试驱动开发的反复。 有童鞋对测试用例有了一些疑问 有童鞋恢复到了以前的模式，先写功能性代码，然后用用例才测试 疑问是这样的，他在开发中，遇到了大量的遗留代码的问题，这时候在对已有代码的信任度上已经产生了质疑，他总是觉得要覆盖更多的内容才放心，但是这样却觉得UnitTest写起来没完没了了。 我对这个的回答是从两个方面进行的 因为这次是第一次采用UnitTest，所以一定会出现大量的以前藏着的雷被踩到。 已有的代码，我们这次假定是信任的，如果真的有问题，我们暂时绕过去，并告知测试和原有代码维护人员。因为如果不这样我们是无法定义用例覆盖的边界的。而遗留代码的问题，是应该相关的维护者补充对应的测试用例来覆盖的。 测试驱动开发是一个循环的事情，坚持住了，往前走就是良性循环，被遗漏的代码越来越少，雷区越来越少，代码质量提升。但是如果往后走，总觉得没时间写，总是有大量的不可覆盖不可测试的代码，每写一点就趟雷，会严重的打击士气，然后就不写了，然后就恶性循环了。 让我觉得非常受打击的一个事情是有些童鞋回到以往的开发模式了。先写功能代码，然后用用例开覆盖功能代码，起到测试的作用。 我记得我多次提过这样的危害，但是还有童鞋跑了回去，晚上只好那他开刀进行CodeReview。结果发现了一些设计和场景层面没有照顾到的地方。在此，我再说一次用 UnitTest 会带来的好处，当然这里只是说了一些对于开发者最明显的好处 用用例覆盖场景，因为我们考虑的是场景并且有测试人员配合，所以我们不容易遗漏场景 写TestCase是在进行设计，从使用者的角度，从API设计者的角度去看代码，会让设计更加自然 用功能性代码覆盖用例，功能性代码有保障 测试时期发生Bug容易定位 为测试节省时间，比如边界测试等都可以通过TestCase来进行，而无需每次都人肉 等 我决定他下一个任务我和他结对编程。 行百里者半九十，古人诚不欺我。]]></description>
			<content:encoded><![CDATA[<p>2010-03-09  昨天雪，今天冷</p>
<p>昨天已经有童鞋提测了，今天测试MM开始了基于单元测试的测试流程，明天我们就可以拿到一个对比的报告了，很期待。第一个完整流程指日可待。</p>
<p>今天遇到的一个情况是测试驱动开发的反复。</p>
<ul>
<li>有童鞋对测试用例有了一些疑问</li>
<li>有童鞋恢复到了以前的模式，先写功能性代码，然后用用例才测试</li>
</ul>
<p>疑问是这样的，他在开发中，遇到了大量的遗留代码的问题，这时候在对已有代码的信任度上已经产生了质疑，他总是觉得要覆盖更多的内容才放心，但是这样却觉得UnitTest写起来没完没了了。<br />
我对这个的回答是从两个方面进行的</p>
<ol>
<li>因为这次是第一次采用UnitTest，所以一定会出现大量的以前藏着的雷被踩到。</li>
<li>已有的代码，我们这次假定是信任的，如果真的有问题，我们暂时绕过去，并告知测试和原有代码维护人员。因为如果不这样我们是无法定义用例覆盖的边界的。而遗留代码的问题，是应该相关的维护者补充对应的测试用例来覆盖的。</li>
</ol>
<p>测试驱动开发是一个循环的事情，坚持住了，往前走就是良性循环，被遗漏的代码越来越少，雷区越来越少，代码质量提升。但是如果往后走，总觉得没时间写，总是有大量的不可覆盖不可测试的代码，每写一点就趟雷，会严重的打击士气，然后就不写了，然后就恶性循环了。</p>
<p>让我觉得非常受打击的一个事情是有些童鞋回到以往的开发模式了。先写功能代码，然后用用例开覆盖功能代码，起到测试的作用。<br />
我记得我多次提过这样的危害，但是还有童鞋跑了回去，晚上只好那他开刀进行CodeReview。结果发现了一些设计和场景层面没有照顾到的地方。在此，我再说一次用 UnitTest 会带来的好处，当然这里只是说了一些对于开发者最明显的好处</p>
<ul>
<li>用用例覆盖场景，因为我们考虑的是场景并且有测试人员配合，所以我们不容易遗漏场景</li>
<li>写TestCase是在进行设计，从使用者的角度，从API设计者的角度去看代码，会让设计更加自然</li>
<li>用功能性代码覆盖用例，功能性代码有保障</li>
<li>测试时期发生Bug容易定位</li>
<li>为测试节省时间，比如边界测试等都可以通过TestCase来进行，而无需每次都人肉</li>
<li>等</li>
</ul>
<p>我决定他下一个任务我和他结对编程。</p>
<p>行百里者半九十，古人诚不欺我。</p>
]]></content:encoded>
			<wfw:commentRss>http://yinwm.com/2010/03/project-mossad-day-9/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>一个人的项目&#8212;-第二天</title>
		<link>http://yinwm.com/2008/09/%e4%b8%80%e4%b8%aa%e4%ba%ba%e7%9a%84%e9%a1%b9%e7%9b%ae-%e7%ac%ac%e4%ba%8c%e5%a4%a9/</link>
		<comments>http://yinwm.com/2008/09/%e4%b8%80%e4%b8%aa%e4%ba%ba%e7%9a%84%e9%a1%b9%e7%9b%ae-%e7%ac%ac%e4%ba%8c%e5%a4%a9/#comments</comments>
		<pubDate>Wed, 03 Sep 2008 03:49:46 +0000</pubDate>
		<dc:creator>yinwm</dc:creator>
				<category><![CDATA[程序设计]]></category>
		<category><![CDATA[项目管理]]></category>
		<category><![CDATA[scrum]]></category>
		<category><![CDATA[TDD]]></category>

		<guid isPermaLink="false">http://yinwm.cn/wordpress/?p=49</guid>
		<description><![CDATA[今天继续很早睡了，凌晨4点，够早吧。 完成了第一版的几乎所有功能，当然仅仅是完成了功能。在使用ScrumWorks进行自我管理的时候还是可以的，但是自己一个人就有一个大问题，就是基本上想到了什么任务就做什么任务，而没有从最初就设定一系列的任务然后自我分配，这就是项目管理的最大的问题。我想也许是因为一个人的缘故吧。但是这里面存在的一个问题就是，这样干到哪想到哪的方式，是很混乱的，几乎就是没有管理嘛。要非说有，也是存在于一个自己脑子里面的一时干不完的TODO list。 为了改变这个龌龊的做法，我对自己进行了如下的要求。 设定最终完成要达到的状态，也就是做的这个东西要满足什么需求 根据这些需求拆分功能和组件 设定Milestone，每一个Milestone要完成什么东西 根据情况设定工作时长，每一个Milestone的完成时间（这个可以酌情） 使用一个工具记录下来这些 Just Do It 这里面的几个难点 针对上面的要求1，很多时候我们是做不到的，也就是我们在做一个东西的时候，大多的时候只有一个笼统的想法，和最后我要把这个东西做成一个什么样子。但是具体的是什么，也许我们并说不清楚。这个东西越大，越说不清楚。所以，我要求自己停下来，好好的想，我到底要一个什么东西，我要干什么，要实现什么功能，要满足我什么需求，这个一定要想清楚。 针对上面的要求2，我们大多时候，是不能非常好的进行拆分的，我们缺乏的是经验，没办法，多做吧。 针对上面的5，一定要用一个工具记录下来，因为你不记录下来，没有监督，那不就是自我安慰了么。现在的工具很多，Trac，Redmine，很多很多的。最不济的，记事本你总有吧，纸笔你总有吧。 在这个为期两天的一个土鳖小项目里面，我发现工具的重要性，我现在是Bug，REF，什么的都放在Trac上面，但是我的Task，时间管理都是使用ScrumWorks（而且这个还是一个试用版）。我没有找到一个能够把他们都管理起来并且很容易结合的东西。对了，Trac 还不支持bzr。 看来没办法，只能自己写了。现在正在缓慢的进行一个项目，就是做一个完整的 Ticket，代码，时间管理的工具，最好天然的支持Scrum。]]></description>
			<content:encoded><![CDATA[<p>今天继续很早睡了，凌晨4点，够早吧。</p>
<p>完成了第一版的几乎所有功能，当然仅仅是完成了功能。在使用ScrumWorks进行自我管理的时候还是可以的，但是自己一个人就有一个大问题，就是基本上想到了什么任务就做什么任务，而没有从最初就设定一系列的任务然后自我分配，这就是项目管理的最大的问题。我想也许是因为一个人的缘故吧。<br />但是这里面存在的一个问题就是，这样干到哪想到哪的方式，是很混乱的，几乎就是没有管理嘛。要非说有，也是存在于一个自己脑子里面的一时干不完的TODO list。</p>
<p>为了改变这个龌龊的做法，我对自己进行了如下的要求。
<ol>
<li>设定最终完成要达到的状态，也就是做的这个东西要满足什么需求</li>
<li>根据这些需求拆分功能和组件</li>
<li>设定Milestone，每一个Milestone要完成什么东西</li>
<li>根据情况设定工作时长，每一个Milestone的完成时间（这个可以酌情）</li>
<li>使用一个工具记录下来这些</li>
<li>Just Do It</li>
</ol>
<p>这里面的几个难点
<ol>
<li>针对上面的要求1，很多时候我们是做不到的，也就是我们在做一个东西的时候，大多的时候只有一个笼统的想法，和最后我要把这个东西做成一个什么样子。但是具体的是什么，也许我们并说不清楚。这个东西越大，越说不清楚。<br />所以，我要求自己停下来，好好的想，我到底要一个什么东西，我要干什么，要实现什么功能，要满足我什么需求，这个一定要想清楚。</li>
<li>针对上面的要求2，我们大多时候，是不能非常好的进行拆分的，我们缺乏的是经验，没办法，多做吧。</li>
<li>针对上面的5，一定要用一个工具记录下来，因为你不记录下来，没有监督，那不就是自我安慰了么。现在的工具很多，Trac，Redmine，很多很多的。最不济的，记事本你总有吧，纸笔你总有吧。</li>
</ol>
<p>在这个为期两天的一个土鳖小项目里面，我发现工具的重要性，我现在是Bug，REF，什么的都放在Trac上面，但是我的Task，时间管理都是使用ScrumWorks（而且这个还是一个试用版）。我没有找到一个能够把他们都管理起来并且很容易结合的东西。对了，Trac 还不支持bzr。</p>
<p>看来没办法，只能自己写了。<br />现在正在缓慢的进行一个项目，就是做一个完整的 Ticket，代码，时间管理的工具，最好天然的支持Scrum。</p>
<p></p>
]]></content:encoded>
			<wfw:commentRss>http://yinwm.com/2008/09/%e4%b8%80%e4%b8%aa%e4%ba%ba%e7%9a%84%e9%a1%b9%e7%9b%ae-%e7%ac%ac%e4%ba%8c%e5%a4%a9/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>一个人的项目&#8212;-第一天</title>
		<link>http://yinwm.com/2008/09/%e4%b8%80%e4%b8%aa%e4%ba%ba%e7%9a%84%e9%a1%b9%e7%9b%ae-%e7%ac%ac%e4%b8%80%e5%a4%a9/</link>
		<comments>http://yinwm.com/2008/09/%e4%b8%80%e4%b8%aa%e4%ba%ba%e7%9a%84%e9%a1%b9%e7%9b%ae-%e7%ac%ac%e4%b8%80%e5%a4%a9/#comments</comments>
		<pubDate>Mon, 01 Sep 2008 23:13:36 +0000</pubDate>
		<dc:creator>yinwm</dc:creator>
				<category><![CDATA[程序设计]]></category>
		<category><![CDATA[项目管理]]></category>
		<category><![CDATA[scrum]]></category>
		<category><![CDATA[TDD]]></category>

		<guid isPermaLink="false">http://yinwm.cn/wordpress/?p=48</guid>
		<description><![CDATA[现在在给一个自己的网站 http://www.yes7080.com 写一个类似于百度贴吧的功能。当前合作者出去和老婆Happy了，暂时项目只有我自己一个人。想想，也不能瞎写，就使用一些东西把自己管理起来吧。 刚刚看完Scrum，发现和我平常期望的，理想的，使用的方法很像很像（看来我有慧根）。所以打算自己一个人试试看。并结合TDD等方法，把自己管起来。 毕竟，刚刚入门，而且一个人，难免有东施效颦，丢三落四的情况出现。 第一天，也就是今天凌晨1，项目初创开始新项目，使用bzr开启一个新的repository，开工。采用TDD，构建一个最简单的，最土鳖的测试用例，不要求很完整，只是一个对大多数常用功能的简单使用，目的是为了尽快的搭建出项目原型和框架。遵照Uncle Bob的谆谆教诲，在完成一个土鳖测试用例之后，成功的让Eclipse画出来一坨坨的小红线（编译不过嘛）。然后撰写真正的Interface，还有Abstract Class，真不错，终于编译成功。 收获：这个过程并不是一个浪费时间的过程，而是结合了思考，设计，具体请参见我的《初涉敏捷编程》。这过程中出现了很多的对API的推敲，对类的职能的思考。而且，在写真正的接口的时候，可以不用过多的考虑用不到的方法，也就是我总说的，关注一个类的职能。Duty Oriented. 2，开始功能实现有了用例和接口，就可以写实现了。因为项目的特性，一个只读的数据存储结构是最合适的。但是无奈项目时间紧迫，所以我没有这么充裕的时间去选型，甚至设计实现一个适合这个场景的存储结构。所以我把实现分成了多个部分，第一部分，也就是使用传统的数据库作为存储，这样可以最快的实现功能，并且满足现在访问量并不大的情况。以后随着访问量的增加以及时间的充裕，可以继续完善甚至更改存储体系。 醒目的目录结构是这样的./src./src/com/yes7080/saybar （存放所有的接口，并且这是使用时候的统一入口）./src/com/yes7080/saybar/impl （存放实现）./src/com/yes7080/saybar/impl/datamodel （存放使用datamodel，亦即使用数据库的实现）./test./test/src./test/src/com/yes7080/test/saybar （测试代码）实现使用了二级目录结构，也就是使用impl/datamodel，这样的目的就是因为以后可能出现其他的存储方式。而且，正因为这样从目录解耦，就可以更好的进行设计了（具体的设计思想，我以后会撰文解说的）。 很爽的写了很多，很土鳖的没有采用任何的项目管理。工具使用Trac，但是更多的是一个用于文档和Ticket跟踪。所以我决定对自己采用Scrum进行管理。也对自己有一个监控。 最开始，实话实说，我真的忘记了Scrum具体应该怎么开始了，但是非常简单，用户故事（是这样么说么）只有一个，就是实现一个用数据库的存储。我给自己两天的时间，也就是后天天亮之前完成，大约是还有10-16小时的工作时间。然后在看情况进行修改吧。嘿嘿~暂时给自己定一个时间，然后，继续工作鸟~~]]></description>
			<content:encoded><![CDATA[<p>现在在给一个自己的网站 <a href="http://www.yes7080.com/" target="_blank">http://www.yes7080.com</a> 写一个类似于百度贴吧的功能。当前合作者出去和老婆Happy了，暂时项目只有我自己一个人。想想，也不能瞎写，就使用一些东西把自己管理起来吧。</p>
<p>刚刚看完Scrum，发现和我平常期望的，理想的，使用的方法很像很像（看来我有慧根）。所以打算自己一个人试试看。并结合TDD等方法，把自己管起来。</p>
<p>毕竟，刚刚入门，而且一个人，难免有东施效颦，丢三落四的情况出现。</p>
<p>第一天，也就是今天凌晨<br />1，项目初创<br />开始新项目，使用bzr开启一个新的repository，开工。<br />采用TDD，构建一个最简单的，最土鳖的测试用例，不要求很完整，只是一个对大多数常用功能的简单使用，目的是为了尽快的搭建出项目原型和框架。<br />遵照Uncle Bob的谆谆教诲，在完成一个土鳖测试用例之后，成功的让Eclipse画出来一坨坨的小红线（编译不过嘛）。然后撰写真正的Interface，还有Abstract Class，真不错，终于编译成功。</p>
<p>收获：这个过程并不是一个浪费时间的过程，而是结合了思考，设计，具体请参见我的《<a href="http://yinwm.cn/blog/2008/04/agile-programming-on-first-touch.html" target="_blank">初涉敏捷编程</a>》。这过程中出现了很多的对API的推敲，对类的职能的思考。<br />而且，在写真正的接口的时候，可以不用过多的考虑用不到的方法，也就是我总说的，关注一个类的职能。Duty Oriented.</p>
<p>2，开始功能实现<br />有了用例和接口，就可以写实现了。因为项目的特性，一个只读的数据存储结构是最合适的。但是无奈项目时间紧迫，所以我没有这么充裕的时间去选型，甚至设计实现一个适合这个场景的存储结构。所以我把实现分成了多个部分，第一部分，也就是使用传统的数据库作为存储，这样可以最快的实现功能，并且满足现在访问量并不大的情况。以后随着访问量的增加以及时间的充裕，可以继续完善甚至更改存储体系。</p>
<p>醒目的目录结构是这样的<br />./src<br />./src/com/yes7080/saybar （存放所有的接口，并且这是使用时候的统一入口）<br />./src/com/yes7080/saybar/impl （存放实现）<br />./src/com/yes7080/saybar/impl/datamodel （存放使用datamodel，亦即使用数据库的实现）<br />./test<br />./test/src<br />./test/src/com/yes7080/test/saybar （测试代码）<br />实现使用了二级目录结构，也就是使用impl/datamodel，这样的目的就是因为以后可能出现其他的存储方式。而且，正因为这样从目录解耦，就可以更好的进行设计了（具体的设计思想，我以后会撰文解说的）。</p>
<p>很爽的写了很多，很土鳖的没有采用任何的项目管理。工具使用Trac，但是更多的是一个用于文档和Ticket跟踪。<br />所以我决定对自己采用Scrum进行管理。也对自己有一个监控。</p>
<p>最开始，实话实说，我真的忘记了Scrum具体应该怎么开始了，但是非常简单，用户故事（是这样么说么）只有一个，就是实现一个用数据库的存储。我给自己两天的时间，也就是后天天亮之前完成，大约是还有10-16小时的工作时间。然后在看情况进行修改吧。嘿嘿~<br />暂时给自己定一个时间，然后，继续工作鸟~~</p>
]]></content:encoded>
			<wfw:commentRss>http://yinwm.com/2008/09/%e4%b8%80%e4%b8%aa%e4%ba%ba%e7%9a%84%e9%a1%b9%e7%9b%ae-%e7%ac%ac%e4%b8%80%e5%a4%a9/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>初涉敏捷编程</title>
		<link>http://yinwm.com/2008/04/%e5%88%9d%e6%b6%89%e6%95%8f%e6%8d%b7%e7%bc%96%e7%a8%8b/</link>
		<comments>http://yinwm.com/2008/04/%e5%88%9d%e6%b6%89%e6%95%8f%e6%8d%b7%e7%bc%96%e7%a8%8b/#comments</comments>
		<pubDate>Wed, 30 Apr 2008 23:42:40 +0000</pubDate>
		<dc:creator>yinwm</dc:creator>
				<category><![CDATA[程序设计]]></category>
		<category><![CDATA[agile]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[TDD]]></category>
		<category><![CDATA[极限编程]]></category>

		<guid isPermaLink="false">http://yinwm.cn/wordpress/?p=47</guid>
		<description><![CDATA[我们都知道一个设计良好的程序是优雅的，我们也知道单元测试是很重要的，有充足的单元测试就可以尽可能的保证一个程序模块的正确性。这两部分是一直困扰我的部分，如何保证一个设计是尽可能的优雅的，还有就是让单元测试测试到尽可能多的测试点。以前的做法都是提前对程序进行设计，然后进行编码，然后编写单元测试代码，再根据觉得哪里不妥修改代码，修改测试用例。 这里我遇到的问题是 设计阶段不能设计的足够精细这一点非常令我恼火，有的想要给别人看的代码，比如开源，要求你的API设计来不得半点马虎，要设计的很合理。前期的脑纸笔的设计方式，会让细节偏差很大。而合理的API基本上都是通过不断的编码得到的。 单元测试用例并不能覆盖几乎100%的代码当你写好了代码之后，你就懒得写测试用例了，尤其是针对一些不总用到的功能。而写出的用例大多都是一个非常完整的场景，而不是针对某一个小模块的测试。针对性不强。 针对这两个让我极其愤恨的问题，我决定使用敏捷编程，测试先行。 我对测试的看法一直是测试（我承认这是一句废话），我以前都是写了代码（也就过了设计阶段）再写用例，这样程序的API设计大都是凭借对代码的理解捏造出来的，理解的深，功力深，API设计的就相对好一些，理解的不够深，捏造的成分就大，后期自己看着都恶心。但是Kent Beck和Uncle Bob告诉我们测试用例就是在进行程序的设计。其实，一个好用的API，都是经过实践检验的，这样自然要不断的编写和修改代码，而我们把这个过程提前，放在最初要设计的测试用例里面。一个测试用例，就是对一组API的调用的描述，也就是场景重现，这阶段已经就是在使用自己的API，也就是检验自己的API。当然，最初的这部分编码连编译都无法通过，因为你要求的所有东西都没有，但也正因为如此，你才能按照你最舒服的调用方式调用API，而不是受到什么限制。 例如 Query query = xxDAO.createQuery(&#8220;Target&#8221;); query.addExpression(Expression.eq(&#8220;name&#8221;, &#8220;Testing&#8221;); List l = xxDAO.doQuery(query) 这时候，我们没有Query，没有addExpression，没有Expression，什么都没有，但是这代码看着很自然。这就说明如果我们这样设计API就还算不差。下一步很自然的，我们要让这部分代码编译通过，增加Query这个interface[1]，然后完成编译。 我认为，到了这个阶段，程序的设计已经完成了80%了，剩下的就是看哪里能够变得更加合理，让臭味更小了。至于所有的实现类的编码，我觉得就是体力劳动了，虽然体力劳动也很累很耗时，但毕竟能够找到解决方法。这时候剩下的事情就简单多了，编写真正的实现类代码，让单元测试通过。 我们应该在编写真正的代码的时候，都是针对测试用例里面使用的API进行编码。这句话的意思就是，我们编写出的方法，应该是测试用例用到的，而不是额外的编写一些暂时没有用到的代码。如果这些将要编写的类/方法没有被测试用例覆盖到，那么要么是这些类/方法暂时没有需求，要么就是测试用例覆盖度不够。哪怕是一个简单的setter/getter，也要有测试用例直接或者间接覆盖到。之所以这样，只是为了避免破窗原理，一个没写，就会有第二个，就会有第n个。 [1]：之所以增加的是interface，就是因为增加的interface更加抽象，行为更加的单纯，并且在测试的时候，可以充分的使用Mock。但是这不是绝对的，在一些纯粹的逻辑的东西，就完全是一个class就能代替的，而无需interface，比如例子中的Expression，用class就比interface好很多]]></description>
			<content:encoded><![CDATA[<p><meta http-equiv="CONTENT-TYPE" content="text/html; charset=utf-8"><title></title><meta name="GENERATOR" content="OpenOffice.org 2.4  (Win32)"><br />
<style type="text/css">
<!--
@page { size: 21cm 29.7cm; margin: 2cm }
P { margin-bottom: 0.21cm }
-->
</style>
<p style="margin-bottom: 0cm;">
<p style="margin-bottom: 0cm;">我们都知道一个设计良好的程序是优雅的，我们也知道单元测试是很重要的，有充足的单元测试就可以尽可能的保证一个程序模块的正确性。这两部分是一直困扰我的部分，如何保证一个设计是尽可能的优雅的，还有就是让单元测试测试到尽可能多的测试点。以前的做法都是提前对程序进行设计，然后进行编码，然后编写单元测试代码，再根据觉得哪里不妥修改代码，修改测试用例。</p>
<p style="margin-bottom: 0cm;">这里我遇到的问题是</p>
<ol>
<li>
<p style="margin-bottom: 0cm;">设计阶段不能设计的足够精细<font face="Times New Roman, serif"><br /></font>这一点非常令我恼火，有的想要给别人看的代码，比如开源，要求你的<font face="Times New Roman, serif">API</font>设计来不得半点马虎，要设计的很合理。前期的脑纸笔的设计方式，会让细节偏差很大。而合理的<font face="Times New Roman, serif">API</font>基本上都是通过不断的编码得到的。</p>
</li>
<li>
<p style="margin-bottom: 0cm;">单元测试用例并不能覆盖几乎<font face="Times New Roman, serif">100%</font>的代码<font face="Times New Roman, serif"><br /></font>当你写好了代码之后，你就懒得写测试用例了，尤其是针对一些不总用到的功能。而写出的用例大多都是一个非常完整的场景，而不是针对某一个小模块的测试。针对性不强。</p>
</li>
</ol>
<p style="margin-bottom: 0cm;">针对这两个让我极其愤恨的问题，我决定使用敏捷编程，测试先行。</p>
<p style="margin-bottom: 0cm;">我对测试的看法一直是测试（我承认这是一句废话），我以前都是写了代码（也就过了设计阶段）再写用例，这样程序的<font face="Times New Roman, serif">API</font>设计大都是凭借对代码的理解捏造出来的，理解的深，功力深，<font face="Times New Roman, serif">API</font>设计的就相对好一些，理解的不够深，捏造的成分就大，后期自己看着都恶心。但是<font face="Times New Roman, serif">Kent<br />
Beck</font>和<font face="Times New Roman, serif">Uncle<br />
Bob</font>告诉我们测试用例就是在进行程序的设计。其实，一个好用的<font face="Times New Roman, serif">API</font>，都是经过实践检验的，这样自然要不断的编写和修改代码，而我们把这个过程提前，放在最初要设计的测试用例里面。一个测试用例，就是对一组<font face="Times New Roman, serif">API</font>的调用的描述，也就是场景重现，这阶段已经就是在使用自己的<font face="Times New Roman, serif">API</font>，也就是检验自己的<font face="Times New Roman, serif">API</font>。当然，最初的这部分编码连编译都无法通过，因为你要求的所有东西都没有，但也正因为如此，你才能按照你最舒服的调用方式调用<font face="Times New Roman, serif">API</font>，而不是受到什么限制。</p>
<p style="margin-bottom: 0cm;">例如</p>
<p style="margin-bottom: 0cm;"><font face="Times New Roman, serif">Query<br />
query = xxDAO.createQuery(&#8220;Target&#8221;);</font></p>
<p style="margin-bottom: 0cm;"><font face="Times New Roman, serif">query.addExpression(Expression.eq(&#8220;name&#8221;,<br />
&#8220;Testing&#8221;);</font></p>
<p style="margin-bottom: 0cm;"><font face="Times New Roman, serif">List<br />
l = xxDAO.doQuery(query)</font></p>
<p style="margin-bottom: 0cm;">这时候，我们没有<font face="Times New Roman, serif">Query</font>，没有<font face="Times New Roman, serif">addExpression</font>，没有<font face="Times New Roman, serif">Expression</font>，什么都没有，但是这代码看着很自然。这就说明如果我们这样设计<font face="Times New Roman, serif">API</font>就还算不差。下一步很自然的，我们要让这部分代码编译通过，增加<font face="Times New Roman, serif">Query</font>这个<font face="Times New Roman, serif">interface[1]</font>，然后完成编译。</p>
<p style="margin-bottom: 0cm;">我认为，到了这个阶段，程序的设计已经完成了<font face="Times New Roman, serif">80%</font>了，剩下的就是看哪里能够变得更加合理，让臭味更小了。至于所有的实现类的编码，我觉得就是体力劳动了，虽然体力劳动也很累很耗时，但毕竟能够找到解决方法。这时候剩下的事情就简单多了，编写真正的实现类代码，让单元测试通过。</p>
<p style="margin-bottom: 0cm;">我们应该在编写真正的代码的时候，都是针对测试用例里面使用的<font face="Times New Roman, serif">API</font>进行编码。这句话的意思就是，我们编写出的方法，应该是测试用例用到的，而不是额外的编写一些暂时没有用到的代码。如果这些将要编写的类<font face="Times New Roman, serif">/</font>方法没有被测试用例覆盖到，那么要么是这些类<font face="Times New Roman, serif">/</font>方法暂时没有需求，要么就是测试用例覆盖度不够。哪怕是一个简单的<font face="Times New Roman, serif">setter/getter</font>，也要有测试用例直接或者间接覆盖到。之所以这样，只是为了避免破窗原理，一个没写，就会有第二个，就会有第<font face="Times New Roman, serif">n</font>个。</p>
<p style="margin-bottom: 0cm;">
</p>
<p style="margin-bottom: 0cm;">
</p>
<p style="margin-bottom: 0cm;">
</p>
<p style="margin-bottom: 0cm;"><font face="Times New Roman, serif">[1]</font>：之所以增加的是<font face="Times New Roman, serif">interface</font>，就是因为增加的<font face="Times New Roman, serif">interface</font>更加抽象，行为更加的单纯，并且在测试的时候，可以充分的使用<font face="Times New Roman, serif">Mock</font>。但是这不是绝对的，在一些纯粹的逻辑的东西，就完全是一个<font face="Times New Roman, serif">class</font>就能代替的，而无需<font face="Times New Roman, serif">interface</font>，比如例子中的<font face="Times New Roman, serif">Expression</font>，用<font face="Times New Roman, serif">class</font>就比<font face="Times New Roman, serif">interface</font>好很多</p>
<p style="margin-bottom: 0cm;"></p>
]]></content:encoded>
			<wfw:commentRss>http://yinwm.com/2008/04/%e5%88%9d%e6%b6%89%e6%95%8f%e6%8d%b7%e7%bc%96%e7%a8%8b/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

