<?xml version="1.0" encoding="GB2312"?>   
<rdf:RDF 
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" 
xmlns:dc="http://purl.org/dc/elements/1.1/" 
xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" 
xmlns:admin="http://webns.net/mvcb/" 
xmlns:cc="http://web.resource.org/cc/" 
xmlns="http://purl.org/rss/1.0/"> 

<channel rdf:about="http://ronaldmatt.bokee.com/index.html"> 
<title><![CDATA[纯粹物件导向空间]]></title> 
<link>http://ronaldmatt.bokee.com/index.html</link> 
<description><![CDATA[Object-Oriented
Funtional Programming
Common Lisp / Scheme
Haskell
Self-Help]]></description> 
<dc:language>zh-cn</dc:language> 
<dc:creator>RonaldMatt</dc:creator> 
<dc:date>2007-11-08T15:51:48Z</dc:date> 
<admin:generatorAgent rdf:resource="http://blog.bokee.com/" /> 

<items> 
<rdf:Seq>
<rdf:li rdf:resource="http://ronaldmatt.bokee.com/6523508.html" />
<rdf:li rdf:resource="http://ronaldmatt.bokee.com/6518007.html" />
<rdf:li rdf:resource="http://ronaldmatt.bokee.com/6516038.html" />
<rdf:li rdf:resource="http://ronaldmatt.bokee.com/6508042.html" />
<rdf:li rdf:resource="http://ronaldmatt.bokee.com/6358109.html" />
<rdf:li rdf:resource="http://ronaldmatt.bokee.com/6339562.html" />
<rdf:li rdf:resource="http://ronaldmatt.bokee.com/6327108.html" />
<rdf:li rdf:resource="http://ronaldmatt.bokee.com/6326463.html" />
<rdf:li rdf:resource="http://ronaldmatt.bokee.com/6319041.html" />
<rdf:li rdf:resource="http://ronaldmatt.bokee.com/6299477.html" />
</rdf:Seq> 
</items> 

</channel>


<item rdf:about="http://ronaldmatt.bokee.com/6523508.html"> 
<title><![CDATA[UnitTestingAbstractionLevel]]></title> 
<link>http://ronaldmatt.bokee.com/6523508.html</link> 
<description><![CDATA[<p>单元测试可以让你验证你的函数的抽象或者封装的层次，譬如我写了一个函数：</p><p>foo(input, output)，函数内部有一个buffer是1024，如果要测试覆盖所有代码，我的测试size一定要大于1024，光在测试里面构造出一个大于1024的input就会很奇怪，因为这个细节已经被foo封装掉了。所以通过编写测试，你要调整自己的封装，就是把bufferSize暴露出来，这样你的测试的输入的size才有意义：foo(bufferSize, input, output)；那么测试的行为就很好理解了。</p><p />]]></description> 
<dc:subject><![CDATA[pondering]]></dc:subject> 
<dc:creator><![CDATA[RonaldMatt]]></dc:creator> 
<dc:date>2007-11-08T15:51:46Z</dc:date> 
</item> 
<item rdf:about="http://ronaldmatt.bokee.com/6518007.html"> 
<title><![CDATA[AssertionImplication]]></title> 
<link>http://ronaldmatt.bokee.com/6518007.html</link> 
<description><![CDATA[<p>TDD一个东西很有启发，但是很突兀，不知道这个思想哪里来的：</p><p>如果你测试写Assert.IsEmpty(foo)，那么你首先可一先让foo是null，而不是返回一个empty的东西。这个容易理解，但是却显得没有任何推理。其实IsEmpty逻辑上蕴含了IsNotNull了。如果你在实现Assert.IsEmpty的里面先调用一下Assert.IsNotNull，那么先判断的就是null而不是empty，而foo返回null就很顺理成章了。</p>]]></description> 
<dc:subject><![CDATA[pondering]]></dc:subject> 
<dc:creator><![CDATA[RonaldMatt]]></dc:creator> 
<dc:date>2007-11-04T10:17:25Z</dc:date> 
</item> 
<item rdf:about="http://ronaldmatt.bokee.com/6516038.html"> 
<title><![CDATA[ArchitecturalBugs]]></title> 
<link>http://ronaldmatt.bokee.com/6516038.html</link> 
<description><![CDATA[<p>最近做的两个项目，看到一些bug，其实都是架构上面的问题：</p><p>1，日期输入框没有对日期格式进行验证，分析：为什么要用一个一般输入框呢？就不能有一个日期输入控件吗？</p><p>2，数据库字段可以为空，但是代码的Domain层没有考虑那个字段为空的情况，分析：这个为什么不能通过类似Haskell的Maybe类型系统来解决呢？为什么我们要把Nullable的这个知识分布在数据库，ORM，Domain层呢？我们应该可以通过某些方法把它们集中到一个权威的地方。</p><p>3，很多bug很难重现，分析：为什么数据库不设计成只能加记录不能删记录呢？（一种funtional的数据库设计？）就好像财务记账一样，不用橡皮擦，这样我们就能够回到任何一个系统状态。那么重现bug就是小菜一碟了。</p><p>4，为什么输入的地方总是缺乏格式验证？分析：为什么就不能把验证集中表现到domain对象哪里，然后用到表现层？就像RoR一样。</p><p>从上面的例子可以看出，很多bug都不仅仅能归咎于疏忽或者流程，而是我们现在的应用架构的缺陷导致的。你修复bug的时候不能只见树木不见森林。</p>]]></description> 
<dc:subject><![CDATA[pondering]]></dc:subject> 
<dc:creator><![CDATA[RonaldMatt]]></dc:creator> 
<dc:date>2007-11-02T10:26:54Z</dc:date> 
</item> 
<item rdf:about="http://ronaldmatt.bokee.com/6508042.html"> 
<title><![CDATA[UnscalableSolutions]]></title> 
<link>http://ronaldmatt.bokee.com/6508042.html</link> 
<description><![CDATA[<p>最近不断碰到UnscalableSolutions，不得不写一下，今天碰到一个：html问题，如果你要给一个页面加css 的link，那么你必须加到head中间，但如果你使用控件，譬如ascx，那么为了满足封装性，控件的css应该不需要被控件的使用者知道，但对于html标准来说，这是不允许的。</p><p>所以html标准是一个UnscalableSolutions，它之所以设计成这样是为了减少html解释器实现的复杂性。而把复杂性推给了生成html的代码。而开发人员就是生活在各种UnscalableSolutions或者平台之中的。这就是开发Scalable Application的其中一个难度。</p>]]></description> 
<dc:subject><![CDATA[pondering]]></dc:subject> 
<dc:creator><![CDATA[RonaldMatt]]></dc:creator> 
<dc:date>2007-10-26T16:09:53Z</dc:date> 
</item> 
<item rdf:about="http://ronaldmatt.bokee.com/6358109.html"> 
<title><![CDATA[TddIsFolding]]></title> 
<link>http://ronaldmatt.bokee.com/6358109.html</link> 
<description><![CDATA[<p>你思考程序的解释执行你就会看到一个成形的程序是不断被进行递归解释而进行求值的，整个过程你可以说是一个展开的过程，譬如fib 10展开成fib 9跟fib 8的表现形式，如此一直展开下去，而你编程就是去编写那些展开的规则，而这就是容易出错的地方，TDD,就是把展开的一些步骤先掠过，譬如直接return一个数值0，那么return 0就可以认为是return foo.length的展开后的值，而随着TDD，你会逐渐吧return 0的直接形式变成需要更多解释的return foo.length的形式，这就是一个fold的过程，或者叫construction，而程序的执行则是intepretation。如果你能领悟到这一点，TDD就是小菜一碟了。</p>]]></description> 
<dc:subject><![CDATA[pondering]]></dc:subject> 
<dc:creator><![CDATA[RonaldMatt]]></dc:creator> 
<dc:date>2007-07-05T18:30:05Z</dc:date> 
</item> 
<item rdf:about="http://ronaldmatt.bokee.com/6339562.html"> 
<title><![CDATA[UnitTestingConfiguration]]></title> 
<link>http://ronaldmatt.bokee.com/6339562.html</link> 
<description><![CDATA[<p>配置和编程的本质区别是什么？</p><p>为什么有的代码很难单元测试？</p><p>配置可以单元测试吗？</p><p>什么东西才需要单元测试？</p><p>什么是组件编程？</p><p>什么是Convention over configuration?它们两者有什么区别？</p><p>为什么Web Container是一个白痴的想法？</p><p>首先，配置和编程就其形式而言没有根本区别，你可以用数据形式编程，也可以用代码形式配置，配置跟代码是一回事，配置活动和编程活动是一回事。但似乎配置和编程还是有根本的区别呀？区别就是，配置完全就是为了正确地链接它外部的系统或者组件或者模块（随便你叫什么），所以纯粹的配置是不能被单元测试的，因为要验证它的正确性，必须把它链接的所有东西作为一个“单元”来进行验证，仅仅验证配置本身是没有任何意义的，譬如你验证一个链接字符串有什么意义呢？你要看系统是否能够连到数据库，才是真正的验证。当然连接字符串是否符合一个schma，那是可以验证的，可以成为链接字符串的“单元测试”？</p><p>我们再看看编程或者代码，理想状况下，你写的那个“单元”，很可能是跟其他部分没有依赖的，譬如你要写一个堆栈的抽象数据类型，那么它可能跟这个系统99.9%的东西都答不上边，这时，你谈论配置是没有意义的，“堆栈”的正确性完全是它自己作为一个整体是否正确，而这种东西是可以被单元测试的。而这种理想状况只是在系统被设计得很模块化的时候才会出现，往往你写的一个“单元”，有它自己的一些逻辑，但同时又混进了一些“配置”的东西在里面。你的系统设计得越不模块化，你混进的“配置”噪声就越多，就导致你的每一个“单元”越难被单元测试。</p><p>组件编程就是希望达到一种模块化，组件是代码，组件的链接是配置。很多人的观念是编程是耗费时间的，配置是不大耗费时间的，所以配置都是好的，但如果配置巨复杂，你的开发效率也会很低下，这就是Java什么狗屁都要配置的恶果。不必要地加入配置，就不必要地加大了配置验证的工作量。Web Container这类主意，把页面不必要地依赖了一个本来不需要依赖的东西——容器，那么你就不必要地又要加入配置了，还有就是配置的验证，你要不停地redploy，reboot这个容器。</p><p>DHH所谓的Convention over configuration究竟是啥意思？两者为什么有区别？Convention，就是把配置隐含到每个组件里面了，也就是你不需要你个显式的“配置单元”或者配置文件了，你只要把每个组件作对，那么它就自然整合到大的系统里面了，而如果这种隐含是直观的（譬如ActiveRecord），那么作为开发人员就基本上不需要考虑太多配置了。另一方面，如果你要显式地配置，那么你就有很多可能会出错，同时增加了很多不必要的工作量。同时显式配置必定包括了三个部分，配置本身，被链接的两端。而隐式配置只有两个部分，要相对简单。</p>]]></description> 
<dc:subject><![CDATA[pondering]]></dc:subject> 
<dc:creator><![CDATA[RonaldMatt]]></dc:creator> 
<dc:date>2007-06-23T13:00:22Z</dc:date> 
</item> 
<item rdf:about="http://ronaldmatt.bokee.com/6327108.html"> 
<title><![CDATA[CodeBloating]]></title> 
<link>http://ronaldmatt.bokee.com/6327108.html</link> 
<description><![CDATA[做一个很短的私活，不需要考虑什么设计，尤其交付之后提的一些修改的建议，我发现所有修改都是倾向于就地修改，倾向于Quick Fix，你觉得把代码提取到其他地方是一个要动脑的活，最简单的就是让代码倾向于在当前的地方开始肿胀。这是我们这种编程模型的一个问题？如何能够让代码在随便一个地方肿胀又不会增加复杂性？]]></description> 
<dc:subject><![CDATA[pondering]]></dc:subject> 
<dc:creator><![CDATA[RonaldMatt]]></dc:creator> 
<dc:date>2007-06-15T17:23:55Z</dc:date> 
</item> 
<item rdf:about="http://ronaldmatt.bokee.com/6326463.html"> 
<title><![CDATA[AbstractionVsConfusion]]></title> 
<link>http://ronaldmatt.bokee.com/6326463.html</link> 
<description><![CDATA[<p>如果你要建立一个Swing的目录树，那么你不能直接用File因为File的toString是全部路径的，而你只是希望toString返回一个当前路径。那么最简单就是建立一个Node类：</p><p>Node&amp;gt;&amp;gt;<br />&amp;nbsp;&amp;nbsp; file: File<br />&amp;nbsp;&amp;nbsp; toString: ^file.name</p><p>但这只是组合，不是抽象，问题就在于你要同时去折腾Node和File两个概念，尤其在递归的结构里面，更加麻烦，而真正的抽象就是建立一个DirectoryNode抽象</p><p>DirectoryNode&amp;gt;&amp;gt;<br />&amp;nbsp;&amp;nbsp;&amp;nbsp; subDirs<br />&amp;nbsp;&amp;nbsp;&amp;nbsp; roots<br />&amp;nbsp;&amp;nbsp;&amp;nbsp; ...</p><p>这样DirectoryNode的使用者就完全不需要理会File这个类了，或者说DirectoryNode遮蔽了File，这才是抽象，因为我们用一个东西遮蔽着另一个东西，系统至少不需要对付两个概念。提供了更好的模块化，因为文件相关的东西全部被封装到DirectoryNode里面。</p><p>一般人做抽象常犯的错误就是错把confusion当成abstraction，抽象一定是要砍掉东西的，不是往上加东西的。</p>]]></description> 
<dc:subject><![CDATA[pondering]]></dc:subject> 
<dc:creator><![CDATA[RonaldMatt]]></dc:creator> 
<dc:date>2007-06-15T10:33:25Z</dc:date> 
</item> 
<item rdf:about="http://ronaldmatt.bokee.com/6319041.html"> 
<title><![CDATA[TheWhiteBoardMethod]]></title> 
<link>http://ronaldmatt.bokee.com/6319041.html</link> 
<description><![CDATA[<p>当你学习一样全新的学问或者技术的时候，不是所有这里学问都有一个导论的，可能这不是一种学术，可能根本就没完全发展成体系，你只是能够看到一些支离破碎的东西：一些概念，一些方法，一些原则，一些dos &amp;amp; donts，一些案例，等等。那么你如何去对这样的尚未形成体系或者尚未在你脑中形成体系的知识进行组织管理呢？我能知道的就是“白板法”。</p><p>就是把所有的概念，方法等等东西以条目形式一古脑地放在一个白板上（这是侦探常用的破案方法），然后一些东西逐渐开始显现出联系，原来分散的东西开始扎堆，随着你的知识的增长，你开始把这些凌乱的东西理出头绪来。</p><p>白板还可以帮助你标出那些眼下要注意的东西，譬如你这段时间准备重点加强某些方面，那么你可以把那些东西扎堆。</p><p>这个白板我会命名为XYZ Puzzle，就像一个拼图或者谜题一样。你可以用Google Notes或者一些Sticky Notes的工具，甚至像Michael Scofield一样把Stikcy Notes贴到墙上。</p>]]></description> 
<dc:subject><![CDATA[pondering]]></dc:subject> 
<dc:creator><![CDATA[RonaldMatt]]></dc:creator> 
<dc:date>2007-06-10T17:17:58Z</dc:date> 
</item> 
<item rdf:about="http://ronaldmatt.bokee.com/6299477.html"> 
<title><![CDATA[ContextBasedNaming]]></title> 
<link>http://ronaldmatt.bokee.com/6299477.html</link> 
<description><![CDATA[<p>最近挑灯夜读了Kent Beck大侠的Smalltalk Best Practice Patterns Volumn 1: Coding，又重新开始注意命名了，在Intention Revealing Selector里面举了一个例子linerSearchFor -&amp;gt; searchFor -&amp;gt; includes，作者觉得后面的名字要比前面好，作者教的一个方法就是想像这个东西的另外一个实现方式，而我觉得这种方式并不太有效，对我。<br />我最近在折腾FxCop的自定义Rules，从中悟出了一个Kent Beck自己真正使用的命名方式：基于上下文来命名。故事是这样的：<br />FxCop有一个函数Check(xxxx)返回一个ProblemCollection，那么如果你测试，你就要这样写：Assert.IsNotNull(rule.Check(xxx))，而你这样读就就会觉得很怪，这样的代码不成一个句子，Check is not null是不通的，Check是一个动词，在这里。而我重新命名了一下得到一个更好的名字ProblemsOf，这个名字孤立来看，似乎没什么意思，但是如果你把它放回单元测试的上下文，就变成：Assert.IsNotNull(ProblemsOf(xxx))，problems is not null还是比较说得通的。从中得出的经验就是：你永远不能孤立地去命名，这样你永远都命不好，方法就是把这个名字放在它被用到的上下文里面去看它是否能够和这个上下文无缝的结合起来，符合语法，符合习惯，这样你的代码才能向一篇文章那样易读，因为你的脑袋不需要去填充这些名字和名字之间的gap。<br />回到Kent Beck自己的例子Assert.IsTrue(a.searchFor(foo))当然不能符合上下文，而Assert.IsTrue(a.includes(b))，就非常符合上下文了。故此这个名字好。<br />你并不是为了读者能够更容易地理解这个函数的实现而给它命名，你是为了让读者更容易理解函数如何组合在一起而命名，因为后者更难理解。</p>]]></description> 
<dc:subject><![CDATA[hacking]]></dc:subject> 
<dc:creator><![CDATA[RonaldMatt]]></dc:creator> 
<dc:date>2007-05-29T11:10:01Z</dc:date> 
</item> 

  
</rdf:RDF> 