﻿<?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-Read Sean-随笔分类-Programming in General</title><link>http://www.blogjava.net/sean/category/36.html</link><description>Read me, read Sean.</description><language>zh-cn</language><lastBuildDate>Thu, 04 Aug 2011 02:57:40 GMT</lastBuildDate><pubDate>Thu, 04 Aug 2011 02:57:40 GMT</pubDate><ttl>60</ttl><item><title>学好编程，从写作开始。</title><link>http://www.blogjava.net/sean/archive/2011/08/03/355717.html</link><dc:creator>laogao</dc:creator><author>laogao</author><pubDate>Wed, 03 Aug 2011 15:49:00 GMT</pubDate><guid>http://www.blogjava.net/sean/archive/2011/08/03/355717.html</guid><wfw:comment>http://www.blogjava.net/sean/comments/355717.html</wfw:comment><comments>http://www.blogjava.net/sean/archive/2011/08/03/355717.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sean/comments/commentRss/355717.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sean/services/trackbacks/355717.html</trackback:ping><description><![CDATA[<p>
可能很多人不以为然，写作和编程能有什么关系。</p>
<p>
首先，写作促进我们思考。程序员每天的工作，不论是学习新知识新技术、理解软件需求、阅读代码/文档、设计框架、还是实现业务逻辑，都离不开思考。相信很多人都有过这样的体验: 对于某个设计或知识点，你以为自己想清楚了，但真要让你解释给别人听，你又会觉得无从下手，几轮过后回想当初，之前所看到和相信的不过是错觉，当你完整的向其他人转述一遍之后，你才算是真正想明白了。其实这个道理和书桌上放一只橡皮鸭/填充玩偶(然后向它讲述你的想法)的做法是相通的，只不过写作本身不光整理了思路，还留下了文档，同时，写作的过程也是不断加深印象、提升信心的过程，可谓一举多得。</p>
<p>
其次，和写作一样，编程的主要目的是与世界交流，不论这个世界是指的机器的世界还是人的世界。要有效地与机器世界交流，你编写的代码必须符合一定的语法和范式，必须逻辑上讲得通，这样才有意义。而基本的写作训练可以让我们养成主动留意错别字、格式、拼写错误、逻辑错误的习惯。如C. A. R. Haore所说，我们宁要明显无错误的代码，不要无明显错误的代码。这点洁癖是好程序员必须具备的修养。这也是为什么我看到有明显拼写错误的程序员简历时，会直接把他/她们拉黑。同样地，我一直坚持认为，如果程序员写不出结构优良的纯文本文档，那么我们也不必指望他/她能够写出优雅的代码。除此之外，我们还可以再稍微发散一下: 开源项目那么多，满足类似需要的往往不止一个，为什么有的很成功，有的却无人问津? 主创/灵魂人物的写作能力，不论是代码、文档、邮件还是PPT，是很重要的分野。我相信，改变世界、影响更多的人，是很多程序员梦寐以求的，而要做到这一点，离不开写作。</p>
<p>
最后，写作通常是程序员的短板，亦即最容易低成本高产出的地方。我一直很不喜欢人为地给不同专业背景的人贴上文科和理科的标签，仿佛他们之间没有交集似的。这样做的最大问题在我看来是一方天然地觉得另一方的知识对自己没价值，以至于形成了类似"写作是文科生的菜"、"编程是理科才要学的东西"等谬误。就我的观察，计算机科班出身的同学，往往文字表达能力不够强，可能多少和这种心理暗示有关。如此明显的短板，如果能够引起广大程序员群体的重视，肯在写作上持续投入，效果一定是惊人的。</p>
<img src ="http://www.blogjava.net/sean/aggbug/355717.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sean/" target="_blank">laogao</a> 2011-08-03 23:49 <a href="http://www.blogjava.net/sean/archive/2011/08/03/355717.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用Scala解Hanoi塔</title><link>http://www.blogjava.net/sean/archive/2009/11/23/303374.html</link><dc:creator>laogao</dc:creator><author>laogao</author><pubDate>Mon, 23 Nov 2009 11:20:00 GMT</pubDate><guid>http://www.blogjava.net/sean/archive/2009/11/23/303374.html</guid><wfw:comment>http://www.blogjava.net/sean/comments/303374.html</wfw:comment><comments>http://www.blogjava.net/sean/archive/2009/11/23/303374.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/sean/comments/commentRss/303374.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sean/services/trackbacks/303374.html</trackback:ping><description><![CDATA[<p>到处吹嘘Scala很长时间了，却一直没有在自己的blog上增加过任何相关内容，今天就来补一补。当然，Scala的基本特性就不多废话了，网上已经有相当多的资料，如果懒得google，只想看完本文，那么你只需要知道它是一门以JVM为目标运行环境的静态编程语言(当然，Scala官方也有.NET版，但已不是其重点)，同时具备面向对象和函数式编程语言的特点。本文将通过一个简单的示例，展示Scala的FP能力，其中十分heavy的用到了implicit隐式转换和模式匹配。</p>
<p>代码是从guithub的gist上抄的，简单改了改，原始代码见: <a href="http://gist.github.com/66925" target="_blank">http://gist.github.com/66925</a></p>
<p>
</p>
<div style="background-color: #eeeeee; font-size: 13px; border-left-color: #cccccc; padding-right: 5px; padding-bottom: 4px; padding-left: 4px; padding-top: 4px; width: 98%; word-break: break-all; "><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;scala.reflect.Manifest<br />
&nbsp;&nbsp;<br />
def&nbsp;simpleName(m:Manifest[_]):String&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;{<br />
&nbsp;&nbsp;val&nbsp;name&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;m.toString<br />
&nbsp;&nbsp;name.substring(name.lastIndexOf(</span><span style="color: #000000; ">'</span><span style="color: #000000; ">$</span><span style="color: #000000; ">'</span><span style="color: #000000; ">)</span><span style="color: #000000; ">+</span><span style="color: #000000; ">1</span><span style="color: #000000; ">)<br />
}<br />
&nbsp;&nbsp;<br />
trait&nbsp;Num<br />
</span><span style="color: #0000FF; ">final</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;Succ[Pre</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">:Num]&nbsp;</span><span style="color: #0000FF; ">extends</span><span style="color: #000000; ">&nbsp;Num<br />
</span><span style="color: #0000FF; ">final</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;_1&nbsp;</span><span style="color: #0000FF; ">extends</span><span style="color: #000000; ">&nbsp;Num<br />
type&nbsp;_2&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;Succ[_1]<br />
type&nbsp;_3&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;Succ[_2]<br />
type&nbsp;_4&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;Succ[_3]<br />
&nbsp;<br />
</span><span style="color: #0000FF; ">case</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;Move[N</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">:Num,A,B,C]()<br />
<br />
implicit&nbsp;def&nbsp;move1[A,B,C](implicit&nbsp;a:Manifest[A],&nbsp;b:Manifest[B])&nbsp;:&nbsp;Move[_1,A,B,C]&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;{<br />
&nbsp;&nbsp;System.out.println(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Move&nbsp;from&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">+</span><span style="color: #000000; ">&nbsp;simpleName(a)&nbsp;</span><span style="color: #000000; ">+</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;to&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">+</span><span style="color: #000000; ">&nbsp;simpleName(b))<br />
&nbsp;&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000; "><br />
}<br />
&nbsp;<br />
implicit&nbsp;def&nbsp;moveN[P</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">:Num,A,B,C](implicit&nbsp;m1:Move[P,A,C,B],&nbsp;m2:Move[_1,A,B,C],&nbsp;m3:Move[P,C,B,A])&nbsp;:&nbsp;Move[Succ[P],A,B,C]&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000; "><br />
&nbsp;&nbsp;<br />
def&nbsp;run[N</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">:Num,A,B,C](implicit&nbsp;m:Move[N,A,B,C])&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000; "><br />
&nbsp;&nbsp;<br />
</span><span style="color: #0000FF; ">case</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;Left()<br />
</span><span style="color: #0000FF; ">case</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;Center()<br />
</span><span style="color: #0000FF; ">case</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;Right()<br />
&nbsp;&nbsp;<br />
run[_3,Left,Right,Center]<br />
</span></div>
<p>&nbsp;</p>
<p>这段代码解决的是经典的汉诺塔问题，通过一根中柱，将左柱上64个大小依次叠加的圆盘全部移动到右柱，要求整个过程中每次只能移动一个圆盘，且每个圆盘只能独立占据一根柱子或是叠加在比自身更大的圆盘上。</p>
<p>简单分析一下就知道，这是一个递归问题(FP的英雄特长)：</p>
<ul>
    <li>当只有1个圆盘时，不用通过中柱，直接可以从左柱移动到右柱；</li>
    <li>当有2个圆盘时，将小盘移动到中柱，剩下的大盘移动到右柱，再从中柱把小盘移动到右柱；</li>
    <li>当有3个圆盘时，先移动2个圆盘到中柱，再移动大盘到右柱，再移动2个圆盘到右柱；</li>
    <li>...</li>
</ul>
<p>很容易发现一个pattern，那就是移动N(N&gt;1)个圆盘，可以通过以下三个步骤：</p>
<ol>
    <li>移动N-1个圆盘，从左柱到中柱；</li>
    <li>移动剩下的1个圆盘，从左柱到右柱；</li>
    <li>移动N-1个圆盘，从中柱到右柱。</li>
</ol>
<p>在解释代码之前，先说说Scala的implicit隐式转换，这是一个非常powerful的idea，当Scala编译器发现类型不匹配，它不会直接fail，而是尝试从代码中指定的，在scope内的implicit转换定义，来替换问题对象或表达式以满足类型要求，当然，为了避免歧义，同一时刻Scala需要找到唯一的一个满足条件的implicit定义。</p>
<p>我们的代码首先定义了一个取得友好类名的方法，不去深究它，然后定义了一个正整数的序列，也不去深究它了，你只需要当作他们是正整数就好，接触过FP的同学应该对此类定义不陌生，接下来定义了如下3个支持implicit传入参数的方法：</p>
<ol>
    <li>implicit def move1[A,B,C](implicit a:Manifest[A], b:Manifest[B]) : Move[_1,A,B,C]</li>
    <li>implicit def moveN[P&lt;:Num,A,B,C]( implicit <br />
    m1:Move[P,A,C,B], <br />
    m2:Move[_1,A,B,C], <br />
    m3:Move[P,C,B,A]<br />
    ) : Move[Succ[P],A,B,C]</li>
    <li>def run[N&lt;:Num,A,B,C](implicit m:Move[N,A,B,C])</li>
</ol>
<p>含义分别是：</p>
<ol>
    <li>当需要Move[_1,A,B,C]值时，可以找我帮忙(我有个side-effect，那就是输出移动指令)；</li>
    <li>当需要Move[Succ[P],A,B,C]时，可以找我帮忙；</li>
    <li>运行我，我接受Move[N,A,B,C]类型的参数。</li>
</ol>
<p>简单说明：Scala用[]表示类型参数，区别于Java的&lt;&gt;，另外，Scala的类型声明在变量/函数定义之后。Move[_,A,B,C]的含义是通过C，从A移动圆盘到B。</p>
<p>我们来模拟运行一下，为了演示效果，用一个中等复杂的数目，3个圆盘，从Left移动到Right。</p>
<p>run[_3,Left,Right,Center]，对应Move[Succ[_2],Left,Right,Center]，于是展开成三个Move：</p>
<p>Move[_2,Left,Center,Right]即Move[Succ[_1],Left,Center,Right]<br />
Move[_1,Left,Right,Center]<br />
Move[_2,Center,Right,Left]即Move[Succ[_1],Center,Right,Left]<br />
</p>
<p>然后继续展开Move[_2,Left,Center,Right]和Move[_2,Center,Right,Left]，得到：</p>
<p>Move[_1,Left,Right,Center]<br />
Move[_1,Left,Center,Right]<br />
Move[_1,Right,Center,Left]<br />
--------------------------<br />
Move[_1,Left,Right,Center]<br />
--------------------------<br />
Move[_1,Center,Left,Right]<br />
Move[_1,Center,Right,Left]<br />
Move[_1,Left,Right,Center]<br />
</p>
<p>这个时候已经全部都匹配Move[_1,A,B,C]，于是我们很容易得到以下输出：</p>
<p>Move from Left to Right<br />
Move from Left to Center<br />
Move from Right to Center<br />
Move from Left to Right<br />
Move from Center to Left<br />
Move from Center to Right<br />
Move from Left to Right<br />
</p>
<p>希望本文能带给Scala初学者一些感性认识和启发。</p>
<p><br />
</p>
<img src ="http://www.blogjava.net/sean/aggbug/303374.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sean/" target="_blank">laogao</a> 2009-11-23 19:20 <a href="http://www.blogjava.net/sean/archive/2009/11/23/303374.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于编程语言的typing(一些基本概念)</title><link>http://www.blogjava.net/sean/archive/2009/09/28/296825.html</link><dc:creator>laogao</dc:creator><author>laogao</author><pubDate>Mon, 28 Sep 2009 11:19:00 GMT</pubDate><guid>http://www.blogjava.net/sean/archive/2009/09/28/296825.html</guid><wfw:comment>http://www.blogjava.net/sean/comments/296825.html</wfw:comment><comments>http://www.blogjava.net/sean/archive/2009/09/28/296825.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sean/comments/commentRss/296825.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sean/services/trackbacks/296825.html</trackback:ping><description><![CDATA[<br />
最近围观一本JavaScript的书籍引发的争论，一不小心碰到一篇讲编程语言类型系统划分的帖子，回想起当年还在公司内部的Tech Session上主讲过这个话题，不过只涉及到静态/动态、强类型/弱类型，远没有这位仁兄总结的那么全面。<br />
<br />
原文链接<br />
<a href="http://www.reddit.com/r/programming/comments/63tnv/bruce_eckel_33104_im_over_it_java/c02qx55" target="_blank">http://www.reddit.com/r/programming/comments/63tnv/bruce_eckel_33104_im_over_it_java/c02qx55</a><br />
<br />
不多废话，直入正题：<br />
<br />
[维度一] Static vs Dynamic Typing<br />
静态类型和动态类型，区分的关键点为编译期或运行期确定类型：静态类型在编译期确定，动态类型在运行期确定。<br />
静态类型代表 Java、Scala、Haskell<br />
动态类型代表 Ruby、Python、Erlang<br />
<br />
[维度二] Strong vs Weak Typing <br />
强类型和弱类型，区分的关键点为运行时是否自动转换到与实际类型不符的类型：强类型要求手工类型转换，弱类型自动转换。<br />
强类型代表 Java、Scala、Python<br />
弱类型代表 C、Assembly、JavaScript<br />
<br />
[维度三] Latent (Implicit) vs Manifest (Explicit) Typing<br />
隐式类型和显式类型，区分的关键点为是否要在源码中声明类型：隐式类型不需要，显式类型需要。<br />
隐式类型代表 Haskell、Erlang、Python<br />
显式类型代表 C、C++、Java<br />
<br />
[维度四] Nominal vs Structural Typing<br />
名义类型和结构类型，区分的关键点为类型判定是根据标称还是根据内容：名义类型根据标称，结构类型根据内容。<br />
名义类型代表 C、C++、Java <br />
结构类型代表 Haskell、Erlang、Python<br />
<br />
关于JavaScript书籍的争论，请移步如下网址：<br />
1- 网友Hax的"炮轰"帖 <a href="http://www.javaeye.com/topic/474725" target="_blank">http://www.javaeye.com/topic/474725</a><br />
2- 周爱民(aimingoo)的MSN空间 <a href="http://aimingoo.spaces.live.com/blog/" target="_blank">http://aimingoo.spaces.live.com/blog/</a><br />
3- 火星常驻JE办事处相关帖 <a href="http://mars.group.javaeye.com/group/topic/14325" target="_blank">http://mars.group.javaeye.com/group/topic/14325</a><br />
<br />

<img src ="http://www.blogjava.net/sean/aggbug/296825.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sean/" target="_blank">laogao</a> 2009-09-28 19:19 <a href="http://www.blogjava.net/sean/archive/2009/09/28/296825.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>"已"是如何变成"å·²"的？小记UTF-8编码</title><link>http://www.blogjava.net/sean/archive/2009/05/05/269045.html</link><dc:creator>laogao</dc:creator><author>laogao</author><pubDate>Tue, 05 May 2009 11:24:00 GMT</pubDate><guid>http://www.blogjava.net/sean/archive/2009/05/05/269045.html</guid><wfw:comment>http://www.blogjava.net/sean/comments/269045.html</wfw:comment><comments>http://www.blogjava.net/sean/archive/2009/05/05/269045.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sean/comments/commentRss/269045.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sean/services/trackbacks/269045.html</trackback:ping><description><![CDATA[
		<br />本来是手写的一张草稿，清理台面的时候，正要扔，发现内容还有点意思，干脆吧，开个随笔记录一下，备忘，说不定还能帮到别人。<br /><br />在我们日常生活和工作中，尤其浏览含有中文的网站，时常会为乱码问题犯愁，一些天生Unicode支持不到位的编程语言和软件更是加重了这个现象。虽说已经是2009年了，我们时不时还是能碰到一些明显脑残的code，吐出一堆乱码，不论你用选什么字符集，似乎都无法还原最初的中文。比如"å·²"，或者同一个页面，无法用同一个字符集显示，任何一种字符都至少有一部分显示不正确，不是这儿就是那儿。<br /><br />首先科普一下UTF-8。UTF-8是Unicode所有字符编码实现中，应用范围最广的一个，最大的特点是兼容ASCII码，在此基础上，通过1..4个byte来表示每一个Unicode字符。它是怎么做到的？其实很简单，看首个byte：<br />00000000 ~ 01111111 : 0~127 (ASCII, 单个byte) 表示Unicode范围\u0000 ~ \u007F<br />11000000 ~ 11011111 : (2个1打头，所以2个byte) 表示Unicode范围\u0080 ~ \u07FF<br />11100000 ~ 11101111 : (3个1打头，所以3个byte) 表示Unicode范围\u0800 ~ \uFFFF<br />11110000 ~ 11110111 : (4个1打头，所以4个byte) 表示Unicode范围\u10000 ~ \u10FFFF<br /><br />除了单个byte这个case，其他情况下，后续的byte均以10打头。这些打头的bit：10、110、1110、11110，都不作为具体编码的一部分参与真正Unicode编码的计算。所以1..4个byte，其有效位数分别为：7、11、16、21，因此才有了\u007F、\u07FF、\uFFFF这样的临界值，且4个byte的空间还有富余。<br /><br />有了这个基础知识，我们就来具体看看这个"已"字，是怎么被UTF-8表示的，以及为什么处理不当的代码会最终输出"å·²"。<br /><br />"已"字，用Unicode表示法，是\u5DF2，按照2个byte拆开成二进制表示：<br />01011101 11110010<br />如果用UTF-8编码，采用1110???? 10?????? 10??????这个格式，?号部分填入上述01011101 11110010，得到<br />11100101 10110111 10110010 这样3个byte。<br /><br />好了，这个时候脑残代码出现，假如它不认识UTF-8，那么他会看到这样3个前后没有关联的byte，在西欧Latin-1字符集(即ISO 8859-1)中，它们分别表示"å"、"·"、"²"这3个字符。如果把它们分别再按照UTF-8编码，就成了：<br />11000011 10100101 11000010 10110111 11000010 10110010<br />完美的UTF-8编码，只不过，这完全是假象，只能通过非常规手段才能恢复它原本的样子。<br /><br /><br /><img src ="http://www.blogjava.net/sean/aggbug/269045.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sean/" target="_blank">laogao</a> 2009-05-05 19:24 <a href="http://www.blogjava.net/sean/archive/2009/05/05/269045.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Dreaming in Code 是本好书</title><link>http://www.blogjava.net/sean/archive/2009/03/02/257268.html</link><dc:creator>laogao</dc:creator><author>laogao</author><pubDate>Sun, 01 Mar 2009 16:44:00 GMT</pubDate><guid>http://www.blogjava.net/sean/archive/2009/03/02/257268.html</guid><wfw:comment>http://www.blogjava.net/sean/comments/257268.html</wfw:comment><comments>http://www.blogjava.net/sean/archive/2009/03/02/257268.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sean/comments/commentRss/257268.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sean/services/trackbacks/257268.html</trackback:ping><description><![CDATA[
		<br />周末花了一个下午和一个晚上把Scott Rosenberg的Dreaming in Code从头到尾看了一遍。最直接的感受是这本书把我之前很多记忆碎片串在了一起，成为一个生动而"完整"的故事，虽然书的内容本身组织的多少还是有点散。<br /><br /><img src="http://www.blogjava.net/images/blogjava_net/sean/dreaming_in_code.jpg" alt="dreaming_in_code.jpg" border="0" width="240" height="240" /><br /><br />我本人对Chandler这个项目并不陌生：之前出于对Python/wxWidget和开源本身的兴趣，陆续用过几个0.x的版本，一开始并不是十分友好，性能上也有问题，甚至会莫名的吃掉我机器上的数百兆（或者上G?）空间。后来的版本在性能和可用性上确实提高了不少，但一直感觉这个项目缺少必要的、以及许多开源项目应有的momentum。Phillip J. Eby对Chandler开发人员不懂Python的批评，当时我的印象也很深。而项目中出现的人物，包括Mitchell Kapor、Ted Leung，也都在Chandler这个范畴之外follow过。其他细节包括：Chandler和Cosmo这两个名称的由来、Chandler项目组中女性成员相对高的比例、一些熟悉的人物及其观点（Alan Kay, Bill Joy, Frederick Brooks, Donald Knuth、Linus Torvalds, Ward Cunningham, Larry Wall, Guido van Rossum, Joel Spolsky, etc.）、一些公司的分分和和以及人员流动等等。感觉挺亲切的。<br /><br />可能更重要、也更深刻的原因是：尽管书中一再提到"There's no such thing as a typical software project, every project is different"，我仍然深深的感觉到，Chandler遇到的这些问题，其实也是我亲历的很多项目的种种问题的一个缩影。对这些问题，作者并没有给出解决方案，其实谁也没有标准答案。软件开发是一项非常具有挑战性的工作，也正是像我们这样有热情、有涵养的专业人士生存的空间和价值所在。<br /><br /><img src ="http://www.blogjava.net/sean/aggbug/257268.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sean/" target="_blank">laogao</a> 2009-03-02 00:44 <a href="http://www.blogjava.net/sean/archive/2009/03/02/257268.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Debt Metaphor</title><link>http://www.blogjava.net/sean/archive/2009/02/22/256062.html</link><dc:creator>laogao</dc:creator><author>laogao</author><pubDate>Sun, 22 Feb 2009 09:50:00 GMT</pubDate><guid>http://www.blogjava.net/sean/archive/2009/02/22/256062.html</guid><wfw:comment>http://www.blogjava.net/sean/comments/256062.html</wfw:comment><comments>http://www.blogjava.net/sean/archive/2009/02/22/256062.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sean/comments/commentRss/256062.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sean/services/trackbacks/256062.html</trackback:ping><description><![CDATA[
		<br />以下是一段视频，Ward Cunningham针对Debt Metaphor这个隐喻的由来和人们对它的一些误解进行了澄清：<br /><br /><object width="480" height="385"><param name="movie" value="http://www.youtube.com/v/pqeJFYwnkjE&amp;hl=zh_CN&amp;fs=1" /><param name="allowFullScreen" value="true" /><param name="allowscriptaccess" value="always" /><embed src="http://www.youtube.com/v/pqeJFYwnkjE&amp;hl=zh_CN&amp;fs=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="480" height="385"></embed></object><br /><br />我最感兴趣的是Burden这一段：Cunningham解释说，经常看到有些开发团队，他们快速的开发出软件产品推向市场，不断的往里面添加新的特性，在这个过程中，不断的学习，但从不把学到的东西、总结的经验教训应用回去，这就像是从银行借贷，但从不想着有一天需要偿还（是不是有点像是在说引发这次次贷危机的美国人的消费习惯和观念？），到最后，你所有的收入都将用于偿还利息，你的购买力也将降为零。映射回软件开发的语境，如果我们在一个较长的时间跨度内，开发一个软件，不断的增加feature，但从不回过头去整理和重构，把对这个软件和这些特性的新的理解和认知写回去，那么最终这个软件、这些feature将不再会有任何实际的价值，对它们做任何事，都将花费越来越多的时间和精力，开发进度也就因此下降为零。<br /><br /><br /><img src ="http://www.blogjava.net/sean/aggbug/256062.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sean/" target="_blank">laogao</a> 2009-02-22 17:50 <a href="http://www.blogjava.net/sean/archive/2009/02/22/256062.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Python 3.0 Released</title><link>http://www.blogjava.net/sean/archive/2008/12/04/244431.html</link><dc:creator>laogao</dc:creator><author>laogao</author><pubDate>Thu, 04 Dec 2008 11:30:00 GMT</pubDate><guid>http://www.blogjava.net/sean/archive/2008/12/04/244431.html</guid><wfw:comment>http://www.blogjava.net/sean/comments/244431.html</wfw:comment><comments>http://www.blogjava.net/sean/archive/2008/12/04/244431.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sean/comments/commentRss/244431.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sean/services/trackbacks/244431.html</trackback:ping><description><![CDATA[
		<br />在<a target="_blank" title="http://www.blogjava.net/sean/archive/2008/10/04/232389.html" href="/sean/archive/2008/10/04/232389.html">Python 2.6发布</a>两个月后，Python 3.0正式release了。<br /><br />Download: <a target="_blank" title="http://www.python.org/download/releases/3.0/" href="http://www.python.org/download/releases/3.0/">http://www.python.org/download/releases/3.0/</a><br />What's New:<a target="_blank" title="http://docs.python.org/whatsnew/2.6.html#python-3-0" href="http://docs.python.org/whatsnew/2.6.html#python-3-0">http://docs.python.org/whatsnew/2.6.html#python-3-0</a><br /><br /><br /><img src ="http://www.blogjava.net/sean/aggbug/244431.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sean/" target="_blank">laogao</a> 2008-12-04 19:30 <a href="http://www.blogjava.net/sean/archive/2008/12/04/244431.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Python 2.6 Released</title><link>http://www.blogjava.net/sean/archive/2008/10/04/232389.html</link><dc:creator>laogao</dc:creator><author>laogao</author><pubDate>Sat, 04 Oct 2008 13:29:00 GMT</pubDate><guid>http://www.blogjava.net/sean/archive/2008/10/04/232389.html</guid><wfw:comment>http://www.blogjava.net/sean/comments/232389.html</wfw:comment><comments>http://www.blogjava.net/sean/archive/2008/10/04/232389.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sean/comments/commentRss/232389.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sean/services/trackbacks/232389.html</trackback:ping><description><![CDATA[
		<br />就在国庆大假这几天，Python 2.6 (final)悄然发布了，作为为Python 3000 (3.0)铺路的2.x更新版，2.6的发布颇具有里程碑式的意义。<br /><br />Download: <a target="_blank" title="http://www.python.org/download/releases/2.6/" href="http://www.python.org/download/releases/2.6/">http://www.python.org/download/releases/2.6/ </a><br />What's New: <a target="_blank" title="http://docs.python.org/whatsnew/2.6.html" href="http://docs.python.org/whatsnew/2.6.html">http://docs.python.org/whatsnew/2.6.html</a><br /><br /><br /><img src ="http://www.blogjava.net/sean/aggbug/232389.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sean/" target="_blank">laogao</a> 2008-10-04 21:29 <a href="http://www.blogjava.net/sean/archive/2008/10/04/232389.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[Tips] 使用Groovy处理邮件通知</title><link>http://www.blogjava.net/sean/archive/2008/08/27/225118.html</link><dc:creator>laogao</dc:creator><author>laogao</author><pubDate>Wed, 27 Aug 2008 10:55:00 GMT</pubDate><guid>http://www.blogjava.net/sean/archive/2008/08/27/225118.html</guid><wfw:comment>http://www.blogjava.net/sean/comments/225118.html</wfw:comment><comments>http://www.blogjava.net/sean/archive/2008/08/27/225118.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sean/comments/commentRss/225118.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sean/services/trackbacks/225118.html</trackback:ping><description><![CDATA[
		<br />通过Groovy实现邮件通知（其实是转发到ANT）十分容易，先上代码：<br /><br /><div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; background-color: rgb(238, 238, 238); font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: rgb(0, 128, 128);"> 1</span> <span style="color: rgb(0, 0, 0);">ant </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">new</span><span style="color: rgb(0, 0, 0);"> AntBuilder()<br /></span><span style="color: rgb(0, 128, 128);"> 2</span> <span style="color: rgb(0, 0, 0);"><br /></span><span style="color: rgb(0, 128, 128);"> 3</span> <span style="color: rgb(0, 0, 0);">def mail(subject, body, attachment </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> [dir:</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">.</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">,files:[]]) {<br /></span><span style="color: rgb(0, 128, 128);"> 4</span> <span style="color: rgb(0, 0, 0);">    ant.mail(mailhost:</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">mail.com</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">, mailport:</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">1025</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">, user:</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">mailer</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">, password:</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">123</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">, subject:</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">${subject}</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">) {<br /></span><span style="color: rgb(0, 128, 128);"> 5</span> <span style="color: rgb(0, 0, 0);">        from(address:</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">nobody@mail.com</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">)<br /></span><span style="color: rgb(0, 128, 128);"> 6</span> <span style="color: rgb(0, 0, 0);">        to(address:</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">nobody@mail.com</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">)<br /></span><span style="color: rgb(0, 128, 128);"> 7</span> <span style="color: rgb(0, 0, 0);">        message(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">${body}</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">)<br /></span><span style="color: rgb(0, 128, 128);"> 8</span> <span style="color: rgb(0, 0, 0);">        attachments() {<br /></span><span style="color: rgb(0, 128, 128);"> 9</span> <span style="color: rgb(0, 0, 0);">            </span><span style="color: rgb(0, 0, 255);">if</span><span style="color: rgb(0, 0, 0);"> (attachment.files) {<br /></span><span style="color: rgb(0, 128, 128);">10</span> <span style="color: rgb(0, 0, 0);">                fileset(dir:</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">${attachment.dir}</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">) {<br /></span><span style="color: rgb(0, 128, 128);">11</span> <span style="color: rgb(0, 0, 0);">                    attachment.files.each {<br /></span><span style="color: rgb(0, 128, 128);">12</span> <span style="color: rgb(0, 0, 0);">                        include(name:it)<br /></span><span style="color: rgb(0, 128, 128);">13</span> <span style="color: rgb(0, 0, 0);">                    }<br /></span><span style="color: rgb(0, 128, 128);">14</span> <span style="color: rgb(0, 0, 0);">                }<br /></span><span style="color: rgb(0, 128, 128);">15</span> <span style="color: rgb(0, 0, 0);">            }<br /></span><span style="color: rgb(0, 128, 128);">16</span> <span style="color: rgb(0, 0, 0);">        }<br /></span><span style="color: rgb(0, 128, 128);">17</span> <span style="color: rgb(0, 0, 0);">    }<br /></span><span style="color: rgb(0, 128, 128);">18</span> <span style="color: rgb(0, 0, 0);">}<br /></span><span style="color: rgb(0, 128, 128);">19</span> <span style="color: rgb(0, 0, 0);"><br /></span><span style="color: rgb(0, 128, 128);">20</span> <span style="color: rgb(0, 0, 0);">attachment </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> [dir:</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">/tmp</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">, files:[</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">some.properties</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">,</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">some.sql</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">]]<br /></span><span style="color: rgb(0, 128, 128);">21</span> <span style="color: rgb(0, 0, 0);">mail(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">Test mail message at ${new Date()}</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">, </span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">This is a test message.</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">, attachment)<br /></span><span style="color: rgb(0, 128, 128);">22</span> <span style="color: rgb(0, 0, 0);"></span></div><br />这个简单的例子很好的展示了如下Groovy特性：<br />1- Groovy脚本可以不需要定义任何class，方法定义和实际调用也可以混在一起，十分顺手。<br />2- 定义变量不需要指定类型，只要赋值即可，不过运行期依然是强类型。<br />3- 方法参数可以有默认值。<br />4- List和Map的构建直接在语义层面提供支持，如[a:1,b:2]和[1,2]。<br />5- GString使得我们可以方便的在String中引用变量甚至是表达式，如"${a.b.c}"或"${new Date()}"。<br />6- 逻辑判断在true/false基础上有所扩展，[]（0个元素的List）和null均做false处理。<br />7- Closure支持，方便我们在外围代码处“当场”指定处理逻辑，省去了大多数在Java中需要匿名内部类来处理的麻烦，如attachment.files.each { .... }，只有一个传入参数时，可直接用it指代。<br />8- 与ANT的无缝集成，以及对Builder模式的良好支持，使得我们可以写出上述初看上去有些不可思议的代码。<br /><br />P.S. 虽然Groovy自己已经bundle了ANT，可以直接使用其中的绝大多数功能，不过为了调用ANT的mail task，还需要将ANT发行版中带有的ant-javamail.jar以及JavaMail API对应的jar包（可以从Sun网站下载）加到classpath。如果你的JDK版本低于6.0，还需要activation.jar。<br /><br /><br /><img src ="http://www.blogjava.net/sean/aggbug/225118.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sean/" target="_blank">laogao</a> 2008-08-27 18:55 <a href="http://www.blogjava.net/sean/archive/2008/08/27/225118.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[Tips] "When ANT is not enough"升级版 - Groovy to the rescue!</title><link>http://www.blogjava.net/sean/archive/2008/08/25/224246.html</link><dc:creator>laogao</dc:creator><author>laogao</author><pubDate>Mon, 25 Aug 2008 11:38:00 GMT</pubDate><guid>http://www.blogjava.net/sean/archive/2008/08/25/224246.html</guid><wfw:comment>http://www.blogjava.net/sean/comments/224246.html</wfw:comment><comments>http://www.blogjava.net/sean/archive/2008/08/25/224246.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sean/comments/commentRss/224246.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sean/services/trackbacks/224246.html</trackback:ping><description><![CDATA[
		<br />
		<a target="_blank" title="http://www.blogjava.net/sean/archive/2008/08/20/223139.html" href="/sean/archive/2008/08/20/223139.html">前一篇随笔</a>提到目前计划系统学习Python和Groovy，有什么比实际使用更好的学习途径呢？1年多以前，我曾经在<a target="_blank" title="http://www.blogjava.net/sean/archive/2007/06/18/124859.html" href="/sean/archive/2007/06/18/124859.html">另一篇随笔</a>提到：当ANT不够用时，可以用JRuby+Antwrap，这是一个可行的方案，并且经过了长期实战的考验。不过Groovy可以做的更好（感兴趣的朋友可以和Antwrap版对照一下）：<br /><br /><div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; background-color: rgb(238, 238, 238); font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: rgb(0, 128, 128);"> 1</span> <span style="color: rgb(0, 128, 0);">#</span><span style="color: rgb(0, 128, 0);">!/usr/bin/env groovy</span><span style="color: rgb(0, 128, 0);"><br /></span><span style="color: rgb(0, 128, 128);"> 2</span> <span style="color: rgb(0, 128, 0);"></span><span style="color: rgb(0, 0, 0);"><br /></span><span style="color: rgb(0, 128, 128);"> 3</span> <span style="color: rgb(0, 0, 0);">ant </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> new AntBuilder()<br /></span><span style="color: rgb(0, 128, 128);"> 4</span> <span style="color: rgb(0, 0, 0);">root </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(128, 0, 0);">"</span><span style="color: rgb(128, 0, 0);">:pserver:cvsuser:password@10.10.10.1/cvsrepo/SampleProduct</span><span style="color: rgb(128, 0, 0);">"</span><span style="color: rgb(0, 0, 0);"><br /></span><span style="color: rgb(0, 128, 128);"> 5</span> <span style="color: rgb(0, 0, 0);"><br /></span><span style="color: rgb(0, 128, 128);"> 6</span> <span style="color: rgb(0, 0, 0);"></span><span style="color: rgb(0, 0, 255);">def</span><span style="color: rgb(0, 0, 0);"> checkout() {<br /></span><span style="color: rgb(0, 128, 128);"> 7</span> <span style="color: rgb(0, 0, 0);">    ant.cvs(cvsroot:root,command:</span><span style="color: rgb(128, 0, 0);">"</span><span style="color: rgb(128, 0, 0);">checkout -A</span><span style="color: rgb(128, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">,</span><span style="color: rgb(128, 0, 0);">"</span><span style="color: rgb(128, 0, 0);">package</span><span style="color: rgb(128, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">:</span><span style="color: rgb(128, 0, 0);">"</span><span style="color: rgb(128, 0, 0);">.</span><span style="color: rgb(128, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">,dest:</span><span style="color: rgb(128, 0, 0);">"</span><span style="color: rgb(128, 0, 0);">cvsoriginal</span><span style="color: rgb(128, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">,compressionlevel:</span><span style="color: rgb(128, 0, 0);">"</span><span style="color: rgb(128, 0, 0);">9</span><span style="color: rgb(128, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">)<br /></span><span style="color: rgb(0, 128, 128);"> 8</span> <span style="color: rgb(0, 0, 0);">}<br /></span><span style="color: rgb(0, 128, 128);"> 9</span> <span style="color: rgb(0, 0, 0);"><br /></span><span style="color: rgb(0, 128, 128);">10</span> <span style="color: rgb(0, 0, 0);"></span><span style="color: rgb(0, 0, 255);">def</span><span style="color: rgb(0, 0, 0);"> update() {<br /></span><span style="color: rgb(0, 128, 128);">11</span> <span style="color: rgb(0, 0, 0);">    ant.cvs(cvsroot:root,command:</span><span style="color: rgb(128, 0, 0);">"</span><span style="color: rgb(128, 0, 0);">update -A -d</span><span style="color: rgb(128, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">,</span><span style="color: rgb(128, 0, 0);">"</span><span style="color: rgb(128, 0, 0);">package</span><span style="color: rgb(128, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">:</span><span style="color: rgb(128, 0, 0);">"</span><span style="color: rgb(128, 0, 0);">.</span><span style="color: rgb(128, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">,dest:</span><span style="color: rgb(128, 0, 0);">"</span><span style="color: rgb(128, 0, 0);">cvsoriginal</span><span style="color: rgb(128, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">,compressionlevel:</span><span style="color: rgb(128, 0, 0);">"</span><span style="color: rgb(128, 0, 0);">9</span><span style="color: rgb(128, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">)<br /></span><span style="color: rgb(0, 128, 128);">12</span> <span style="color: rgb(0, 0, 0);">}<br /></span><span style="color: rgb(0, 128, 128);">13</span> <span style="color: rgb(0, 0, 0);"><br /></span><span style="color: rgb(0, 128, 128);">14</span> <span style="color: rgb(0, 0, 0);"></span><span style="color: rgb(0, 0, 255);">def</span><span style="color: rgb(0, 0, 0);"> compile(project) {<br /></span><span style="color: rgb(0, 128, 128);">15</span> <span style="color: rgb(0, 0, 0);">    ant.javac(srcdir:</span><span style="color: rgb(128, 0, 0);">"</span><span style="color: rgb(128, 0, 0);">cvsoriginal/$project/src</span><span style="color: rgb(128, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">,destdir:</span><span style="color: rgb(128, 0, 0);">"</span><span style="color: rgb(128, 0, 0);">cvsoriginal/$project</span><span style="color: rgb(128, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">,target:</span><span style="color: rgb(128, 0, 0);">"</span><span style="color: rgb(128, 0, 0);">1.5</span><span style="color: rgb(128, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">,encoding:</span><span style="color: rgb(128, 0, 0);">"</span><span style="color: rgb(128, 0, 0);">GBK</span><span style="color: rgb(128, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">)<br /></span><span style="color: rgb(0, 128, 128);">16</span> <span style="color: rgb(0, 0, 0);">    ant.copy(todir:</span><span style="color: rgb(128, 0, 0);">"</span><span style="color: rgb(128, 0, 0);">cvsoriginal/$project</span><span style="color: rgb(128, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">) {<br /></span><span style="color: rgb(0, 128, 128);">17</span> <span style="color: rgb(0, 0, 0);">        fileset(dir:</span><span style="color: rgb(128, 0, 0);">"</span><span style="color: rgb(128, 0, 0);">cvsoriginal/$project/src</span><span style="color: rgb(128, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">) {<br /></span><span style="color: rgb(0, 128, 128);">18</span> <span style="color: rgb(0, 0, 0);">            exclude(name:</span><span style="color: rgb(128, 0, 0);">"</span><span style="color: rgb(128, 0, 0);">**/*.java</span><span style="color: rgb(128, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">)<br /></span><span style="color: rgb(0, 128, 128);">19</span> <span style="color: rgb(0, 0, 0);">        }<br /></span><span style="color: rgb(0, 128, 128);">20</span> <span style="color: rgb(0, 0, 0);">    }<br /></span><span style="color: rgb(0, 128, 128);">21</span> <span style="color: rgb(0, 0, 0);">    ant.jar(destfile:</span><span style="color: rgb(128, 0, 0);">"</span><span style="color: rgb(128, 0, 0);">build/$project.jar</span><span style="color: rgb(128, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">,basedir:</span><span style="color: rgb(128, 0, 0);">"</span><span style="color: rgb(128, 0, 0);">cvsoriginal/$project</span><span style="color: rgb(128, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">,manifest:</span><span style="color: rgb(128, 0, 0);">"</span><span style="color: rgb(128, 0, 0);">MANIFEST.MF</span><span style="color: rgb(128, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">) {<br /></span><span style="color: rgb(0, 128, 128);">22</span> <span style="color: rgb(0, 0, 0);">        exclude(name:</span><span style="color: rgb(128, 0, 0);">"</span><span style="color: rgb(128, 0, 0);">src/**</span><span style="color: rgb(128, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">)<br /></span><span style="color: rgb(0, 128, 128);">23</span> <span style="color: rgb(0, 0, 0);">    }<br /></span><span style="color: rgb(0, 128, 128);">24</span> <span style="color: rgb(0, 0, 0);">}<br /></span><span style="color: rgb(0, 128, 128);">25</span> <span style="color: rgb(0, 0, 0);"><br /></span><span style="color: rgb(0, 128, 128);">26</span> <span style="color: rgb(0, 0, 0);"></span><span style="color: rgb(0, 0, 0);">//</span><span style="color: rgb(0, 0, 0);"> to actually call your target<br /></span><span style="color: rgb(0, 128, 128);">27</span> <span style="color: rgb(0, 0, 0);">checkout root<br /></span><span style="color: rgb(0, 128, 128);">28</span> <span style="color: rgb(0, 0, 0);"></span></div><br />代码更紧凑，与Java的集成更无缝。只需要简单的培训，有Java基础的朋友就能上手，不必再额外学习和习惯Ruby的语法，不用gem install，也不用去monkey-patch个别文件来集成和打包Ant、Antwrap和JRuby，只需要一个groovy-all-x.x.x.jar加上自己的脚本即可轻松搞定。<br /><br />[2008-08-26 19:25:00 更新] 经过实际环境测试，原来JRuby(1.1)+Antwrap需要24分钟完成的构建，改用Groovy(1.5.6)后，仅需要16分钟，对于需要经常处理构建的项目和产品来说，这8分钟还是相当可观的。<br /><br /><br /><img src ="http://www.blogjava.net/sean/aggbug/224246.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sean/" target="_blank">laogao</a> 2008-08-25 19:38 <a href="http://www.blogjava.net/sean/archive/2008/08/25/224246.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>08年秋季学习计划</title><link>http://www.blogjava.net/sean/archive/2008/08/20/223139.html</link><dc:creator>laogao</dc:creator><author>laogao</author><pubDate>Tue, 19 Aug 2008 16:33:00 GMT</pubDate><guid>http://www.blogjava.net/sean/archive/2008/08/20/223139.html</guid><wfw:comment>http://www.blogjava.net/sean/comments/223139.html</wfw:comment><comments>http://www.blogjava.net/sean/archive/2008/08/20/223139.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sean/comments/commentRss/223139.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sean/services/trackbacks/223139.html</trackback:ping><description><![CDATA[
		<br />从开始专心学习Java到现在，已经7年了，非常幸运的，在这7年中，除了1年的“半工半读”和1年的.NET项目，累积有5年都是从事的Java为主的应用开发，应该说，Java伴随我走过了从入行到站稳脚跟最关键的几年。从某种意义上讲，我应该感谢Java。<br /><br />然而，随着时间的推移，接触到的不同的东西越来越多，自己对不同问题的看法和解决问题的思路越来越成熟，Java虽然是全能型的语言，也变得越来越无法满足日常工作和娱乐的需要，在一些特定的场合，也越发别扭，有时我甚至会怀念学生时代的Pascal/Delphi和后来的C/C++。回过头去看自己，从刚入行时的Java fanboi，到现在这样一个pragmatist和purist的结合体，是多么有趣的转变。<br /><br />在过去的2年多，除了Java和.NET，陆陆续续接触到并有过简单使用经历的编程语言，有bash、PL/SQL、JavaScript、PHP、Perl、Ruby/JRuby、Scala、Python/Jython、Groovy等等，不过都没有系统的学习过，基本就是拿来主义，什么好用用什么，解决手里的问题为主。<br /><br />也许是工作性质的原因，日常要处理的非技术性任务越来越多，留给具体编程的时间现在一天比一天少，编程带来的乐趣和解决技术问题带来的快感，就显得十分珍贵。所以我下定决心一定要系统的学习1~2种和Java/.NET不同的编程语言。一开始这个选择并不容易，我曾经还<a target="_blank" title="http://twitter.com/laogao/statuses/853718837" href="http://twitter.com/laogao/statuses/853718837">半调侃式的和一个朋友讲：“Python, Ruby, Scala, Groovy, 一个都不能少”</a>，经过一段时间的权衡之后，还是比较自然的作出了最后的选择：Python和Groovy。<br /><br />Python这个选择其实并不难，留意Python这门语言，其实说起来也有差不多3年了，它完全符合“和Java/.NET不同的编程语言”这个条件，而且应用面很广，也十分成熟，功能也非常强大，加上大量现成的类库，能够胜任很多不同类型的编程任务。基本上这是一个不用任何思想斗争的选择。<br /><br />Groovy这个选择相对没有那么直接，至少在我真正用Groovy上手做一些东西之前，对它并没有特别的感觉，甚至质疑它存在的价值，因为有Jython和JRuby的选择，为什么一定要Groovy呢？到最近开始接触Groovy，才逐渐了解到它的出身（Groovy的作者受到Python的启发，不过后来Groovy发展的越来越接近Ruby），和它在“兼容”Java代码的前提下为Java语言带来的巨大的表达力提升。对于有Java基础的人而言，Groovy十分容易上手，而且可以帮助我们简化很多繁杂的任务，比如自动构建和自动化测试，而这正是我目前需要处理的。<br /><br />最后简单说说其他几个落选的原因（除了时间有限之外）：<br />bash - 目前掌握的基本够用，发现不够时，完全可以用Python代替。<br />PL/SQL - 基本很少使用了，偶尔需要维护，今后深入用的机会也不多。<br />JavaScript - 对“网页”版的编程语言可能有天生的偏见，尤其是作为Java fanboi，当然对它“盗用”Java这个名字是有意见的。<br />PHP - 用的机会很少，而且过于quick&amp;dirty，不是我喜欢的风格。<br />Perl - 虽然功能强大，但是感觉语法有些terse，不是很适应，能处理的任务，基本上Python也可以。<br />Ruby - 语法和设计理念还是有一些认同，但是对Unicode的支持和部署环境的支持都还不是很让人信服，另外，拿孟岩的话说，Ruby是一门魔幻语言，我不喜欢过于魔幻的东西，而更习惯具体直接的代码。<br />Scala - 也是一门和Java平台关系密切的语言，不过有些“学院派”，函数式编程固然是亮点，但短期内看不到太多的学习价值。<br /><br />所以，综上所述，最后再点一下题，08年秋季的学习计划是：系统学习Python和Groovy。<br /><br /><br /><img src ="http://www.blogjava.net/sean/aggbug/223139.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sean/" target="_blank">laogao</a> 2008-08-20 00:33 <a href="http://www.blogjava.net/sean/archive/2008/08/20/223139.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Django终于要1.0了</title><link>http://www.blogjava.net/sean/archive/2008/06/17/208682.html</link><dc:creator>laogao</dc:creator><author>laogao</author><pubDate>Tue, 17 Jun 2008 10:14:00 GMT</pubDate><guid>http://www.blogjava.net/sean/archive/2008/06/17/208682.html</guid><wfw:comment>http://www.blogjava.net/sean/comments/208682.html</wfw:comment><comments>http://www.blogjava.net/sean/archive/2008/06/17/208682.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sean/comments/commentRss/208682.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sean/services/trackbacks/208682.html</trackback:ping><description><![CDATA[
		<br />或许多少和Python/Django这个社区的人们比较内敛、慢条斯理、喜欢埋头做事有关，Django一直以来版本号更新都很保守，到现在还是0.96。什么时候才有1.0这个问题被人提起无数次，这让很多对它有兴趣的朋友处于长期的观望之中。眼睁睁看着隔壁Rails如火如荼的都出到了2.1.0，Django终于要有动作了：<br /><br /><a target="_blank" title="http://code.djangoproject.com/wiki/VersionOneRoadmap" href="http://code.djangoproject.com/wiki/VersionOneRoadmap">http://code.djangoproject.com/wiki/VersionOneRoadmap</a><br /><br />根据这份线路图，今年7月，Django会推出1.0的第一个alpha版本，接下来在8月会有两次beta和一次rc，如果一切顺利的话，让许多人望眼欲穿的Django 1.0将有望于今年9月正式同大家见面。<br /><br /><br /><img src ="http://www.blogjava.net/sean/aggbug/208682.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sean/" target="_blank">laogao</a> 2008-06-17 18:14 <a href="http://www.blogjava.net/sean/archive/2008/06/17/208682.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[新闻] JRuby 1.1.2 发布</title><link>http://www.blogjava.net/sean/archive/2008/05/28/203452.html</link><dc:creator>laogao</dc:creator><author>laogao</author><pubDate>Wed, 28 May 2008 04:08:00 GMT</pubDate><guid>http://www.blogjava.net/sean/archive/2008/05/28/203452.html</guid><wfw:comment>http://www.blogjava.net/sean/comments/203452.html</wfw:comment><comments>http://www.blogjava.net/sean/archive/2008/05/28/203452.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/sean/comments/commentRss/203452.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sean/services/trackbacks/203452.html</trackback:ping><description><![CDATA[
		<br />JVM下的Ruby语言实现JRuby发布1.1版的第二个升级版1.1.2，这个版本最主要关注的仍然是性能，值得一提的包括：<br />
1- 启动时间大大缩短。<br />
2- YAML标记解析速度提高100x以上。<br />
3- 方法调用在性能、线程和栈深度等方面的增强。<br />4- 其他bug。<br /><br />主页: <a target="_blank" title="http://jruby.codehaus.org/" href="http://jruby.codehaus.org/">http://jruby.codehaus.org/</a><br />下载: <a target="_blank" title="http://dist.codehaus.org/jruby/" href="http://dist.codehaus.org/jruby/">http://dist.codehaus.org/jruby/</a><br /><br /><br /><img src ="http://www.blogjava.net/sean/aggbug/203452.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sean/" target="_blank">laogao</a> 2008-05-28 12:08 <a href="http://www.blogjava.net/sean/archive/2008/05/28/203452.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[新闻] JRuby 1.1发布</title><link>http://www.blogjava.net/sean/archive/2008/04/05/190985.html</link><dc:creator>laogao</dc:creator><author>laogao</author><pubDate>Sat, 05 Apr 2008 15:01:00 GMT</pubDate><guid>http://www.blogjava.net/sean/archive/2008/04/05/190985.html</guid><wfw:comment>http://www.blogjava.net/sean/comments/190985.html</wfw:comment><comments>http://www.blogjava.net/sean/archive/2008/04/05/190985.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sean/comments/commentRss/190985.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sean/services/trackbacks/190985.html</trackback:ping><description><![CDATA[
		<br />JVM下的Ruby语言实现JRuby正式发布1.1版，这个版本最主要关注的是性能，除此之外，值得一提的包括：<br />1- 支持将Ruby代码编译成Java Bytecode。<br />2- 将Oniguruma这个Ruby 1.9标配的正则库移植到Java。<br />3- 重构过的IO实现。<br />4- 更低的内存消耗。<br />5- 修复大量其他兼容性问题。<br /><br />主页: <a target="_blank" title="http://jruby.codehaus.org/" href="http://jruby.codehaus.org/">http://jruby.codehaus.org/</a><br />下载: <a target="_blank" title="http://dist.codehaus.org/jruby/" href="http://dist.codehaus.org/jruby/">http://dist.codehaus.org/jruby/</a><br /><br /><br /><img src ="http://www.blogjava.net/sean/aggbug/190985.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sean/" target="_blank">laogao</a> 2008-04-05 23:01 <a href="http://www.blogjava.net/sean/archive/2008/04/05/190985.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[Tips] 移植Oracle数据库到PostgreSQL的要点</title><link>http://www.blogjava.net/sean/archive/2008/03/11/185510.html</link><dc:creator>laogao</dc:creator><author>laogao</author><pubDate>Tue, 11 Mar 2008 15:04:00 GMT</pubDate><guid>http://www.blogjava.net/sean/archive/2008/03/11/185510.html</guid><wfw:comment>http://www.blogjava.net/sean/comments/185510.html</wfw:comment><comments>http://www.blogjava.net/sean/archive/2008/03/11/185510.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sean/comments/commentRss/185510.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sean/services/trackbacks/185510.html</trackback:ping><description><![CDATA[
		<br />最近尝试把一个Oracle数据库，连同构建在这个数据库上的Java应用移植到PostgreSQL环境。在移植过程中，总结了一些要点，一方面作为笔记备忘，一方面也给有类似任务需要处理而又无从下手的朋友作为参考。<br /><br />1- 首先是准备PostgreSQL环境。有条件的话，最好是找一台空闲的PC机作为测试服务器，安装Linux或BSD，然后从源码编译最新的PostgreSQL 8.3.0。编译时，通过configure指定--with-perl和--with-python以支持PL/Perl和PL/Python。因为绝大多数Linux发行版都已自带Perl和Python，不必额外安装。<br /><br />2- 如果是Windows环境，又需要Perl和Python，则必须额外安装，Python的话，可以方便的找到2.5 for Windows的安装包，Perl的话，推荐ActivePerl，相对麻烦一点，为了后面用到的一些便利的功能顺利加载，Perl版本尽量选5.8.8。<br /><br />3- 创建数据库和用户。通过initdb初始化数据目录，配置postgresql.conf指定主机IP、端口等等信息，配置pg_hba.conf指定访问权限，通过pg_ctl -D &lt;数据目录&gt; -l &lt;日志文件&gt; start启动postmaster，然后createdb、createuser创建数据库和用户。数据库建好之后，就可以createlang -d &lt;数据库名&gt; [plperl|plperlu|plpython|plpythonu]开启PL/Perl和PL/Python。具体命令行参数可通过各命令加--help查看。<br /><br />4- 安装PostgreSQL客户端pgAdminIII，最新版是1.8.2，有条件的话，也可以下载源码自己编译。<br /><br />5- 安装Oracle客户端，需要在PostgreSQL同一台机器，以便Perl用于连接数据库的DBI和Oracle驱动DBD::Oracle模块顺利安装。如果是Windows上的ActivePerl，则可以通过ppm install DBD-Oracle，如果是Linux/BSD，则可以通过CPAN来安装，如perl -MCPAN -e shell进入CPAN Shell，通过install &lt;模块名&gt;或force install &lt;模块名&gt;安装DBI和DBD::Oracle。<br /><br />6- 数据库的移植，可以选择ora2pg来帮忙，目前的版本是4.7。ora2pg是一个用于读取Oracle数据库schema、数据，并生成PostgreSQL脚本或直接导入PostgreSQL数据库的Perl工具。用法很简单，就是通过.conf文件指定数据库连接信息包括NLS_LANG、需要导出导入的schema、table、view、data等等，然后执行一个pl脚本。这是目前相对比较成熟的一个方案，但是遇到schema复杂、约束较强的数据库，需要手工处理的地方还是不少。建议不要直接写入PostgreSQL，而是生成SQL脚本，验证无误后再执行。ora2pg默认会把Oracle中名称的大写转换成小写，因为PostgreSQL在解析SQL时，除非""括起来，默认都是转成小写。schema、table、view、sequence、data等等，基本用ora2pg，加上一些手工调整即可搞定。至于function、stored procedure等，还是手工移吧，偷不得懒。除了ora2pg，其实也可以配置DBI-Link，将Oracle数据库挂到PostgreSQL数据库作为一组独立的"schema"，然后用create table xxx as select ... from ...这样的语法来倒表和数据。PostgreSQL的contrib包也附带有一个dblink，不过是连接其他PostgreSQL数据库的，如果需要连接非PostgreSQL，
还是考虑DBI-Link，任何可以通过Perl的DBI接口访问的数据库，都能link进PostgreSQL。<br /><br />7- 接下来就是Java应用本身了，我这次移的这个应用是Spring+iBatis架构的，很多SQL语句都是明文，好在DAO层的基础部分(CRUD)的SQLMap是工具自动生成，且都是符合ANSI SQL92标准的，不需要修改即可使用。其余的高级查询SQL，需要调整的地方不少，一些常见的修改列举如下：<br /><br />i. SELECT出来的column（包括子查询），如果有别名，必须加AS，比如 select null as some_column from some_table;<br />ii. PostgreSQL没有dual表，类似select 0 from dual的语句，写成select 0即可;<br />iii. DECODE函数需要重构成(case when some_column = 'some_value' then 'some_other_value' when ... then ... else 'some_default_value' end ) as some_column;<br />iv. NVL()函数，PostgreSQL中相对应的是coalesce()，其实几乎所有主流DBMS都支持coalesce，包括Oracle，这才是标准写法;<br />v. 比较日期，在PostgreSQL中，建议使用date_trunc('day', SOME_DATE) = date_trunc('day', #enteredDate#)这样的写法，其中'day'位置可选字段包括有year、month、week、hour、minute、second等等;<br />vi. SYSDATE，对应到PostgreSQL是current_timestamp，可以根据需要使用current_date;<br />vii. ROWNUM，通常我们用ROWNUM都是为了限制查询出来的记录数，PostgreSQL没有这个关键字，需要改成在SELECT语句最后添加 LIMIT语句，如LIMIT 100;<br />viii. (+)这样的外连接写法需要调整为SQL标准的 table1 [LEFT|RIGHT|FULL] OUTER JOIN table2 ON (...);<br />ix. CONNECT BY ... START WITH ... 递归查询可以参考 http: //www.postgresql.org/docs/8.3/static/tablefunc.html 的connectby()函数.<br /><br />最后再多提一点，PostgreSQL自带的过程语言是PL/pgSQL，在PostgreSQL上写function，除了用plpqsql，还支持sql、plperl(u)、plpython(u)等等。如果你对SQL天生过敏，看类似PL/pgSQL的代码都很吃力，别说是写了，你完全可以用你喜欢的语言来表达函数和存储过程的逻辑。有了PL/Python，你还怕什么呢？你几乎能做任何事。<br /><br />[更新 20080313] 把JDBC驱动的部分漏掉了，移植Java应用时，除了改SQL，还需要拿PostgreSQL的JDBC驱动放到classpath下面，如WEB-INF/lib，然后修改数据库连接URL，改成jdbc:postgresql://&lt;ip&gt;:&lt;port&gt;/&lt;dbname&gt;即可。<br /><br />[更新 20080323] 移植schema和数据时，比ora2pg更方便的一种方式是利用EnterpriseDB的Migration Tool，将Oracle的JDBC驱动ojdbc14.jar拷贝到EnterpriseDB安装路径下的jre/lib/ext下后，启动Developer Studio即可建立Oracle连接，选中schema后，可以通过右键Online Migration将schema、数据、函数包等等一次性通通导入EnterpriseDB。如果要继续往"纯"PostgreSQL移，从EDB做backup，然后到PostgreSQL下做restore，这样会丢掉函数包，因为毕竟EDB在PostgreSQL基础上做了相当改造以和Oracle兼容，不过函数包之类还是手工移比较稳妥。<br /><br /><br /><img src ="http://www.blogjava.net/sean/aggbug/185510.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sean/" target="_blank">laogao</a> 2008-03-11 23:04 <a href="http://www.blogjava.net/sean/archive/2008/03/11/185510.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[新闻] Sun Hires Python Experts</title><link>http://www.blogjava.net/sean/archive/2008/03/05/183819.html</link><dc:creator>laogao</dc:creator><author>laogao</author><pubDate>Tue, 04 Mar 2008 16:07:00 GMT</pubDate><guid>http://www.blogjava.net/sean/archive/2008/03/05/183819.html</guid><wfw:comment>http://www.blogjava.net/sean/comments/183819.html</wfw:comment><comments>http://www.blogjava.net/sean/archive/2008/03/05/183819.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sean/comments/commentRss/183819.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sean/services/trackbacks/183819.html</trackback:ping><description><![CDATA[
		<br />在JRuby得到Sun的认可并成功发布1.0，已经向1.1迈进之时，比Ruby应用更广、更早出现JVM运行版本的Python也终于得到了Sun的重视：<br /><br /><a target="_blank" title="http://www.eweek.com/c/a/Application-Development/Sun-Hires-Python-Experts/" href="http://www.eweek.com/c/a/Application-Development/Sun-Hires-Python-Experts/">http://www.eweek.com/c/a/Application-Development/Sun-Hires-Python-Experts/</a><br /><br />尽管Sun官方的说法是他们会更多的应用和扶持native版的Python，相信一直饱受冷落的Jython，也多少会从中受益吧。<br /><br /><br /><img src ="http://www.blogjava.net/sean/aggbug/183819.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sean/" target="_blank">laogao</a> 2008-03-05 00:07 <a href="http://www.blogjava.net/sean/archive/2008/03/05/183819.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[新闻] Grails正式发布1.0版</title><link>http://www.blogjava.net/sean/archive/2008/02/07/179425.html</link><dc:creator>laogao</dc:creator><author>laogao</author><pubDate>Thu, 07 Feb 2008 02:47:00 GMT</pubDate><guid>http://www.blogjava.net/sean/archive/2008/02/07/179425.html</guid><wfw:comment>http://www.blogjava.net/sean/comments/179425.html</wfw:comment><comments>http://www.blogjava.net/sean/archive/2008/02/07/179425.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/sean/comments/commentRss/179425.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sean/services/trackbacks/179425.html</trackback:ping><description><![CDATA[
		<br />春节期间真是好事不断啊，继<a target="_blank" title="http://www.blogjava.net/sean/archive/2008/02/07/179424.html" href="/sean/archive/2008/02/07/179424.html">PostgreSQL 8.3正式release</a>后，Grails也如约发布1.0版。作为受到Ruby on Rails启发开动的"Groovy版Rails"项目，它在设计哲学上和Rails，包括JRuby版的Rails，有着很大的不同，Rails是从头开始用Ruby完整实现从表现层到ORM所有功能，而Grails则是利用Java社区现有的成熟开源框架，如Spring、Hibernate等，在此基础上整合出一套完整的Web应用框架，有点类似Pylons和Django的区别。<br /><br />我<a target="_blank" title="http://www.blogjava.net/sean/archive/2008/01/27/178036.html" href="/sean/archive/2008/01/27/178036.html">之前一篇随笔</a>也提到过，和其它在JVM中运行的动态语言如Jython、JRuby不同的是，Groovy对于有Java基础的人们来说，学习曲线几乎是平的，上手很快，如果你同时也熟悉Spring和Hibernate等，那么Grails更是不二之选。<br /><br />官网: <a target="_blank" title="http://grails.codehaus.org/" href="http://grails.codehaus.org/">http://grails.codehaus.org/</a><br />Release Notes: <a target="_blank" title="http://grails.org/1.0+Release+Notes" href="http://grails.org/1.0+Release+Notes">http://grails.org/1.0+Release+Notes</a><br />下载: <a target="_blank" title="http://grails.codehaus.org/Download" href="http://grails.codehaus.org/Download">http://grails.codehaus.org/Download</a><br /><br /><br /><img src ="http://www.blogjava.net/sean/aggbug/179425.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sean/" target="_blank">laogao</a> 2008-02-07 10:47 <a href="http://www.blogjava.net/sean/archive/2008/02/07/179425.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[新闻] 开源数据库PostgreSQL正式发布8.3版</title><link>http://www.blogjava.net/sean/archive/2008/02/07/179424.html</link><dc:creator>laogao</dc:creator><author>laogao</author><pubDate>Thu, 07 Feb 2008 02:04:00 GMT</pubDate><guid>http://www.blogjava.net/sean/archive/2008/02/07/179424.html</guid><wfw:comment>http://www.blogjava.net/sean/comments/179424.html</wfw:comment><comments>http://www.blogjava.net/sean/archive/2008/02/07/179424.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sean/comments/commentRss/179424.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sean/services/trackbacks/179424.html</trackback:ping><description><![CDATA[
		<br />经过1月3日和1月18日两轮RC后，PostgreSQL 8.3终于在本月初正式发布了。该版本有很多功能和性能上的重大提升，包括全文搜索、新的数据类型(XML、ENUM、UUID等)、并发autovacuum、异步提交等等，以及对Windows平台更好的支持，如SSPI和VC++等。<br /><br />官方声明: <a target="_blank" title="http://www.postgresql.org/about/news.918" href="http://www.postgresql.org/about/news.918">http://www.postgresql.org/about/news.918</a><br />Release Notes: <a target="_blank" title="http://www.postgresql.org/docs/8.3/static/release-8-3.html" href="http://www.postgresql.org/docs/8.3/static/release-8-3.html">http://www.postgresql.org/docs/8.3/static/release-8-3.html</a><br />各版本功能对照: <a target="_blank" title="http://www.postgresql.org/about/featurematrix" href="http://www.postgresql.org/about/featurematrix">http://www.postgresql.org/about/featurematrix</a><br />下载: <a target="_blank" title="http://www.postgresql.org/ftp/" href="http://www.postgresql.org/ftp/">http://www.postgresql.org/ftp/</a><br /><br /><br /><img src ="http://www.blogjava.net/sean/aggbug/179424.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sean/" target="_blank">laogao</a> 2008-02-07 10:04 <a href="http://www.blogjava.net/sean/archive/2008/02/07/179424.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[链接] 会Java？学Groovy？这里有篇很好的介绍/演示 </title><link>http://www.blogjava.net/sean/archive/2008/01/27/178036.html</link><dc:creator>laogao</dc:creator><author>laogao</author><pubDate>Sun, 27 Jan 2008 09:14:00 GMT</pubDate><guid>http://www.blogjava.net/sean/archive/2008/01/27/178036.html</guid><wfw:comment>http://www.blogjava.net/sean/comments/178036.html</wfw:comment><comments>http://www.blogjava.net/sean/archive/2008/01/27/178036.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/sean/comments/commentRss/178036.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sean/services/trackbacks/178036.html</trackback:ping><description><![CDATA[
		<br />如果你有一定的Java基础，想快速的尝尝动态语言的味道，Groovy是个自然的过渡选择。今天无意中看到一篇介绍Groovy的文章，很短，也很有趣，从一个典型的Java程序开始，一步一步"Groovy化"，最终改造成一个完整的Groovy程序：<br /><a target="_blank" href="http://java.dzone.com/news/java-groovy-few-easy-steps"><br />http://java.dzone.com/news/java-groovy-few-easy-steps</a><br /><br />如果你对当下动态语言的现状有所了解，那你多半已经知道能在JVM中运行的动态语言，远不止Groovy一种，那么什么理由会让你选择Groovy而非Jython、JRuby或者其他类似的语言呢？笔者认为主要还是编码习惯和风格，Groovy是这些语言中最接近Java的。Jython和JRuby都是从其他成功的动态语言"移植"过来，带有明显的Python、Ruby语法特征和习惯。选择Jython或者JRuby的朋友，我想大都是原本就有Python或者Ruby的基础，舍不得Python和Ruby的一些很方便的语法和编码风格/习惯/哲学，或者干脆就是为了将Python和Ruby世界的一些框架引入到Java中，或者说是让Python/Ruby应用能够更好的利用Java平台已有的资源。如果你在动态语言上没有这方面的需求，只是为了让你的Java应用更动态，选择Jython或者JRuby只能是凭空增加学习难度。要知道，Python和Ruby都是很有特点、很有个性的语言（其实Java又何尝不是），要从Java的思维和哲学，转向Python/Ruby的思维和哲学，并不是那么容易做到和做好的。<br /><br /><br /><img src ="http://www.blogjava.net/sean/aggbug/178036.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sean/" target="_blank">laogao</a> 2008-01-27 17:14 <a href="http://www.blogjava.net/sean/archive/2008/01/27/178036.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Django: Up and Running</title><link>http://www.blogjava.net/sean/archive/2007/11/28/163589.html</link><dc:creator>laogao</dc:creator><author>laogao</author><pubDate>Tue, 27 Nov 2007 16:57:00 GMT</pubDate><guid>http://www.blogjava.net/sean/archive/2007/11/28/163589.html</guid><wfw:comment>http://www.blogjava.net/sean/comments/163589.html</wfw:comment><comments>http://www.blogjava.net/sean/archive/2007/11/28/163589.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sean/comments/commentRss/163589.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sean/services/trackbacks/163589.html</trackback:ping><description><![CDATA[
		<br />算到现在，已经有将近半年没有写过技术文章了，手痒痒，于是拿Django来说事，希望最终的产出能够做成一个简短易懂的Django快速起步，让初次接触Django的朋友能够在最短的时间内了解Django的基本概念和结构。<br /><br />
Django是使用Python实现的一个基于MVC的web应用框架，类似Ruby世界的Ruby on
Rails。如果你是通过Google搜到这篇文章，那么说明你已经对Django有所耳闻，并且愿意了解更多Django相关的信息。我在这里就不多废
话Ruby vs Python或者Rails vs Django，直奔主题。<br /><br />
在写这篇文章时，最新的CPython版本为2.5.1，Django版本为0.96，如无特别说明，本文所有介绍和示例均以此环境为准。<br /><br />
0- 在开始之前，首先当然是安装一个基本能用的开发环境。<br /><br />
如果你的操作系统是Linux或者其他类Unix系统，很可能已经预装了Python，可以在命令行执行python -V查看Python版本。如果你是Windows操作系统，或者想尝试不同版本的Python，那么可以到<a href="http://www.python.org/" target="_blank">http://www.python.org/</a>下载相应的安装包进行安装。<br /><br />
有了Python以后，到<a href="http://www.djangoproject.com/" target="_blank">http://www.djangoproject.com/</a>下载Django，解压以后，cd到解压出来的目录，执行python setup.py install。<br /><br />
为了能够做出一个基本的多层web应用，还需要安装一个数据库，如果没有特别喜好和偏向，推荐PostgreSQL，可以在<a href="http://www.postgresql.org/" target="_blank">http://www.postgresql.org/</a>找到合适的版本下载和安装。<br /><br />
我们还缺少一个数据库驱动，在<a href="http://www.initd.org/pub/software/psycopg/" target="_blank">http://www.initd.org/pub/software/psycopg/</a>可以找到用于连接PostgreSQL的psycopg2，安装方法类似Django。<br /><br />
1- django-admin.py startproject<br /><br />
所有环境OK以后，我们开始动手把玩Django，首先找一个干净的目录，执行<br />
$ python django-admin.py startproject hello<br /><br />
上面这行命令会新建一个hello子目录，包含以下文件：<br />
__init.py__: 表示该目录存放Python程序<br />
manage.py: 提供Django项目相关的管理操作<br />
settings.py: 相当于该Django项目的全局设置<br />
urls.py: 用于配置URL映射，基本上就是通过正则表达式指定不同URL由相应的view方法相应<br /><br />
2- manage.py runserver<br /><br />
至此我们已经搭起了一个基本的Django项目框架，执行<br />
$ python manage.py runserver<br />
命令行会提示在8000端口运行一个开发用的web server，转到浏览器的<a href="http://localhost:8000/" target="_blank">http://localhost:8000/</a>即可看到It worked!的提示信息。你也可以指定端口号，方法是python manage.py runserver XXXX。<br /><br />
3- settings.py<br /><br />
接下来我们做一个完整的从model/数据库到view/template的例子。修改settings.py:<br /><div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; background-color: rgb(238, 238, 238); font-size: 13px; width: 98%;"><span style="color: rgb(0, 0, 0);">DATABASE_ENGINE </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(128, 0, 0);">postgresql_psycopg2</span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(0, 0, 0);"><br />DATABASE_NAME </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(128, 0, 0);">hello</span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 128, 0);">#</span><span style="color: rgb(0, 128, 0);"> Your db name</span><span style="color: rgb(0, 128, 0);"><br /></span><span style="color: rgb(0, 0, 0);">DATABASE_USER </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(128, 0, 0);">postgres</span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 128, 0);">#</span><span style="color: rgb(0, 128, 0);"> Your db user</span><span style="color: rgb(0, 128, 0);"><br /></span><span style="color: rgb(0, 0, 0);">DATABASE_PASSWORD </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(128, 0, 0);">********</span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 128, 0);">#</span><span style="color: rgb(0, 128, 0);"> Your db password</span><span style="color: rgb(0, 128, 0);"><br /></span><span style="color: rgb(0, 0, 0);">DATABASE_HOST </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(128, 0, 0);">''</span><span style="color: rgb(0, 0, 0);"><br />DATABASE_PORT </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(128, 0, 0);">''</span><span style="color: rgb(0, 0, 0);"><br /><br />INSTALLED_APPS </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> (<br />    </span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(128, 0, 0);">django.contrib.auth</span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(0, 0, 0);">,<br />    </span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(128, 0, 0);">django.contrib.contenttypes</span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(0, 0, 0);">,<br />    </span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(128, 0, 0);">django.contrib.sessions</span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(0, 0, 0);">,<br />    </span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(128, 0, 0);">django.contrib.sites</span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(0, 0, 0);">,<br />    </span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(128, 0, 0);">hello</span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(0, 0, 0);">, </span><span style="color: rgb(0, 128, 0);">#</span><span style="color: rgb(0, 128, 0);"> Our new project</span><span style="color: rgb(0, 128, 0);"><br /></span><span style="color: rgb(0, 0, 0);">) </span></div><br />
4- models.py<br /><br />
新建models.py:<br /><div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; background-color: rgb(238, 238, 238); font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: rgb(0, 0, 255);">from</span><span style="color: rgb(0, 0, 0);"> datetime </span><span style="color: rgb(0, 0, 255);">import</span><span style="color: rgb(0, 0, 0);"> datetime<br /></span><span style="color: rgb(0, 0, 255);">from</span><span style="color: rgb(0, 0, 0);"> django.db </span><span style="color: rgb(0, 0, 255);">import</span><span style="color: rgb(0, 0, 0);"> models<br /><br /></span><span style="color: rgb(0, 0, 255);">class</span><span style="color: rgb(0, 0, 0);"> Book(models.Model):<br />    isbn        </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> models.SlugField(maxlength</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">20</span><span style="color: rgb(0, 0, 0);">)<br />    title       </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> models.CharField(maxlength</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">200</span><span style="color: rgb(0, 0, 0);">)<br />    author      </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> models.CharField(maxlength</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">200</span><span style="color: rgb(0, 0, 0);">)<br />    description </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> models.TextField(blank</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">True,null</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">True)<br />    published   </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> models.DateTimeField(default</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">datetime.now)<br /></span></div>这里我们从django.db.models.Model继承我们的model类Book，同时还用到了models中现成的字段类，如
SlugField、CharField、TextField、DateTimeField等。大家比较陌生的恐怕是SlugField，这个基本上类似
CharField，不过增加了其内容需要符合URL要求的限制条件。<br /><br />
执行下面的命令测试数据库脚本的生成:<br />
$ python manage.py sql hello<br />
应该看到如下输出结果:<br />
BEGIN;<br />
CREATE TABLE "hello_book" (<br />
"id" serial NOT NULL PRIMARY KEY,<br />
"isbn" varchar(20) NOT NULL,<br />
"title" varchar(200) NOT NULL,<br />
"author" varchar(200) NOT NULL,<br />
"description" text NULL,<br />
"published" timestamp with time zone NOT NULL<br />
);<br />
COMMIT;<br /><br />
确认无误后可以通过下面的命令提交到数据库:<br />
$ python manage.py syncdb<br />
其间会要求我们创建一个管理员账号，如果暂时不打算做admin页面，可以跳过。<br /><br />
5- views.py<br /><br />
model有了之后，接下来我们就可以开始画视图了。由于篇幅和时间有限，我仅简单介绍一下Django的template，然后实现一个最基本的图书清单页面。<br /><br />
首先定义图书清单的URL，在urls.py中:<br /><div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; background-color: rgb(238, 238, 238); font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: rgb(0, 0, 255);">from</span><span style="color: rgb(0, 0, 0);"> django.conf.urls.defaults </span><span style="color: rgb(0, 0, 255);">import</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 0);">*</span><span style="color: rgb(0, 0, 0);"><br /><br />urlpatterns </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> patterns(</span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(128, 0, 0);">hello.views</span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(0, 0, 0);">,<br />    (r</span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(128, 0, 0);">^hello/books/$</span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(0, 0, 0);">, </span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(128, 0, 0);">book_list</span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(0, 0, 0);">),<br />)<br /></span></div>含义为hello/books/这个URI资源对应的相应view方法为hello.views.book_list。<br /><br />
新建templates目录，然后新建books.html:<br /><div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; background-color: rgb(238, 238, 238); font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: rgb(0, 0, 255);">&lt;</span><span style="color: rgb(128, 0, 0);">html  </span><span style="color: rgb(255, 0, 0);">xmlns</span><span style="color: rgb(0, 0, 255);">="http://www.w3.org/1999/xhtml"</span><span style="color: rgb(255, 0, 0);"> lang</span><span style="color: rgb(0, 0, 255);">="zh-cn"</span><span style="color: rgb(255, 0, 0);"> xml:lang</span><span style="color: rgb(0, 0, 255);">="zh-cn"</span><span style="color: rgb(0, 0, 255);">&gt;</span><span style="color: rgb(0, 0, 0);"><br /></span><span style="color: rgb(0, 0, 255);">&lt;</span><span style="color: rgb(128, 0, 0);">head</span><span style="color: rgb(0, 0, 255);">&gt;</span><span style="color: rgb(0, 0, 0);"><br /></span><span style="color: rgb(0, 0, 255);">&lt;</span><span style="color: rgb(128, 0, 0);">title</span><span style="color: rgb(0, 0, 255);">&gt;</span><span style="color: rgb(0, 0, 0);">{{ title|escape }}</span><span style="color: rgb(0, 0, 255);">&lt;/</span><span style="color: rgb(128, 0, 0);">title</span><span style="color: rgb(0, 0, 255);">&gt;</span><span style="color: rgb(0, 0, 0);"><br /></span><span style="color: rgb(0, 0, 255);">&lt;/</span><span style="color: rgb(128, 0, 0);">head</span><span style="color: rgb(0, 0, 255);">&gt;</span><span style="color: rgb(0, 0, 0);"><br /></span><span style="color: rgb(0, 0, 255);">&lt;</span><span style="color: rgb(128, 0, 0);">body</span><span style="color: rgb(0, 0, 255);">&gt;</span><span style="color: rgb(0, 0, 0);"><br /></span><span style="color: rgb(0, 0, 255);">&lt;</span><span style="color: rgb(128, 0, 0);">h2</span><span style="color: rgb(0, 0, 255);">&gt;</span><span style="color: rgb(0, 0, 0);">{{ title }}</span><span style="color: rgb(0, 0, 255);">&lt;/</span><span style="color: rgb(128, 0, 0);">h2</span><span style="color: rgb(0, 0, 255);">&gt;</span><span style="color: rgb(0, 0, 0);"><br /></span><span style="color: rgb(0, 0, 255);">&lt;</span><span style="color: rgb(128, 0, 0);">table </span><span style="color: rgb(255, 0, 0);">border</span><span style="color: rgb(0, 0, 255);">="1"</span><span style="color: rgb(0, 0, 255);">&gt;</span><span style="color: rgb(0, 0, 0);"><br />  </span><span style="color: rgb(0, 0, 255);">&lt;</span><span style="color: rgb(128, 0, 0);">tr</span><span style="color: rgb(0, 0, 255);">&gt;&lt;</span><span style="color: rgb(128, 0, 0);">th</span><span style="color: rgb(0, 0, 255);">&gt;</span><span style="color: rgb(0, 0, 0);">ISBN</span><span style="color: rgb(0, 0, 255);">&lt;/</span><span style="color: rgb(128, 0, 0);">th</span><span style="color: rgb(0, 0, 255);">&gt;&lt;</span><span style="color: rgb(128, 0, 0);">th</span><span style="color: rgb(0, 0, 255);">&gt;</span><span style="color: rgb(0, 0, 0);">书名</span><span style="color: rgb(0, 0, 255);">&lt;/</span><span style="color: rgb(128, 0, 0);">th</span><span style="color: rgb(0, 0, 255);">&gt;&lt;</span><span style="color: rgb(128, 0, 0);">th</span><span style="color: rgb(0, 0, 255);">&gt;</span><span style="color: rgb(0, 0, 0);">作者</span><span style="color: rgb(0, 0, 255);">&lt;/</span><span style="color: rgb(128, 0, 0);">th</span><span style="color: rgb(0, 0, 255);">&gt;&lt;</span><span style="color: rgb(128, 0, 0);">th</span><span style="color: rgb(0, 0, 255);">&gt;</span><span style="color: rgb(0, 0, 0);">出版日期</span><span style="color: rgb(0, 0, 255);">&lt;/</span><span style="color: rgb(128, 0, 0);">th</span><span style="color: rgb(0, 0, 255);">&gt;&lt;/</span><span style="color: rgb(128, 0, 0);">tr</span><span style="color: rgb(0, 0, 255);">&gt;</span><span style="color: rgb(0, 0, 0);"><br />  {% for book in books %}<br />  </span><span style="color: rgb(0, 0, 255);">&lt;</span><span style="color: rgb(128, 0, 0);">tr</span><span style="color: rgb(0, 0, 255);">&gt;</span><span style="color: rgb(0, 0, 0);"><br />    </span><span style="color: rgb(0, 0, 255);">&lt;</span><span style="color: rgb(128, 0, 0);">td</span><span style="color: rgb(0, 0, 255);">&gt;</span><span style="color: rgb(0, 0, 0);">{{ book.isbn }}</span><span style="color: rgb(0, 0, 255);">&lt;/</span><span style="color: rgb(128, 0, 0);">td</span><span style="color: rgb(0, 0, 255);">&gt;</span><span style="color: rgb(0, 0, 0);"><br />    </span><span style="color: rgb(0, 0, 255);">&lt;</span><span style="color: rgb(128, 0, 0);">td</span><span style="color: rgb(0, 0, 255);">&gt;</span><span style="color: rgb(0, 0, 0);">{{ book.title }}</span><span style="color: rgb(0, 0, 255);">&lt;/</span><span style="color: rgb(128, 0, 0);">td</span><span style="color: rgb(0, 0, 255);">&gt;</span><span style="color: rgb(0, 0, 0);"><br />    </span><span style="color: rgb(0, 0, 255);">&lt;</span><span style="color: rgb(128, 0, 0);">td</span><span style="color: rgb(0, 0, 255);">&gt;</span><span style="color: rgb(0, 0, 0);">{{ book.author }}</span><span style="color: rgb(0, 0, 255);">&lt;/</span><span style="color: rgb(128, 0, 0);">td</span><span style="color: rgb(0, 0, 255);">&gt;</span><span style="color: rgb(0, 0, 0);"><br />    </span><span style="color: rgb(0, 0, 255);">&lt;</span><span style="color: rgb(128, 0, 0);">td</span><span style="color: rgb(0, 0, 255);">&gt;</span><span style="color: rgb(0, 0, 0);">{{ book.published }}</span><span style="color: rgb(0, 0, 255);">&lt;/</span><span style="color: rgb(128, 0, 0);">td</span><span style="color: rgb(0, 0, 255);">&gt;</span><span style="color: rgb(0, 0, 0);"><br />  </span><span style="color: rgb(0, 0, 255);">&lt;/</span><span style="color: rgb(128, 0, 0);">tr</span><span style="color: rgb(0, 0, 255);">&gt;</span><span style="color: rgb(0, 0, 0);"><br />  {% endfor %}<br /></span><span style="color: rgb(0, 0, 255);">&lt;/</span><span style="color: rgb(128, 0, 0);">table</span><span style="color: rgb(0, 0, 255);">&gt;</span><span style="color: rgb(0, 0, 0);"><br /></span><span style="color: rgb(0, 0, 255);">&lt;/</span><span style="color: rgb(128, 0, 0);">body</span><span style="color: rgb(0, 0, 255);">&gt;</span><span style="color: rgb(0, 0, 0);"><br /></span><span style="color: rgb(0, 0, 255);">&lt;/</span><span style="color: rgb(128, 0, 0);">html</span><span style="color: rgb(0, 0, 255);">&gt;</span><span style="color: rgb(0, 0, 0);"><br /></span></div>Django模板的语法是{{}}表示引用，{%%}表示代码，使用起来也很直观，甚至支持UNIX风格的filter，如这里的{{ title|escape }}。<br /><br />
新建views.py:<br /><div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; background-color: rgb(238, 238, 238); font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: rgb(0, 0, 255);">from</span><span style="color: rgb(0, 0, 0);"> hello.models </span><span style="color: rgb(0, 0, 255);">import</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 0);">*</span><span style="color: rgb(0, 0, 0);"><br /></span><span style="color: rgb(0, 0, 255);">from</span><span style="color: rgb(0, 0, 0);"> django.shortcuts </span><span style="color: rgb(0, 0, 255);">import</span><span style="color: rgb(0, 0, 0);"> render_to_response<br /><br /></span><span style="color: rgb(0, 0, 255);">def</span><span style="color: rgb(0, 0, 0);"> book_list(request):<br />    title </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(128, 0, 0);">Book List</span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(0, 0, 0);"><br />    books </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> Book.objects.all()<br />    </span><span style="color: rgb(0, 0, 255);">return</span><span style="color: rgb(0, 0, 0);"> render_to_response(</span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(128, 0, 0);">books.html</span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(0, 0, 0);">, {</span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(128, 0, 0);">title</span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(0, 0, 0);"> : title, </span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(128, 0, 0);">books</span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(0, 0, 0);"> : books})<br /></span></div>最终页面上的内容，通过title和books两个参数传递给tempate(books.html)处理。<br /><br />
修改settings.py:<br /><div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; background-color: rgb(238, 238, 238); font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: rgb(0, 0, 0);">TEMPLATE_DIRS </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> (<br />    </span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(128, 0, 0);">/opt/PROJECTS/Django/hello/templates/</span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(0, 0, 0);">,<br />)</span></div>指定templates目录位置，注意需要使用绝对路径和'/'，无论是Unix还是Windows系统。<br /><br />
我们手工造一些数据之后，就可以通过<a href="http://localhost:8000/hello/books/" target="_blank">http://localhost:8000/hello/books/</a>访问我们用Django实现的这个简单页面了。<br /><br />
6- What's next<br /><br />
通过上面的简单介绍，相信哪怕是初次接触Django的朋友，也能够对Django有一个初步的认识。其实Django并不难学，并且随着学习的深入，你一定能发现更多的惊喜，不论是来自Django本身，还是Python及其庞大的第三方类库。<br /><br />
如果有时间，建议尝试一下Django的admin pages，即为我们的model提供自动化、网页化的增删改查操作。启用方法如下：<br /><br />
修改models.py (增加class Admin):<br /><div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; background-color: rgb(238, 238, 238); font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: rgb(0, 0, 255);">from</span><span style="color: rgb(0, 0, 0);"> datetime </span><span style="color: rgb(0, 0, 255);">import</span><span style="color: rgb(0, 0, 0);"> datetime<br /></span><span style="color: rgb(0, 0, 255);">from</span><span style="color: rgb(0, 0, 0);"> django.db </span><span style="color: rgb(0, 0, 255);">import</span><span style="color: rgb(0, 0, 0);"> models<br /><br /></span><span style="color: rgb(0, 0, 255);">class</span><span style="color: rgb(0, 0, 0);"> Book(models.Model):<br />    isbn        </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> models.SlugField(maxlength</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">20</span><span style="color: rgb(0, 0, 0);">)<br />    title       </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> models.CharField(maxlength</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">200</span><span style="color: rgb(0, 0, 0);">)<br />    author      </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> models.CharField(maxlength</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">200</span><span style="color: rgb(0, 0, 0);">)<br />    description </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> models.TextField(blank</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">True,null</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">True)<br />    published   </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> models.DateTimeField(default</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">datetime.now)<br />    </span><span style="color: rgb(0, 0, 255);">class</span><span style="color: rgb(0, 0, 0);"> Admin:<br />        </span><span style="color: rgb(0, 0, 255);">pass</span><span style="color: rgb(0, 0, 0);"><br /></span></div><br />
修改settings.py和urls.py，加入admin支持:<br />[settings.py]<br /><div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; background-color: rgb(238, 238, 238); font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: rgb(0, 0, 0);">INSTALLED_APPS </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> (<br />    </span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(128, 0, 0);">django.contrib.auth</span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(0, 0, 0);">,<br />    </span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(128, 0, 0);">django.contrib.contenttypes</span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(0, 0, 0);">,<br />    </span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(128, 0, 0);">django.contrib.sessions</span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(0, 0, 0);">,<br />    </span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(128, 0, 0);">django.contrib.sites</span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(0, 0, 0);">,<br />    </span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(128, 0, 0);">django.contrib.admin</span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(0, 0, 0);">,<br />    </span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(128, 0, 0);">hello</span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(0, 0, 0);">,<br />)</span></div>[urls.py]<br /><div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; background-color: rgb(238, 238, 238); font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: rgb(0, 0, 0);">urlpatterns </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> patterns(</span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(128, 0, 0);">hello.views</span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(0, 0, 0);">,<br />    (r</span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(128, 0, 0);">^hello/books/$</span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(0, 0, 0);">, </span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(128, 0, 0);">book_list</span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(0, 0, 0);">),<br />    (r</span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(128, 0, 0);">^hello/admin/</span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(0, 0, 0);">, include(</span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(128, 0, 0);">django.contrib.admin.urls</span><span style="color: rgb(128, 0, 0);">'</span><span style="color: rgb(0, 0, 0);">)),<br />)<br /></span></div><br />Note:<br />
# 为了成功运行admin pages，需要首先执行python manage.py syncdb admin以创建django_admin_log表。<br />
# 如果前面跳过了创建管理员步骤，简单的方法可以删掉auth_user表，然后python manage.py syncdb重建。<br /><br />
按照我们urls.py的配置，admin pages可以通过<a href="http://localhost:8000/hello/admin/" target="_blank">http://localhost:8000/hello/admin/</a>访问。Enjoy!<br /><br /><br /><img src ="http://www.blogjava.net/sean/aggbug/163589.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sean/" target="_blank">laogao</a> 2007-11-28 00:57 <a href="http://www.blogjava.net/sean/archive/2007/11/28/163589.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title> [Tips] When ANT is not enough, JRuby to the rescue!</title><link>http://www.blogjava.net/sean/archive/2007/06/18/124859.html</link><dc:creator>laogao</dc:creator><author>laogao</author><pubDate>Sun, 17 Jun 2007 16:39:00 GMT</pubDate><guid>http://www.blogjava.net/sean/archive/2007/06/18/124859.html</guid><wfw:comment>http://www.blogjava.net/sean/comments/124859.html</wfw:comment><comments>http://www.blogjava.net/sean/archive/2007/06/18/124859.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sean/comments/commentRss/124859.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sean/services/trackbacks/124859.html</trackback:ping><description><![CDATA[
		<br />
随着项目越来越大，你的ANT脚本变得越来越臃肿，越来越依赖antcontrib来实现构建逻辑？不想放弃对构建过程和产出做精细的控制？恨不得自己写ANT Task？<br /><br />
等一等，在你考虑转向Maven或者真正卷起袖子开始研究ANT Task的API之前，先听我向你推荐Ruby/JRuby。相信我，也许这才是你真正需要的。<br /><br />
在我看来构建脚本最主要的要求是表达能力和控制能力，表达能力是我们应该能够很方便的告诉它我们要它做什么，而控制能力是我们告诉它如何去做，除了必要的
构建命令的参数、依赖关系之外，它应该能够理解我们其他一些特殊要求。在项目相对简单时，ANT的XML格式的"脚本语言"能够比较好的表达构建者的要
求，至少比纯Java的方式更加简单清晰，于是几乎从它诞生之日就成为Java领域当仁不让的头号构建工具。但是ANT也有它不够用的时候，尤其在控制能
力上，为了实现实际使用中越来越复杂越来越精细的对构建过程和产出的要求，ANT的使用者们开始对ANT进行扩展，最具影响力的"非官方"扩展可能就是
antcontrib了，很多实际Java项目的构建文件中我们都能够找到它的影子。但是一堆taskdef和<variable><substring><stringutil><length><math>之类的夹杂在ANT脚本里，怎么看怎么觉得别扭，也严重影响构建脚本的可读性。<br /><br />
我发现我需要的是一个具有完整功能脚本语言来写我的构建逻辑：一方面它要足够简单，我需要清晰的表达我的意图，另一方面当我需要的时候它要足够强大来帮我实现精细的控制。我自然而然想到Ruby。<br /><br />
由于Ruby是一个功能完整的工具，完全自己写Ruby脚本来调用Java自己的构建工具如javac、jar等等并非不可能，但是既然已经有人做了轮
子，我们拿来用就是了，大不了自己再改装一下，也少走些弯路。时下比较流行的解决方案有Buildr、Raven、Rant和Antwrap，它们各有特
点，大家可以根据需要进行选择。<br /><br />
我最终选了其中最"轻量"的Antwrap，原因嘛，我不想学新的API，我已经熟悉ANT常用的Task，而且我希望尽可能多的自己控制构建的过程和产
出。Antwrap最能够满足我的需要。而前不久JRuby刚刚发布了1.0版，这样一来Ruby和Java的跨界引用变得更加容易，Ruby的实现自然
就选择了JRuby。<br /><br />
安装JRuby和Antwrap相当容易，只要把下载的jruby-bin-1.0.tar.gz/.zip解压到本地，确保JAVA_HOME和
CLASSPATH的配置，然后gem install Antwrap (选择Java版)
即可。为了顺利加载Antwrap，需要在CLASSPATH中包含ANT的ant.jar和ant-launcher.jar。(当然，如果想更加方便
的share你的成果，可以把ant的文件拷贝到jruby目录下，在jruby的启动脚本加入必要的export/set命令，然后打包，这样别人只要
从你提供的zip包解压出来即可使用。)<br /><br />
为了给大家一个直观的感觉，举个简单的例子(貌似BlogJava没有提供对Ruby代码的默认支持，大家将就看吧)：<br /><div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; background-color: rgb(238, 238, 238); font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: rgb(0, 128, 128);"> 1</span> <span style="color: rgb(0, 0, 0);">require </span><span style="color: rgb(0, 0, 0);">'</span><span style="color: rgb(0, 0, 0);">rubygems</span><span style="color: rgb(0, 0, 0);">'</span><span style="color: rgb(0, 0, 0);"><br /></span><span style="color: rgb(0, 128, 128);"> 2</span> <span style="color: rgb(0, 0, 0);">gem </span><span style="color: rgb(0, 0, 0);">'</span><span style="color: rgb(0, 0, 0);">Antwrap</span><span style="color: rgb(0, 0, 0);">'</span><span style="color: rgb(0, 0, 0);"><br /></span><span style="color: rgb(0, 128, 128);"> 3</span> <span style="color: rgb(0, 0, 0);">require </span><span style="color: rgb(0, 0, 0);">'</span><span style="color: rgb(0, 0, 0);">antwrap</span><span style="color: rgb(0, 0, 0);">'</span><span style="color: rgb(0, 0, 0);"><br /></span><span style="color: rgb(0, 128, 128);"> 4</span> <span style="color: rgb(0, 0, 0);"><br /></span><span style="color: rgb(0, 128, 128);"> 5</span> <span style="color: rgb(0, 0, 0);">@ant</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">AntProject.</span><span style="color: rgb(0, 0, 255);">new</span><span style="color: rgb(0, 0, 0);">(:name</span><span style="color: rgb(0, 0, 0);">=&gt;</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">SampleAntwrapBuild</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">, :basedir</span><span style="color: rgb(0, 0, 0);">=&gt;</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">.</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">)<br /></span><span style="color: rgb(0, 128, 128);"> 6</span> <span style="color: rgb(0, 0, 0);">@cvsroot</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">:pserver:cvsuser:password@10.10.10.1/cvsrepo/SampleProduct</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);"><br /></span><span style="color: rgb(0, 128, 128);"> 7</span> <span style="color: rgb(0, 0, 0);"><br /></span><span style="color: rgb(0, 128, 128);"> 8</span> <span style="color: rgb(0, 0, 0);">def cvscheckout<br /></span><span style="color: rgb(0, 128, 128);"> 9</span> <span style="color: rgb(0, 0, 0);">    @ant.cvs(<br /></span><span style="color: rgb(0, 128, 128);">10</span> <span style="color: rgb(0, 0, 0);">        :cvsroot</span><span style="color: rgb(0, 0, 0);">=&gt;</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">#{@cvsroot}</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">, <br /></span><span style="color: rgb(0, 128, 128);">11</span> <span style="color: rgb(0, 0, 0);">        :command</span><span style="color: rgb(0, 0, 0);">=&gt;</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">checkout -A</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">, <br /></span><span style="color: rgb(0, 128, 128);">12</span> <span style="color: rgb(0, 0, 0);">        :</span><span style="color: rgb(0, 0, 255);">package</span><span style="color: rgb(0, 0, 0);">=&gt;</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">.</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">, <br /></span><span style="color: rgb(0, 128, 128);">13</span> <span style="color: rgb(0, 0, 0);">        :dest</span><span style="color: rgb(0, 0, 0);">=&gt;</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">cvsoriginal</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">, <br /></span><span style="color: rgb(0, 128, 128);">14</span> <span style="color: rgb(0, 0, 0);">        :compressionlevel</span><span style="color: rgb(0, 0, 0);">=&gt;</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">9</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">)<br /></span><span style="color: rgb(0, 128, 128);">15</span> <span style="color: rgb(0, 0, 0);">end<br /></span><span style="color: rgb(0, 128, 128);">16</span> <span style="color: rgb(0, 0, 0);"><br /></span><span style="color: rgb(0, 128, 128);">17</span> <span style="color: rgb(0, 0, 0);">def cvsupdate<br /></span><span style="color: rgb(0, 128, 128);">18</span> <span style="color: rgb(0, 0, 0);">    @ant.cvs(<br /></span><span style="color: rgb(0, 128, 128);">19</span> <span style="color: rgb(0, 0, 0);">        :cvsroot</span><span style="color: rgb(0, 0, 0);">=&gt;</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">#{@cvsroot}</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">, <br /></span><span style="color: rgb(0, 128, 128);">20</span> <span style="color: rgb(0, 0, 0);">        :command</span><span style="color: rgb(0, 0, 0);">=&gt;</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">update -A -d</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">, <br /></span><span style="color: rgb(0, 128, 128);">21</span> <span style="color: rgb(0, 0, 0);">        :</span><span style="color: rgb(0, 0, 255);">package</span><span style="color: rgb(0, 0, 0);">=&gt;</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">.</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">, <br /></span><span style="color: rgb(0, 128, 128);">22</span> <span style="color: rgb(0, 0, 0);">        :dest</span><span style="color: rgb(0, 0, 0);">=&gt;</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">cvsoriginal</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">, <br /></span><span style="color: rgb(0, 128, 128);">23</span> <span style="color: rgb(0, 0, 0);">        :compressionlevel</span><span style="color: rgb(0, 0, 0);">=&gt;</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">9</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">)<br /></span><span style="color: rgb(0, 128, 128);">24</span> <span style="color: rgb(0, 0, 0);">end<br /></span><span style="color: rgb(0, 128, 128);">25</span> <span style="color: rgb(0, 0, 0);"><br /></span><span style="color: rgb(0, 128, 128);">26</span> <span style="color: rgb(0, 0, 0);">def compile(project_name)<br /></span><span style="color: rgb(0, 128, 128);">27</span> <span style="color: rgb(0, 0, 0);">    @ant.javac(<br /></span><span style="color: rgb(0, 128, 128);">28</span> <span style="color: rgb(0, 0, 0);">        :srcdir</span><span style="color: rgb(0, 0, 0);">=&gt;</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">cvsoriginal/#{project_name}/src</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">, <br /></span><span style="color: rgb(0, 128, 128);">29</span> <span style="color: rgb(0, 0, 0);">        :destdir</span><span style="color: rgb(0, 0, 0);">=&gt;</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">cvsoriginal/#{project_name}</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">, <br /></span><span style="color: rgb(0, 128, 128);">30</span> <span style="color: rgb(0, 0, 0);">        :target</span><span style="color: rgb(0, 0, 0);">=&gt;</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">1.5</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">, <br /></span><span style="color: rgb(0, 128, 128);">31</span> <span style="color: rgb(0, 0, 0);">        :encoding</span><span style="color: rgb(0, 0, 0);">=&gt;</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">GBK</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">)<br /></span><span style="color: rgb(0, 128, 128);">32</span> <span style="color: rgb(0, 0, 0);">    @ant.copy(    :todir</span><span style="color: rgb(0, 0, 0);">=&gt;</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">cvsoriginal/#{project_name}</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">) </span><span style="color: rgb(0, 0, 255);">do</span><span style="color: rgb(0, 0, 0);"><br /></span><span style="color: rgb(0, 128, 128);">33</span> <span style="color: rgb(0, 0, 0);">        fileset(:dir</span><span style="color: rgb(0, 0, 0);">=&gt;</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">cvsoriginal/#{project_name}/src</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">) </span><span style="color: rgb(0, 0, 255);">do</span><span style="color: rgb(0, 0, 0);"><br /></span><span style="color: rgb(0, 128, 128);">34</span> <span style="color: rgb(0, 0, 0);">            exclude(:name</span><span style="color: rgb(0, 0, 0);">=&gt;</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">**/*.java</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">)<br /></span><span style="color: rgb(0, 128, 128);">35</span> <span style="color: rgb(0, 0, 0);">        end<br /></span><span style="color: rgb(0, 128, 128);">36</span> <span style="color: rgb(0, 0, 0);">    end<br /></span><span style="color: rgb(0, 128, 128);">37</span> <span style="color: rgb(0, 0, 0);">    @ant.jar(<br /></span><span style="color: rgb(0, 128, 128);">38</span> <span style="color: rgb(0, 0, 0);">        :destfile</span><span style="color: rgb(0, 0, 0);">=&gt;</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">build/#{project_name}.jar</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">, <br /></span><span style="color: rgb(0, 128, 128);">39</span> <span style="color: rgb(0, 0, 0);">        :basedir</span><span style="color: rgb(0, 0, 0);">=&gt;</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">cvsoriginal/#{project_name}</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">, <br /></span><span style="color: rgb(0, 128, 128);">40</span> <span style="color: rgb(0, 0, 0);">        :manifest</span><span style="color: rgb(0, 0, 0);">=&gt;</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">MANIFEST.MF</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">) </span><span style="color: rgb(0, 0, 255);">do</span><span style="color: rgb(0, 0, 0);"><br /></span><span style="color: rgb(0, 128, 128);">41</span> <span style="color: rgb(0, 0, 0);">        exclude(:name</span><span style="color: rgb(0, 0, 0);">=&gt;</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">src/**</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">)<br /></span><span style="color: rgb(0, 128, 128);">42</span> <span style="color: rgb(0, 0, 0);">    end<br /></span><span style="color: rgb(0, 128, 128);">43</span> <span style="color: rgb(0, 0, 0);">end<br /></span><span style="color: rgb(0, 128, 128);">44</span> <span style="color: rgb(0, 0, 0);"><br /></span><span style="color: rgb(0, 128, 128);">45</span> <span style="color: rgb(0, 0, 0);"># to actually call your target<br /></span><span style="color: rgb(0, 128, 128);">46</span> <span style="color: rgb(0, 0, 0);"></span><span style="color: rgb(0, 0, 255);">if</span><span style="color: rgb(0, 0, 0);"> ARGV.empty</span><span style="color: rgb(0, 0, 0);">?</span><span style="color: rgb(0, 0, 0);"><br /></span><span style="color: rgb(0, 128, 128);">47</span> <span style="color: rgb(0, 0, 0);">    puts </span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">Usage: jruby #{$0} [target]</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);"><br /></span><span style="color: rgb(0, 128, 128);">48</span> <span style="color: rgb(0, 0, 0);"></span><span style="color: rgb(0, 0, 255);">else</span><span style="color: rgb(0, 0, 0);"><br /></span><span style="color: rgb(0, 128, 128);">49</span> <span style="color: rgb(0, 0, 0);">    eval ARGV[</span><span style="color: rgb(0, 0, 0);">0</span><span style="color: rgb(0, 0, 0);">]<br /></span><span style="color: rgb(0, 128, 128);">50</span> <span style="color: rgb(0, 0, 0);">end<br /></span><span style="color: rgb(0, 128, 128);">51</span> <span style="color: rgb(0, 0, 0);"></span></div><br /></math></length></stringutil></substring></variable>可以看到几乎都是我们熟悉的ANT Task，只是更加紧凑更加灵活，一旦掌握了最最基本的Ruby语法，用它写出功能强大的构建脚本可以说是分分钟搞定。<br /><br /><br /><img src ="http://www.blogjava.net/sean/aggbug/124859.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sean/" target="_blank">laogao</a> 2007-06-18 00:39 <a href="http://www.blogjava.net/sean/archive/2007/06/18/124859.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[Tips] 在Linux环境下调整Eclipse中树状列表(GtkTreeView)的显示密度</title><link>http://www.blogjava.net/sean/archive/2007/06/09/123109.html</link><dc:creator>laogao</dc:creator><author>laogao</author><pubDate>Sat, 09 Jun 2007 14:46:00 GMT</pubDate><guid>http://www.blogjava.net/sean/archive/2007/06/09/123109.html</guid><wfw:comment>http://www.blogjava.net/sean/comments/123109.html</wfw:comment><comments>http://www.blogjava.net/sean/archive/2007/06/09/123109.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/sean/comments/commentRss/123109.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sean/services/trackbacks/123109.html</trackback:ping><description><![CDATA[
		<br />
如果你在Windows和Linux两个平台下都使用过Eclipse，那么你一定注意到Windows下Eclipse项目列表的显示要比Linux下
紧凑许多。对于做开发的朋友来讲，在屏幕上同时看到的信息总是希望尽可能多，尽可能全，在这一点上目前的GNOME并不能让我们十分满意，它的GTK+控
件本身和控件之间的间隙占掉了不少本来就很有限的屏幕空间，尤其当屏幕分辨率不是特别高的时候。<br /><br />
Linux版的Eclipse默认使用GTK+2.0的控件，所以在默认的情况下，会和其他GTK应用程序（如OpenOffice.org）一样给人一
种不够密实的感觉。当然，这和GNOME的设计哲学不无关系，不过这不是本文主旨，就不深入讨论了，进入正题讲讲如何调整Eclipse树状列表的显示密
度。<br /><br />
基于GTK+2.0的程序在启动时会到用户的home目录找GTK+2.0的配置文件，即~/.gtkrc-2.0，Ubuntu下默认没有这个文件，需要我们自己创建。打开你习惯的编辑器，加入如下代码：<br /><br />
style "eclipse" {<br />
  font_name="Sans 8"<br />
  GtkTreeView::vertical-separator=0<br />
  GtkTreeView::horizontal-separator=0<br />
}<br />
class "GtkTreeView" style "eclipse"<br /><br />
重启Eclipse应该就能看到效果了。如果你想做得更彻底，对所有常用的GTK+2.0控件都开刀的话，可以这样写：<br /><br />
style "gtkcompact" {<br />
  font_name="Sans 8"<br />
  GtkButton::default_border={0,0,0,0}<br />
  GtkButton::default_outside_border={0,0,0,0}<br />
  GtkButtonBox::child_min_width=0<br />
  GtkButtonBox::child_min_heigth=0<br />
  GtkButtonBox::child_internal_pad_x=0<br />
  GtkButtonBox::child_internal_pad_y=0<br />
  GtkMenu::vertical-padding=1<br />
  GtkMenuBar::internal_padding=0<br />
  GtkMenuItem::horizontal_padding=4<br />
  GtkOptionMenu::indicator_size=0<br />
  GtkOptionMenu::indicator_spacing=0<br />
  GtkPaned::handle_size=4<br />
  GtkRange::trough_border=0<br />
  GtkRange::stepper_spacing=0<br />
  GtkScale::value_spacing=0<br />
  GtkScrolledWindow::scrollbar_spacing=0<br />
  GtkExpander::expander_size=10<br />
  GtkExpander::expander_spacing=0<br />
  GtkTreeView::vertical-separator=0<br />
  GtkTreeView::horizontal-separator=0<br />
  GtkTreeView::expander-size=8<br />
  GtkTreeView::fixed-height-mode=TRUE<br />
  GtkWidget::focus_padding=0<br />
}<br />
class "GtkWidget" style "gtkcompact"<br /><br /><br /><img src ="http://www.blogjava.net/sean/aggbug/123109.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sean/" target="_blank">laogao</a> 2007-06-09 22:46 <a href="http://www.blogjava.net/sean/archive/2007/06/09/123109.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[阅读] JRuby, Groovy, and Swing</title><link>http://www.blogjava.net/sean/archive/2007/02/21/100322.html</link><dc:creator>laogao</dc:creator><author>laogao</author><pubDate>Wed, 21 Feb 2007 12:03:00 GMT</pubDate><guid>http://www.blogjava.net/sean/archive/2007/02/21/100322.html</guid><wfw:comment>http://www.blogjava.net/sean/comments/100322.html</wfw:comment><comments>http://www.blogjava.net/sean/archive/2007/02/21/100322.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sean/comments/commentRss/100322.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sean/services/trackbacks/100322.html</trackback:ping><description><![CDATA[<br />
以下是一组相互关联、按时间先后排列的3篇blog文章，主要探讨是Swing存在的问题以及动态语言如JRuby/Groovy能否帮我们解决Swing的问题：<br />
<br />
<a href="http://rawblock.blogspot.com/2007/02/jruby-can-save-swing.html" target="_blank">JRuby can save Swing</a><br />
<a href="http://pinderkent.blogsavy.com/archives/53" target="_blank">JRuby cannot save Swing</a><br />
<a href="http://shemnon.com/speling/2007/02/groovy-can-save-swing.html" target="_blank">Groovy can save Swing</a><br />
<br />
挺有趣的，推荐大家有空读一读。<br />
<br /><br />
<img src ="http://www.blogjava.net/sean/aggbug/100322.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sean/" target="_blank">laogao</a> 2007-02-21 20:03 <a href="http://www.blogjava.net/sean/archive/2007/02/21/100322.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[阅读] What's XML really good for?</title><link>http://www.blogjava.net/sean/archive/2007/02/21/100302.html</link><dc:creator>laogao</dc:creator><author>laogao</author><pubDate>Wed, 21 Feb 2007 06:31:00 GMT</pubDate><guid>http://www.blogjava.net/sean/archive/2007/02/21/100302.html</guid><wfw:comment>http://www.blogjava.net/sean/comments/100302.html</wfw:comment><comments>http://www.blogjava.net/sean/archive/2007/02/21/100302.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sean/comments/commentRss/100302.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sean/services/trackbacks/100302.html</trackback:ping><description><![CDATA[<br />
<a href="http://www-128.ibm.com/developerworks/java/library/x-xjavaforum1.html" target="_blank">http://www-128.ibm.com/developerworks/java/library/x-xjavaforum1.html</a><br />
<br />
真正让XML发光的用途是什么？写配置文件？数据交换？数据存储？<br />
<br />
<br /><img src ="http://www.blogjava.net/sean/aggbug/100302.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sean/" target="_blank">laogao</a> 2007-02-21 14:31 <a href="http://www.blogjava.net/sean/archive/2007/02/21/100302.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[闲侃DRY] Don't Repeat Others</title><link>http://www.blogjava.net/sean/archive/2007/01/30/96806.html</link><dc:creator>laogao</dc:creator><author>laogao</author><pubDate>Tue, 30 Jan 2007 15:09:00 GMT</pubDate><guid>http://www.blogjava.net/sean/archive/2007/01/30/96806.html</guid><wfw:comment>http://www.blogjava.net/sean/comments/96806.html</wfw:comment><comments>http://www.blogjava.net/sean/archive/2007/01/30/96806.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sean/comments/commentRss/96806.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sean/services/trackbacks/96806.html</trackback:ping><description><![CDATA[<br/>
<a href="http://www.alittlemadness.com/?p=76" target="_blank">http://www.alittlemadness.com/?p=76</a><br/>
<br/>
今天读到这篇文章，原来和<a href="http://www.blogjava.net/sean/archive/2007/01/22/95424.html" target="_blank">我想的</a><a href="http://www.blogjava.net/sean/archive/2007/01/26/96071.html" target="_blank">一样</a>，不过DRO这个提法的提法比我提炼的要更好，呵呵。<br/>
<br/>
<br/>
<img src ="http://www.blogjava.net/sean/aggbug/96806.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sean/" target="_blank">laogao</a> 2007-01-30 23:09 <a href="http://www.blogjava.net/sean/archive/2007/01/30/96806.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[闲侃DRY] 自制框架 vs. 开源框架</title><link>http://www.blogjava.net/sean/archive/2007/01/26/96071.html</link><dc:creator>laogao</dc:creator><author>laogao</author><pubDate>Thu, 25 Jan 2007 16:32:00 GMT</pubDate><guid>http://www.blogjava.net/sean/archive/2007/01/26/96071.html</guid><wfw:comment>http://www.blogjava.net/sean/comments/96071.html</wfw:comment><comments>http://www.blogjava.net/sean/archive/2007/01/26/96071.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sean/comments/commentRss/96071.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sean/services/trackbacks/96071.html</trackback:ping><description><![CDATA[<br/>
<p>接着<a href="http://www.blogjava.net/sean/archive/2007/01/22/95424.html" target="_blank">上一篇</a>的思路聊。既然我们可以把开发者社群看作一个整体，copy-paste别人的blog文章就是在违背DRY的精神，其实所谓"重造轮子"道理也是一样，既然别人都已经做了相同的事情，并且把它开源了，并且你看了它的代码以后，觉得做得不错，为什么还要自己费心去实现同样的功能呢？自己来实现能给你带来什么好处呢？</p>
<p>我可以列举一些我认为可能会让我们选择自己来做的理由:</p>
<p>
<ol>
<li>我比其他人更了解我们自己的需求。</li>
<li>自己实现起来更容易，代码量更小，也更好用。</li>
<li>我是做技术的，能够DIY，就DIY，这样更能体现我的价值。</li>
</ol>
</p>
<p>这些理由有它的道理，但是我们有必要仔细掂量掂量:</p>
<p>
<ol>
<li>我比其他人更了解我们自己的需求。这是软件开发中很常见的一个误区，客户的需求难以把握，随着时间的推移，我们自己的技术层面的需求实际上也在不断变化，开源的框架往往经历了更多的考虑和验证，并且有一群热心的维护者帮你做bug-fix和升级，甚至我们自己就可以成为这群热心人中的一员。</li>
<li>自己实现起来更容易，代码量更小，也更好用。一开始确实是这样的，开源的框架为了满足不同的需要，往往需要比我们自己写代码要更加复杂和冗余，但是自制框架意味着我们自己定义的接口规范，这个接口规范能不能够在整个项目周期保持稳定暂且不谈，就算接口实现的再简单，项目中其他人也需要时间去理解和消化，然后记住一个定义好的调用方式，今后新加入的工程师也需要学习这个接口规范。开源框架则做得更好，一方面在这个项目中积累和学习到的知识，可以直接用在其他采用同一框架的项目中，另一方面新加入的人如果有过该开源框架的开发经验，上手时间可以缩短。</li>
<li>我是做技术的，能够DIY，就DIY，这样更能体现我的价值。我必须承认，我本人就亲身经历过这样的情况。在即将结束的这个项目中，我甚至自己DIY了一个简易的.NET报表引擎，为的是甩开Crystal Reports。一开始能够DIY报表引擎的想法让我兴奋得睡不着觉，最终前后花了3天很满意的完成了设计和开发，并交付报表开发人员重画报表。可是真正用了一段时间之后，基本的需求满足了，基本的可扩展性也具备，但是缺少可视化设计器、更灵活的公式、更丰富的报表元素，基本上就定型了，没有人有时间和精力再去维护它。</li>
</ol>
</p>
<p>在很多开发团队，大家经常碰在一起讨论具体的技术和设计，这很有必要，有时也不可避免。但是也许Joel Spolsky说的对，软件设计很难，但是比设计软件更难的，是同整个team一起设计软件。做技术的，对于自己了解、掌握、做过、尝试过的东西，对于自己熟悉和信任的东西，多多少少有些偏袒，而对于新的、自己不了解、不熟悉的东西，则难免心存疑虑。这就难怪很多设计讨论会最终很难达成一致。这个时候，要么由技术上的最高权威直接拍板，定下来是什么就是什么，要么就分歧双方或多方各自陈述，然后由项目外部的实体进行独立仲裁。</p>
<p>我看好开源框架，尤其是那些经过考验广泛被采用的框架，因为相比自制框架，它们有着更大的优势。</p>
<br/>
<img src ="http://www.blogjava.net/sean/aggbug/96071.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sean/" target="_blank">laogao</a> 2007-01-26 00:32 <a href="http://www.blogjava.net/sean/archive/2007/01/26/96071.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[Tips] &amp;lt;abbr&amp;gt;: 一个有趣的HTML标签</title><link>http://www.blogjava.net/sean/archive/2007/01/25/96069.html</link><dc:creator>laogao</dc:creator><author>laogao</author><pubDate>Thu, 25 Jan 2007 15:23:00 GMT</pubDate><guid>http://www.blogjava.net/sean/archive/2007/01/25/96069.html</guid><wfw:comment>http://www.blogjava.net/sean/comments/96069.html</wfw:comment><comments>http://www.blogjava.net/sean/archive/2007/01/25/96069.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sean/comments/commentRss/96069.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sean/services/trackbacks/96069.html</trackback:ping><description><![CDATA[<br/>
<p>如果你使用Firefox或Opera并且看到我<a href="http://www.blogjava.net/sean/archive/2007/01/25/96060.html" target="_blank">上一篇随笔</a>中<abbr title="What You See Is What You Get">WYSIWYG</abbr>这一个词，你可以看到它下面是用一串点标注出来的，如果你鼠标悬停在上面，会有工具提示"What You See Is What You Get"。HTML源代码是:</p>
<p><code>
&lt;abbr title="What You See Is What You Get"&gt;WYSIWYG&lt;/abbr&gt;
</code></p>
<p>可惜微软的IE并不能正确render这个tag，尽管它是标准(X)HTML的一部分。</p>
<br/>
<img src ="http://www.blogjava.net/sean/aggbug/96069.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sean/" target="_blank">laogao</a> 2007-01-25 23:23 <a href="http://www.blogjava.net/sean/archive/2007/01/25/96069.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[链接] ASP.NET的"六大罪状"</title><link>http://www.blogjava.net/sean/archive/2007/01/25/96060.html</link><dc:creator>laogao</dc:creator><author>laogao</author><pubDate>Thu, 25 Jan 2007 15:01:00 GMT</pubDate><guid>http://www.blogjava.net/sean/archive/2007/01/25/96060.html</guid><wfw:comment>http://www.blogjava.net/sean/comments/96060.html</wfw:comment><comments>http://www.blogjava.net/sean/archive/2007/01/25/96060.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sean/comments/commentRss/96060.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sean/services/trackbacks/96060.html</trackback:ping><description><![CDATA[<br/>

<p><a href="http://www.garrettdimon.com/archives/aspnet-vs-front-end-architecture" target="_blank">http://www.garrettdimon.com/archives/aspnet-vs-front-end-architecture</a></p>

<p>该文作者细数了他在使用ASP.NET进行开发的过程中遇到的6点不爽的地方，主要都集中在前台架构上，包括大量内联的风格标签、不同浏览器生成不同页面代码、失败的标记设计、缺乏语意一致性、服务器端label和客户端label的脱节、服务器端ID和客户端ID脱节等等。尤其当你想使用标准的CSS，构建数据结构和表现分离的清晰页面时，ASP.NET的一些默认的内部处理可以让你对ASP.NET为何这样做完全无语。比较有趣的是本文后面的回复，其中有不少与楼主同病相怜的网友，还有来自微软员工的为ASP.NET辩护的声音。</p>

<p>我一直对MS在很多设计思路和决定上心存疑虑，不明白为什么MS硬是要自成风格搞自己那一套蹩脚的所谓"规范"或"标准"，似乎在鼓励大家follow一个并不清晰、多少有些混杂无章的设计架构，其实为了方便它实现更cool的<abbr title="What You See Is What You Get">WYSIWYG</abbr>开发工具。就拿今天来说，本来我们项目定义好所有模块都按BO和UI分开，BO里面的类和UI里面的类各施其责，原则上UI依赖BO，而不是反过来，按照我的理解和期望，Windows.Forms命名空间应该是由UI层来依赖，而非BO层。很显然，因为我们的form都放在UI层，肯定是依赖Windows.Forms了，而我们尽可能把所有业务逻辑代码放到BO层。但是为了临时实现一个文本文件形式的log，因为我们的业务逻辑代码都在BO层，所以为了记录有意义的log，我们的log逻辑自然而然只能放在BO层。但是一个基本的获取程序运行路径的方法属于System.Windows.Forms.Application类，让我们不得不using System.Windows.Froms。这其实还好，我们也许不应该强求Windows.Forms一定就是只针对UI上的应用。问题是你每天都在面对类似的情况，每天都或多或少在和.NET API和框架其他部分在打架，当你使用.NET的API时间久了，自然而然你就被它带到它的那一套思路中，你的设计也就自然而然跟着它走了，业务逻辑和UI逻辑交织在一起，当你回过头来想把层次理清理顺已经成为Mission:Impossible。因为抛开MS推荐的方式，自己实现一套自认更清晰的架构，相较"官方"的blueprint设计，代价实在有些高。</p>

<p>所以虽然我没有真正开发过ASP.NET，尤其是2.0版，但我很能理解他们遇到的尴尬。</p>

<br/>
<img src ="http://www.blogjava.net/sean/aggbug/96060.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sean/" target="_blank">laogao</a> 2007-01-25 23:01 <a href="http://www.blogjava.net/sean/archive/2007/01/25/96060.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Learn to be defensive</title><link>http://www.blogjava.net/sean/archive/2007/01/25/95819.html</link><dc:creator>laogao</dc:creator><author>laogao</author><pubDate>Wed, 24 Jan 2007 16:03:00 GMT</pubDate><guid>http://www.blogjava.net/sean/archive/2007/01/25/95819.html</guid><wfw:comment>http://www.blogjava.net/sean/comments/95819.html</wfw:comment><comments>http://www.blogjava.net/sean/archive/2007/01/25/95819.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/sean/comments/commentRss/95819.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sean/services/trackbacks/95819.html</trackback:ping><description><![CDATA[<br/>
作为开发者，我们必须要学会defensive programming，尤其是对要求高可靠性和无人职守的企业级应用中，需要特别留意我们的设计和编码，必须尽可能做到足够defensive。<br/>
<br/>
什么是defensive programming？举个大家都看过的例子：<br/>
<br/>
<code>
String str = ...<br/>
if ("".equals(str)) {} <br/>
</code>
<br/>
在这里我们不写str.equals("")而是反过来，就是为了防止出现不必要的NPE – NullPointerException。<br/>
<br/>
运行期异常是最最需要特别关照的一种非正常状况，除了像上面这类要求我们采用相对较好的编码习惯之外，为了减少运行期异常的发生，通常也需要使用try-catch代码块来把我们相对脆弱，或者需要格外保护的逻辑包起来，对于外部传进来的参数，一定要assert它们的合法性，即assert它们是否能够安全的被后面的逻辑所使用。<br/>
<br/>
通常意义上，defensive programming主要cover的是避免不必要的运行期异常发生。我们可以更进一步，更广义的运用defensive programming的核心思想：在企业应用中，除了运行期异常，对于有些看似严重的极端的错误，如网络超时，连接丢失，数据库提交失败等情况，需要我们具体问题具体分析，并非所有checked exception都一定需要我们去一一catch然后处理。更多的时候，尤其当开发无人职守的后台程序，我们可以采取重试、报告、修改外部数据等方式处理，能够自行解决的，就不要动不动就报错，或等待用户确认，不能自行解决的，则要及时报告并停止运行，避免更大的错误发生。<br/>
<br/>
举个相对具体的例子，两个异构的系统，通过一个中间层的消息平台相互发送消息，通信协议采用最基本的socket方式，这三个系统随时都可能出现宕机或链接中断的情况。为了保证数据的完整性，我们拿其中一个需要发送和接收消息的系统来细说：<br/>
<br/>
一个可能的实现方式是：该系统所有要发送的消息保存到数据库，给它一个初始状态；另一个独立进程从数据库按照时间先后拿出消息，更新拿出的这条消息的状态为处理中，并尝试发送消息；成功后根据需要，更新消息状态为成功发送或者直接删除，如果遇到失败或异常，消息恢复为初始状态，线程sleep一段时间，然后再次尝试，多次尝试或者尝试跨度超过一定时间范围，则停止处理，向管理员汇报（通过邮件、短信等途径）。对于接收到的消息，同样是先存入数据库，然后再由后续的进程用类似的方式取出并处理。如果程序崩溃，可以自动重新启动（应用或整个服务器）。这样不管哪一段通信线路出现故障或阻塞，或者宕机，系统都可以一步一个脚印，确保任务主动而自动的执行，并且忠实记录下有价值的状态信息，出现问题时管理员可以很直观的看到在哪个环节出现故障，从而快速找到问题关键并有效解决。<br/>
<br/>
Defensive programming可以让我们的应用更健壮，在保证数据正确性、完整性的前提下，面对困难也能更加独立自主。和defensive programming相关的话题我想大家如果感兴趣，可以展开更多更深入的探讨，这里只是给大家做个介绍性的铺垫，能抛砖引玉当然更好。<br/>
<br/>
<br/><img src ="http://www.blogjava.net/sean/aggbug/95819.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sean/" target="_blank">laogao</a> 2007-01-25 00:03 <a href="http://www.blogjava.net/sean/archive/2007/01/25/95819.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[闲侃DRY] 开篇</title><link>http://www.blogjava.net/sean/archive/2007/01/22/95424.html</link><dc:creator>laogao</dc:creator><author>laogao</author><pubDate>Mon, 22 Jan 2007 15:22:00 GMT</pubDate><guid>http://www.blogjava.net/sean/archive/2007/01/22/95424.html</guid><wfw:comment>http://www.blogjava.net/sean/comments/95424.html</wfw:comment><comments>http://www.blogjava.net/sean/archive/2007/01/22/95424.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/sean/comments/commentRss/95424.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sean/services/trackbacks/95424.html</trackback:ping><description><![CDATA[<br/>
DRY为何物？DRY是Don't Repeat Yourself的缩写，不要重复自己，这是一项软件开发中的重要原则，或者至少是一个很好的习惯。同样的数据、逻辑，我们应该尽量避免在代码、配置文件、数据库中重复，如果实在没有其他更好的办法，也应该尽可能不要手写这些重复的内容。<br/>
<br/>
既然是闲侃，我想就没必要那么一本正经，索性天马行空一把，想到哪儿写到哪儿吧。<br/>
<br/>
一直不习惯用桌面版RSS阅读器，最近又从Newsgator回到了熟悉的Bloglines，由于工作忙，经过几周的积累，Keep New的条目数超过500。唉，又欠下一堆的阅读债。几经删减，订阅的RSS Feed源还是有80个之多。<br/>
<br/>
一直在犹豫要不要取消BlogJava的综合区RSS订阅，一方面舍不得，毕竟自己在BlogJava安家，怎么说也得捧一下场，而我也需要持续了解这个圈子的人眼下都在做些什么想些什么；另一方面，实在有些难以忍受无数不做删减，原封不动照搬照抄其他网站内容的blog文章，其中不少我早已从其他渠道看到过，或者没什么特别的内容，整段整段的代码，这样的文章看了就是一句话，头痛。<br/>
<br/>
当然了，别人怎么写blog，我无权干涉，但是我是不是可以在这里呼吁一下，引用其他网站内容，能不能不要整篇照贴，要么给个链接，让大家自己去看，要么适当的引用你认为最有价值的段落或句子，或者适当给出自己的见解和评论？否则很难让别人相信你的诚意和动机，这是对原文作者、你的读者、以及你自己起码的尊重。<br/>
<br/>
换个角度来看问题，如果我们把软件开发社区看作一个整体，而我们就是这个整体中的一员，那么原封不动的拷贝粘贴这个整体中另外一个个体的文章内容，又何尝不是在广义上与DRY背道而驰？既然互联网给我们大家提供了分享信息的便利，为什么我们自己不懂得去维护这个本该服务于我们自己的环境呢？<br/>
<br/>
<br/>


<img src ="http://www.blogjava.net/sean/aggbug/95424.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sean/" target="_blank">laogao</a> 2007-01-22 23:22 <a href="http://www.blogjava.net/sean/archive/2007/01/22/95424.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>