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

<channel>
	<title>潘凯的博客</title>
	<atom:link href="http://www.devxx.com/feed" rel="self" type="application/rss+xml" />
	<link>http://www.devxx.com</link>
	<description>道，深藏不露，难以理解。</description>
	<lastBuildDate>Fri, 25 Jun 2010 05:24:12 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>出来混，迟早要还</title>
		<link>http://www.devxx.com/2010/06/759.html</link>
		<comments>http://www.devxx.com/2010/06/759.html#comments</comments>
		<pubDate>Fri, 25 Jun 2010 05:24:12 +0000</pubDate>
		<dc:creator>阿凯</dc:creator>
				<category><![CDATA[技术类]]></category>
		<category><![CDATA[杂类]]></category>

		<guid isPermaLink="false">http://www.devxx.com/?p=759</guid>
		<description><![CDATA[这段时间花了很多时间在修改BUG上。有一些BUG可以看得出是以前的人在实现时仓促留下的。有时我们自己在巨大的时间压力之下也很容易会留下这种“缺陷”（可能当时还不是BUG），但是自己心里很清楚，这个地方在以后极容易出BUG。
实现功能往往是最容易的，保持代码的“干净”，架构的“纯洁”才是不容易做到的。
想想在一个版本反复的被测试打回，已经Delay了多日后，当修改一个BUG时，明明需要对架构进行一次重构，增加一个抽象。可又有多少人还会这样“大动干戈”，或者就把已有的代码COPY一下，再改一改就算了。越是在发版本的前夕，越是容易埋下“地雷”，留下隐患。所以我总是最担心最后改BUG那个阶段。

尤其对于我们这样的生命周期非常长，已经换了很多批开发人员的产品。不在代码中埋下“雷”，有时真得在很大程度上靠开发人员的自律。埋下去的雷就一定会爆，不是不爆，时候未到。出来混，迟早要还。快的就要自己去修补，慢的就由后面不好彩的接手人埋单。
有时在和同事一起review代码时，会讨论为什么一段代码需要重构，明明可以正常的运行。这时我会引入一个衡量的标准。如果以后有新的同事来维护这段代码，他会不会在心里骂人。其实我们自己在维护其他人写的代码时都会有这样的经历，有时你会在心里暗骂“靠，这是谁写的烂代码，浪费我大把的时间来调这种低级的BUG。”；也有时候你会由心里佩服人家写的代码，结构太合理了，扩展起来太方便了。不过我想一般是第一种经历居多。所以如果你不想以后维护你代码的人骂你，最好还是把代码写漂亮。
这个问题有时也有点像是在公共场合扔垃圾的问题。总有一小部分人会扔垃圾，然后另外一大部分人会觉得反正我不扔也会有人扔，如是也扔了，不过总是还有一些人不但自己不扔垃圾还会在公共场合主动的收拾垃圾。如果这部分人多了，社会就和谐了。
想起星爷那本《一个演员的修养》。在写代码的同时考虑到为以后会维护这段代码的人负责，我想也应该是一个程序员的修养。
]]></description>
			<content:encoded><![CDATA[<p>这段时间花了很多时间在修改BUG上。有一些BUG可以看得出是以前的人在实现时仓促留下的。有时我们自己在巨大的时间压力之下也很容易会留下这种“缺陷”（可能当时还不是BUG），但是自己心里很清楚，这个地方在以后极容易出BUG。</p>
<p>实现功能往往是最容易的，保持代码的“干净”，架构的“纯洁”才是不容易做到的。</p>
<p>想想在一个版本反复的被测试打回，已经Delay了多日后，当修改一个BUG时，明明需要对架构进行一次重构，增加一个抽象。可又有多少人还会这样“大动干戈”，或者就把已有的代码COPY一下，再改一改就算了。越是在发版本的前夕，越是容易埋下“地雷”，留下隐患。所以我总是最担心最后改BUG那个阶段。</p>
<p><span id="more-759"></span></p>
<p>尤其对于我们这样的生命周期非常长，已经换了很多批开发人员的产品。不在代码中埋下“雷”，有时真得在很大程度上靠开发人员的自律。埋下去的雷就一定会爆，不是不爆，时候未到。出来混，迟早要还。快的就要自己去修补，慢的就由后面不好彩的接手人埋单。</p>
<p>有时在和同事一起review代码时，会讨论为什么一段代码需要重构，明明可以正常的运行。这时我会引入一个衡量的标准。如果以后有新的同事来维护这段代码，他会不会在心里骂人。其实我们自己在维护其他人写的代码时都会有这样的经历，有时你会在心里暗骂“靠，这是谁写的烂代码，浪费我大把的时间来调这种低级的BUG。”；也有时候你会由心里佩服人家写的代码，结构太合理了，扩展起来太方便了。不过我想一般是第一种经历居多。所以如果你不想以后维护你代码的人骂你，最好还是把代码写漂亮。</p>
<p>这个问题有时也有点像是在公共场合扔垃圾的问题。总有一小部分人会扔垃圾，然后另外一大部分人会觉得反正我不扔也会有人扔，如是也扔了，不过总是还有一些人不但自己不扔垃圾还会在公共场合主动的收拾垃圾。如果这部分人多了，社会就和谐了。</p>
<p>想起星爷那本《一个演员的修养》。在写代码的同时考虑到为以后会维护这段代码的人负责，我想也应该是一个程序员的修养。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.devxx.com/2010/06/759.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>如何限制窗口位置及大小</title>
		<link>http://www.devxx.com/2010/06/756.html</link>
		<comments>http://www.devxx.com/2010/06/756.html#comments</comments>
		<pubDate>Fri, 18 Jun 2010 14:30:34 +0000</pubDate>
		<dc:creator>阿凯</dc:creator>
				<category><![CDATA[UI相关]]></category>
		<category><![CDATA[技术类]]></category>

		<guid isPermaLink="false">http://www.devxx.com/?p=756</guid>
		<description><![CDATA[如果需要在用户改变窗口大小时进行限制。比如限制窗口的最小尺寸/最大尺寸，应该处理窗口的WM_GETMINMAXINFO消息。具体的使用方法请参考MSDN。
如果在改变大小时要实现特殊的逻辑，比如像VIM编辑器，在改变窗口的高度时总是以行高为单位，以避免窗口会出现半行文字。这个可以处理窗口的WM_WINDOWPOSCHANGING消息。这个事件在真正改变窗口大小之前发送，修改其中LPWINDOWPOS中的cy和cx值就可以限制窗口大小。
上面的这些处理千万不要放在WM_SIZE中，虽然也可以实现但会导致窗口出现闪烁。
如果还需要知道用户是通过拖动窗口哪个边界来改变窗口的大小。比如说如果当前窗口只显示了一个大图的一小部分，如果用户拖动窗口上边框来改变窗口的大小，这时应保持窗口下部显示内容相对不变，同样如果用户拖动窗口的左边框来改变窗口的大小，应该保持窗口右边的显示区域不变。不过我发现大多数软件都没有处理这个逻辑，比如PhotoShop。它总是保持显示内容左上角的位置不变，这样如果中在显示大图的一部分时拖动窗口的左边框、上边框或是左上角，整个显示的内容会抖动很不爽。
在改变大小时要感知用户是拖动了窗口的哪个边框来改变大小的，可以处理窗口的WM_SIZING消息，该消息的wParam参数指明了窗口的哪一条边或哪一角的位置改变了。（注意：WM_SIZING只在用户交互式的改变窗口大小时才触发，一些间接的改变大小的行为：如窗口复原，平铺窗口，只会触发WM_WINDOWPOSCHANGING。）
]]></description>
			<content:encoded><![CDATA[<p>如果需要在用户改变窗口大小时进行限制。比如限制窗口的最小尺寸/最大尺寸，应该处理窗口的WM_GETMINMAXINFO消息。具体的使用方法请参考MSDN。</p>
<p>如果在改变大小时要实现特殊的逻辑，比如像VIM编辑器，在改变窗口的高度时总是以行高为单位，以避免窗口会出现半行文字。这个可以处理窗口的WM_WINDOWPOSCHANGING消息。这个事件在真正改变窗口大小之前发送，修改其中LPWINDOWPOS中的cy和cx值就可以限制窗口大小。</p>
<p>上面的这些处理千万不要放在WM_SIZE中，虽然也可以实现但会导致窗口出现闪烁。</p>
<p>如果还需要知道用户是通过拖动窗口哪个边界来改变窗口的大小。比如说如果当前窗口只显示了一个大图的一小部分，如果用户拖动窗口上边框来改变窗口的大小，这时应保持窗口下部显示内容相对不变，同样如果用户拖动窗口的左边框来改变窗口的大小，应该保持窗口右边的显示区域不变。不过我发现大多数软件都没有处理这个逻辑，比如PhotoShop。它总是保持显示内容左上角的位置不变，这样如果中在显示大图的一部分时拖动窗口的左边框、上边框或是左上角，整个显示的内容会抖动很不爽。</p>
<p>在改变大小时要感知用户是拖动了窗口的哪个边框来改变大小的，可以处理窗口的WM_SIZING消息，该消息的wParam参数指明了窗口的哪一条边或哪一角的位置改变了。（注意：WM_SIZING只在用户交互式的改变窗口大小时才触发，一些间接的改变大小的行为：如窗口复原，平铺窗口，只会触发WM_WINDOWPOSCHANGING。）</p>
]]></content:encoded>
			<wfw:commentRss>http://www.devxx.com/2010/06/756.html/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>windows平台管道机制使用小结</title>
		<link>http://www.devxx.com/2010/06/753.html</link>
		<comments>http://www.devxx.com/2010/06/753.html#comments</comments>
		<pubDate>Thu, 17 Jun 2010 03:13:13 +0000</pubDate>
		<dc:creator>阿凯</dc:creator>
				<category><![CDATA[技术类]]></category>
		<category><![CDATA[杂类]]></category>
		<category><![CDATA[pipe]]></category>
		<category><![CDATA[windows]]></category>
		<category><![CDATA[全双工]]></category>
		<category><![CDATA[半双工]]></category>
		<category><![CDATA[字节流]]></category>
		<category><![CDATA[消息流]]></category>
		<category><![CDATA[管道]]></category>

		<guid isPermaLink="false">http://www.devxx.com/?p=753</guid>
		<description><![CDATA[最近需要在产品中用到windows平台的管道通讯机制，因此对管道进行了一番考察，将相关的信息记录如下（只记录要点，如何使用的代码请看MSDN上的例子）：
管道是windows平台下的一种RPC机制，不但支持同机器的，也支持跨机器的访问。总之一句话，linux下的管道机制，直观好用，功能强大。windows的管道使用相对复杂，有一些“出乎意料”的地方。


匿名管道

匿名管道，没有名字，创建后返回两个handle，一个是读端一个是写端。由于没有名字，而windows的handle值又是进程内有效的。因此传递handle到另一个进程最方便的方法就是在创建管道的那个进程中，把handle值继承给子进程。否则要用DuplicateHandle函数为目标进程生成一份handle的复制品，再通过另一种RPC方式把这个值传递给目标进程。这个有点绕，本来因为管道简单，想用它来做RPC机制，但又不得不用另一种RPC机制来传管道的handle。（注，有窗口的进程可以通过WM_COPYDATA消息来传handle，没窗口的进程就有点麻烦。）
匿名管道创建出来是单向的，也就是说，如果你要用匿名管道在进程间进行双向通讯就要创建两个匿名管道，一个方向一个。MSDN上提到，匿名管道在内部是通过命名管道来实现的，只是系统底层为它维护了一个唯一的名字，但是命名管道可以是双工的，不知道为什么匿名的就不能双工。
匿名管道创建出来是同步的，不知道是否可以通过其他函数把它设置成异步的，这个我没有试了。
综上所述，匿名管道的使用场景非常有限。不过如果它刚好适合你的需求，你还是会觉得它很方便。

命名管道

命名管道有一个唯一的名字，任何进程都可以通过名字得到这个管道。另外命名管道在创建时可以设置很多参数来控制管道的属性。可以进行异步读写。 在某种程度上管道很像TCP，可以认为TCP以IP做名字，命名管道的名字是你自己定的；TCP用一个四元组来区分不同连接并进行消息的多路分离；命名管道是通过实例，即服务器端可以用同一个名字多次调用CreateNamedPipe来创建同一命名管道的多个实例，允许多个客户端同时通过同一个名字来得到管道和服务器进行通讯。可以创建的最大实例数是在调用CreateNamedPipe函数创建命名管道时指定的，以后不能再改。这个限制对于可扩展的服务器应用是比较郁闷的。
在服务器端，对于多实例的管道，可以用overlap机制也可以使用complet I/O机制来进行消息的多路分离。具体参见MSDN上的例子，很详细。

管道的吞吐量

我写了段代码进行测试，管道在windows XP下的表现最差，只能达到200M/秒左右。在NT、Vista和Win7下可以到500M/秒。为什么在XP下的表现这么差有点奇怪，毕竟现在XP是目前装机量最高的系统。

可创建的管道数量

管道实例和TCP连接一样属于内核对象，要占用内核资源，尤其是它们的读写缓冲都是占用内核的不可换页内存，因此实例的数量在理论上受制于系统资源即物理内存大小。我连续创建30000个命名管道，每个的读写缓冲都指定为4096B，系统性能似乎没有什么影响，创建的速度也很快。不过我只创建，并没有使用。那个缓冲区的大小，MSDN说是只是一个参考值，系统内部会进行动态的调整，因此也有可能我这种情况下，并没有实际给管道的实例分配缓冲区。创建到50000个时系统的性能出现明显的下降，创建的速度也下降了。我使用的系统是2G的物理内存。

另外几个值得注意的问题

一个是创建命名管道时的第二个参数（dwPipeMode），MSDN上对于可指定的两种模式的说明是，一种是字节流（TYPE_BYTE），一种是消息流（TYPE_MESSAGE），（注意同一管道的读端和写端的模式可以分别指定）。但并没有说明这两种模式之间的区别。我看到网上好多人在问这两者之间的区别是什么，但没看到有明确的回复。
其实字节流（TYPE_BYTE）就和TCP的数据流是一样的，系统在缓冲时可能会进行分割，读端单次读到的字节数可能和写端写入的字节数是不一至的，必须在应用层对字节流进行分帧处理。而（TYPE_MESSAGE）则相当于是windows的管道机制在字节流上做了一次分帧，系统保证在写端，一次以消息模式进行的一次写入的字节，在读端会被一次以消息模式进行的读完整的读出来，不多也不少。这样可以省去应用层的分帧逻辑。所以可以看到，如果指定写端为字节流模式，则读端也只能是字节流模式，不能再指定为消息流模式。如果指定写端为消息流模式，则读端还可以指定为字节流模式或是消息流模式。个人认为如果真有人把这两端的模式指定为不一至的话，是纯属找抽。
还有一个是和阻塞式管道相关的问题。我们这次在项目中用了管道，为了简化编程模型特地在单独的工作线程中使用管道，这样可以进行阻塞式的读。因为我们的应用不会同时开启很多的管道，所以用线程来隔离管道并使用阻塞式模型可以极大的简化代码，同时不影响性能。但是最后我在review代码时发现还是被实现成了在一线程一管道的方式下使用overlap异步模型，实现的同事告诉我，他们发现使用同步模型时在两端读写不对称时会出现管道被“卡死”，感觉不像全双工，更像是半双工的。所以又匆忙的改成了异步模式。我当时就觉得很奇怪，如果是这样的话，双工的阻塞式命名管道就没什么的意义了。
其实只要让两端的读线程不阻塞就可以避免这个卡死。虽然是用阻塞模式，但又要避免它阻塞。无语。。。

if (PeekNamedPipe(hPipe, NULL, 0, NULL, &#038;dwByteAvailable, NULL) &#038;&#038; 0 != dwByteAvailable) {
    BOOL bRes = ReadFile(hPipe, buf, BUFSIZE, &#038;cbByteRead, NULL);
    //.....
} else {
    Sleep(500);
}

将读循环中的ReadFile替换成上述的代码，使用PeekNamedPipe在每次阻塞读之前先看看管道里有没有数据，如果没有就不读。这样可以保证每一次的阻塞读不会被阻塞。这样就避免了上述的问题。
最后的结论就是，尽量不要用windows的管道机制，就算真需要管道的语义也应该优先考虑TCP或是UDP。这样应用如果需要进行分布式扩展时会更加的灵活。
]]></description>
			<content:encoded><![CDATA[<p>最近需要在产品中用到windows平台的管道通讯机制，因此对管道进行了一番考察，将相关的信息记录如下（只记录要点，如何使用的代码请看MSDN上的例子）：</p>
<p>管道是windows平台下的一种RPC机制，不但支持同机器的，也支持跨机器的访问。总之一句话，linux下的管道机制，直观好用，功能强大。windows的管道使用相对复杂，有一些“出乎意料”的地方。</p>
<p><span id="more-753"></span></p>
<p><strong><span style="color: #008000;"><br />
匿名管道<br />
</span></strong></p>
<p>匿名管道，没有名字，创建后返回两个handle，一个是读端一个是写端。由于没有名字，而windows的handle值又是进程内有效的。因此传递handle到另一个进程最方便的方法就是在创建管道的那个进程中，把handle值继承给子进程。否则要用DuplicateHandle函数为目标进程生成一份handle的复制品，再通过另一种RPC方式把这个值传递给目标进程。这个有点绕，本来因为管道简单，想用它来做RPC机制，但又不得不用另一种RPC机制来传管道的handle。（注，有窗口的进程可以通过WM_COPYDATA消息来传handle，没窗口的进程就有点麻烦。）</p>
<p>匿名管道创建出来是单向的，也就是说，如果你要用匿名管道在进程间进行双向通讯就要创建两个匿名管道，一个方向一个。MSDN上提到，匿名管道在内部是通过命名管道来实现的，只是系统底层为它维护了一个唯一的名字，但是命名管道可以是双工的，不知道为什么匿名的就不能双工。</p>
<p>匿名管道创建出来是同步的，不知道是否可以通过其他函数把它设置成异步的，这个我没有试了。</p>
<p>综上所述，匿名管道的使用场景非常有限。不过如果它刚好适合你的需求，你还是会觉得它很方便。</p>
<p><strong><span style="color: #008000;"><br />
命名管道<br />
</span></strong></p>
<p>命名管道有一个唯一的名字，任何进程都可以通过名字得到这个管道。另外命名管道在创建时可以设置很多参数来控制管道的属性。可以进行异步读写。 在某种程度上管道很像TCP，可以认为TCP以IP做名字，命名管道的名字是你自己定的；TCP用一个四元组来区分不同连接并进行消息的多路分离；命名管道是通过实例，即服务器端可以用同一个名字多次调用CreateNamedPipe来创建同一命名管道的多个实例，允许多个客户端同时通过同一个名字来得到管道和服务器进行通讯。可以创建的最大实例数是在调用CreateNamedPipe函数创建命名管道时指定的，以后不能再改。这个限制对于可扩展的服务器应用是比较郁闷的。</p>
<p>在服务器端，对于多实例的管道，可以用overlap机制也可以使用complet I/O机制来进行消息的多路分离。具体参见MSDN上的例子，很详细。</p>
<p><strong><span style="color: #008000;"><br />
管道的吞吐量<br />
</span></strong></p>
<p>我写了段代码进行测试，管道在windows XP下的表现最差，只能达到200M/秒左右。在NT、Vista和Win7下可以到500M/秒。为什么在XP下的表现这么差有点奇怪，毕竟现在XP是目前装机量最高的系统。</p>
<p><strong><span style="color: #008000;"><br />
可创建的管道数量<br />
</span></strong></p>
<p>管道实例和TCP连接一样属于内核对象，要占用内核资源，尤其是它们的读写缓冲都是占用内核的不可换页内存，因此实例的数量在理论上受制于系统资源即物理内存大小。我连续创建30000个命名管道，每个的读写缓冲都指定为4096B，系统性能似乎没有什么影响，创建的速度也很快。不过我只创建，并没有使用。那个缓冲区的大小，MSDN说是只是一个参考值，系统内部会进行动态的调整，因此也有可能我这种情况下，并没有实际给管道的实例分配缓冲区。创建到50000个时系统的性能出现明显的下降，创建的速度也下降了。我使用的系统是2G的物理内存。</p>
<p><strong><span style="color: #008000;"><br />
另外几个值得注意的问题<br />
</span></strong></p>
<p>一个是创建命名管道时的第二个参数（dwPipeMode），MSDN上对于可指定的两种模式的说明是，一种是字节流（TYPE_BYTE），一种是消息流（TYPE_MESSAGE），（注意同一管道的读端和写端的模式可以分别指定）。但并没有说明这两种模式之间的区别。我看到网上好多人在问这两者之间的区别是什么，但没看到有明确的回复。</p>
<p>其实字节流（TYPE_BYTE）就和TCP的数据流是一样的，系统在缓冲时可能会进行分割，读端单次读到的字节数可能和写端写入的字节数是不一至的，必须在应用层对字节流进行分帧处理。而（TYPE_MESSAGE）则相当于是windows的管道机制在字节流上做了一次分帧，系统保证在写端，一次以消息模式进行的一次写入的字节，在读端会被一次以消息模式进行的读完整的读出来，不多也不少。这样可以省去应用层的分帧逻辑。所以可以看到，如果指定写端为字节流模式，则读端也只能是字节流模式，不能再指定为消息流模式。如果指定写端为消息流模式，则读端还可以指定为字节流模式或是消息流模式。个人认为如果真有人把这两端的模式指定为不一至的话，是纯属找抽。</p>
<p>还有一个是和阻塞式管道相关的问题。我们这次在项目中用了管道，为了简化编程模型特地在单独的工作线程中使用管道，这样可以进行阻塞式的读。因为我们的应用不会同时开启很多的管道，所以用线程来隔离管道并使用阻塞式模型可以极大的简化代码，同时不影响性能。但是最后我在review代码时发现还是被实现成了在一线程一管道的方式下使用overlap异步模型，实现的同事告诉我，他们发现使用同步模型时在两端读写不对称时会出现管道被“卡死”，感觉不像全双工，更像是半双工的。所以又匆忙的改成了异步模式。我当时就觉得很奇怪，如果是这样的话，双工的阻塞式命名管道就没什么的意义了。</p>
<p>其实只要让两端的读线程不阻塞就可以避免这个卡死。虽然是用阻塞模式，但又要避免它阻塞。无语。。。</p>
<pre name="code" class="cpp">
if (PeekNamedPipe(hPipe, NULL, 0, NULL, &#038;dwByteAvailable, NULL) &#038;&#038; 0 != dwByteAvailable) {
    BOOL bRes = ReadFile(hPipe, buf, BUFSIZE, &#038;cbByteRead, NULL);
    //.....
} else {
    Sleep(500);
}
</pre>
<p>将读循环中的ReadFile替换成上述的代码，使用PeekNamedPipe在每次阻塞读之前先看看管道里有没有数据，如果没有就不读。这样可以保证每一次的阻塞读不会被阻塞。这样就避免了上述的问题。</p>
<p>最后的结论就是，尽量不要用windows的管道机制，就算真需要管道的语义也应该优先考虑TCP或是UDP。这样应用如果需要进行分布式扩展时会更加的灵活。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.devxx.com/2010/06/753.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>各种时间类型的格式及相互转换</title>
		<link>http://www.devxx.com/2010/06/750.html</link>
		<comments>http://www.devxx.com/2010/06/750.html#comments</comments>
		<pubDate>Wed, 16 Jun 2010 05:57:25 +0000</pubDate>
		<dc:creator>阿凯</dc:creator>
				<category><![CDATA[技术类]]></category>
		<category><![CDATA[杂类]]></category>

		<guid isPermaLink="false">http://www.devxx.com/?p=750</guid>
		<description><![CDATA[这里对各种时间值的格式和相互转换做了个总结。非常有用，转一下做个记号。
原文（http://blogs.msdn.com/b/oldnewthing/archive/2003/09/05/54806.aspx）。
]]></description>
			<content:encoded><![CDATA[<p>这里对各种时间值的格式和相互转换做了个总结。非常有用，转一下做个记号。</p>
<p>原文（<a href="http://blogs.msdn.com/b/oldnewthing/archive/2003/09/05/54806.aspx">http://blogs.msdn.com/b/oldnewthing/archive/2003/09/05/54806.aspx</a>）。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.devxx.com/2010/06/750.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>还是要继续写blog才行</title>
		<link>http://www.devxx.com/2010/06/723.html</link>
		<comments>http://www.devxx.com/2010/06/723.html#comments</comments>
		<pubDate>Mon, 14 Jun 2010 11:59:15 +0000</pubDate>
		<dc:creator>阿凯</dc:creator>
				<category><![CDATA[其他]]></category>

		<guid isPermaLink="false">http://www.devxx.com/?p=723</guid>
		<description><![CDATA[这段时间加班加得比较多，零碎一点的时间都被“烽火战国”给消灭了。冷落我的BLOG好长时间了。
正好这次写新UI库还是累了不少心得，要抽时间都记下来才行。
另（http://t.qq.com/KylePan）这是我的微博，有的可以加个。
]]></description>
			<content:encoded><![CDATA[<p>这段时间加班加得比较多，零碎一点的时间都被“烽火战国”给消灭了。冷落我的BLOG好长时间了。</p>
<p>正好这次写新UI库还是累了不少心得，要抽时间都记下来才行。</p>
<p>另（<a href="http://t.qq.com/KylePan">http://t.qq.com/KylePan</a>）这是我的微博，有的可以加个。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.devxx.com/2010/06/723.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>升级2005SP1引起的问题</title>
		<link>http://www.devxx.com/2009/09/715.html</link>
		<comments>http://www.devxx.com/2009/09/715.html#comments</comments>
		<pubDate>Wed, 23 Sep 2009 07:25:02 +0000</pubDate>
		<dc:creator>阿凯</dc:creator>
				<category><![CDATA[技术类]]></category>
		<category><![CDATA[杂类]]></category>
		<category><![CDATA[build]]></category>
		<category><![CDATA[SP1]]></category>
		<category><![CDATA[VS2005]]></category>
		<category><![CDATA[内存分配]]></category>
		<category><![CDATA[构造]]></category>

		<guid isPermaLink="false">http://www.devxx.com/?p=715</guid>
		<description><![CDATA[前两天下了份chrome的源代想编译来看看。根据它的构建指引先要把VS2005升级到SP1。如是花了一下午替机器上的VS2005打了SP1，中途因开的东西太多，磁盘空间不够，失败了两次，第三次才打成功。
第二天要调一个服务器程序上的bug，如是我在本机build了一个服务器程序，上传到外网一跑，崩了。偶滴个神，最最可怕的事情发生了。
赶快查原因，因为没怎么动代码，我用debug的工具定位了一下很快找到了原因。

原来打了VS2005的SP1补丁以后，居然连VC runtime的那三个dll的版本也升级了。这样我新编译出来的服务器程序用到了新的runtime，而服务器程序依赖的ACE动态链接库是在没打SP1之前编译的，用的是老的runtime。
本来这也是可以相安无事的，大家都在不同的模块中嘛。偏偏服务器代码中调用了一个ACE的函数，函数需要传进去一个指针的引用，ACE内部会在堆上分配空间，再用这个指针带出去，而且该函数的文档上标明，调用者要负责detele这个指针指向的内存。因为服务器程序和ACE动态链接库使用的是不同的两个runtime，而堆是由runtime维护的。这样在调用者去delete那个指针指向的空间时，悲剧就发生了。
原因是找到了，但是要解决起来就没这么容易了。照理我只要把ACE也用打了SP1的VS2005重build一次就OK了，可是服务端是和客户端是共用同一个ACE动态链接库的，而且这个ACE是经过定制编译的。这样子的话，客户端的程序也要用打了SP1的VS2005来重build一下。问题是客户端还包括了一些钩子DLL，还有一部分是用驱动实现的，这些是不是也要全部重build。Oh no！万一重build后出现什么灵异问题怎么办？不敢想象。
我们的客户端的所有组成部分都是在一台公用的build机上构建的，这样可以保持环境单一。可偏偏就是服务器端，由于环境配置复杂（主要是用到了oracle的数据库），又只有我一个人开发，所以一直在我的机器上build。唉，要是全部统一build也不会出这样的悲剧了。
最后，木有办法，只能把我本机的VS2005卸载，然后重装，去掉这个邪恶的SP1，一切恢复正常。只是又浪费了我一天的工夫，由此得出以下重要的经验教训。
1、升级开发环境之前一定要了解下升级包的具体改动有哪些，会不会对开发工作造成大的影响。
2、所有相关的代码一定要统一构建。一定要，切记。让构建不受个别开发人员环境的影响。
3、内存的申请和释放要统一，由其是不要跨模块的边界。
这次违反第三点的是ACE的代码，至少它这个方法的实现不是太严谨。我以前也范过一次类似的错误。其实好的方法是让调用者申请内存，并把指向内存的指针传进来，这样内存的分配和释放都由调用者负责，即使调用跨了模块的边界。这样即使不同模块使用的是不同的runtime，也不会有问题。如果调用方无法确认要分配内存的大小，可以调两次，第一次用空指针，让被调方把需要申请的大小传出来。其实在windows api中很多地方使用了这种方式。
]]></description>
			<content:encoded><![CDATA[<p>前两天下了份chrome的源代想编译来看看。根据它的构建指引先要把VS2005升级到SP1。如是花了一下午替机器上的VS2005打了SP1，中途因开的东西太多，磁盘空间不够，失败了两次，第三次才打成功。</p>
<p>第二天要调一个服务器程序上的bug，如是我在本机build了一个服务器程序，上传到外网一跑，崩了。偶滴个神，最最可怕的事情发生了。</p>
<p>赶快查原因，因为没怎么动代码，我用debug的工具定位了一下很快找到了原因。</p>
<p><span id="more-715"></span></p>
<p>原来打了VS2005的SP1补丁以后，居然连VC runtime的那三个dll的版本也升级了。这样我新编译出来的服务器程序用到了新的runtime，而服务器程序依赖的ACE动态链接库是在没打SP1之前编译的，用的是老的runtime。</p>
<p>本来这也是可以相安无事的，大家都在不同的模块中嘛。偏偏服务器代码中调用了一个ACE的函数，函数需要传进去一个指针的引用，ACE内部会在堆上分配空间，再用这个指针带出去，而且该函数的文档上标明，调用者要负责detele这个指针指向的内存。因为服务器程序和ACE动态链接库使用的是不同的两个runtime，而堆是由runtime维护的。这样在调用者去delete那个指针指向的空间时，悲剧就发生了。</p>
<p>原因是找到了，但是要解决起来就没这么容易了。照理我只要把ACE也用打了SP1的VS2005重build一次就OK了，可是服务端是和客户端是共用同一个ACE动态链接库的，而且这个ACE是经过定制编译的。这样子的话，客户端的程序也要用打了SP1的VS2005来重build一下。问题是客户端还包括了一些钩子DLL，还有一部分是用驱动实现的，这些是不是也要全部重build。Oh no！万一重build后出现什么灵异问题怎么办？不敢想象。</p>
<p>我们的客户端的所有组成部分都是在一台公用的build机上构建的，这样可以保持环境单一。可偏偏就是服务器端，由于环境配置复杂（主要是用到了oracle的数据库），又只有我一个人开发，所以一直在我的机器上build。唉，要是全部统一build也不会出这样的悲剧了。</p>
<p>最后，木有办法，只能把我本机的VS2005卸载，然后重装，去掉这个邪恶的SP1，一切恢复正常。只是又浪费了我一天的工夫，由此得出以下重要的经验教训。</p>
<p>1、升级开发环境之前一定要了解下升级包的具体改动有哪些，会不会对开发工作造成大的影响。</p>
<p>2、所有相关的代码一定要统一构建。一定要，切记。让构建不受个别开发人员环境的影响。</p>
<p>3、内存的申请和释放要统一，由其是不要跨模块的边界。</p>
<p>这次违反第三点的是ACE的代码，至少它这个方法的实现不是太严谨。我以前也范过一次类似的错误。其实好的方法是让调用者申请内存，并把指向内存的指针传进来，这样内存的分配和释放都由调用者负责，即使调用跨了模块的边界。这样即使不同模块使用的是不同的runtime，也不会有问题。如果调用方无法确认要分配内存的大小，可以调两次，第一次用空指针，让被调方把需要申请的大小传出来。其实在windows api中很多地方使用了这种方式。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.devxx.com/2009/09/715.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>潘玥上小学了</title>
		<link>http://www.devxx.com/2009/09/712.html</link>
		<comments>http://www.devxx.com/2009/09/712.html#comments</comments>
		<pubDate>Wed, 02 Sep 2009 05:56:44 +0000</pubDate>
		<dc:creator>阿凯</dc:creator>
				<category><![CDATA[我的女儿]]></category>

		<guid isPermaLink="false">http://www.devxx.com/?p=712</guid>
		<description><![CDATA[潘玥终于上小学了。
开学前的周末我和阿玲去替潘玥买文具和字典。中心书城的学生用品店，人挤得满满的，都是小朋友和家长。小朋友的钱真好赚。听说美国在开学期间学生用品都是免税的。国内就不奢望了，只求老板不要趁机抬价就好。
到中心书城的学生教材专区时，我着实吓了一跳。摊在货架上的是各式各样的什么《XX题库》、《XX冲刺》、《XX全解》、《黄冈XX》。都是小学一年级的。我当时心里就是轰的响了一声，才小学一年级呀。我们那时小学哪有这些个劳什子。就几本教材，课外读物都是自己收集的小人书。

我就觉着是谁要把我那个天真无邪，活泼可爱的小潘玥给抢去一样。心情一下就灰暗了，仿佛看到潘玥皱着眉陷在一堆书里埋头做题。我苦命的娃呀。
阿玲很认真的在到处翻着各种和教材相关的参考书籍。我在心里叹了口气，中国的教育体制就是这样的，人在屋檐下不得不低头。
既然来了，看看有没有什么简单一点的，好一点的辅助教材吧。我开始在满满当当的书架上挑起来，尽量挑那些溥一些的，书名上有什么“冲刺”、“黄冈”、“全解”之类词的一概不要。最后总算找到一本溥溥的，具体名字不记得了，是什么“课文同步解释”之类的。我把书翻开一天，天啊，里面都是密密麻麻的那种就算爷爷奶奶带了老花眼镜也看不清的小字，全是题目。我像扔火炭一样，立马把这书给扔了。拿了本“新华字典”就拉着阿玲匆匆走了。
开学那天，全家出动，浩浩荡荡的去送潘玥上学。校门口那叫一个热闹呀！除了学生，更多的是家长。因为家长不让进去，都挤在校门口，孩子都进去看不见了，还不肯离去。个个都面向校门伸着脖子，像鹅一样。
从后面看，潘玥的背都被她那个大书包挡住了，只能看到小脑袋上像两个小刷子一像的两扎头发。一直看到她走进去，再也看不到了，我心里若有所失。希望上小学的潘玥还能像以前一样快乐，一样对这个世界充满好奇。
]]></description>
			<content:encoded><![CDATA[<p>潘玥终于上小学了。</p>
<p>开学前的周末我和阿玲去替潘玥买文具和字典。中心书城的学生用品店，人挤得满满的，都是小朋友和家长。小朋友的钱真好赚。听说美国在开学期间学生用品都是免税的。国内就不奢望了，只求老板不要趁机抬价就好。</p>
<p>到中心书城的学生教材专区时，我着实吓了一跳。摊在货架上的是各式各样的什么《XX题库》、《XX冲刺》、《XX全解》、《黄冈XX》。都是小学一年级的。我当时心里就是轰的响了一声，才小学一年级呀。我们那时小学哪有这些个劳什子。就几本教材，课外读物都是自己收集的小人书。</p>
<p><span id="more-712"></span></p>
<p>我就觉着是谁要把我那个天真无邪，活泼可爱的小潘玥给抢去一样。心情一下就灰暗了，仿佛看到潘玥皱着眉陷在一堆书里埋头做题。我苦命的娃呀。</p>
<p>阿玲很认真的在到处翻着各种和教材相关的参考书籍。我在心里叹了口气，中国的教育体制就是这样的，人在屋檐下不得不低头。</p>
<p>既然来了，看看有没有什么简单一点的，好一点的辅助教材吧。我开始在满满当当的书架上挑起来，尽量挑那些溥一些的，书名上有什么“冲刺”、“黄冈”、“全解”之类词的一概不要。最后总算找到一本溥溥的，具体名字不记得了，是什么“课文同步解释”之类的。我把书翻开一天，天啊，里面都是密密麻麻的那种就算爷爷奶奶带了老花眼镜也看不清的小字，全是题目。我像扔火炭一样，立马把这书给扔了。拿了本“新华字典”就拉着阿玲匆匆走了。</p>
<p>开学那天，全家出动，浩浩荡荡的去送潘玥上学。校门口那叫一个热闹呀！除了学生，更多的是家长。因为家长不让进去，都挤在校门口，孩子都进去看不见了，还不肯离去。个个都面向校门伸着脖子，像鹅一样。</p>
<p>从后面看，潘玥的背都被她那个大书包挡住了，只能看到小脑袋上像两个小刷子一像的两扎头发。一直看到她走进去，再也看不到了，我心里若有所失。希望上小学的潘玥还能像以前一样快乐，一样对这个世界充满好奇。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.devxx.com/2009/09/712.html/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>关于编码规范（二）</title>
		<link>http://www.devxx.com/2009/08/700.html</link>
		<comments>http://www.devxx.com/2009/08/700.html#comments</comments>
		<pubDate>Tue, 11 Aug 2009 13:52:59 +0000</pubDate>
		<dc:creator>阿凯</dc:creator>
				<category><![CDATA[我的编程实践总结]]></category>
		<category><![CDATA[技术类]]></category>
		<category><![CDATA[代码review]]></category>
		<category><![CDATA[代码格式]]></category>
		<category><![CDATA[命名法]]></category>
		<category><![CDATA[架构设计]]></category>
		<category><![CDATA[测试驱动]]></category>
		<category><![CDATA[编码规范]]></category>
		<category><![CDATA[编程实践]]></category>
		<category><![CDATA[编程规范]]></category>
		<category><![CDATA[设计原则]]></category>
		<category><![CDATA[过程改进]]></category>

		<guid isPermaLink="false">http://www.devxx.com/?p=700</guid>
		<description><![CDATA[
促使程序员对架构和设计进行思考

这样的编码规范就很少见了，呵呵。其实这些内容是不是应该放在编码规范当中也是有争议的。不过我个人认为，不但应该放在编码规范中，而且应该是编码规范中最重要的部分。编码规范的本质和作用是什么，就是让代码更具弹性，更具可维护性。而架构设计对这个方面的影响力是最大的，没有理由不在编码规范中进行说明和规定。如果说命名规范或是其他的某种具体实践方法是“地方法规”，那么设计原则就是“宪法”。“宪法”决定了整个国家的法律框架。
如果不能在设计原则上达成统一和一致，即便是大家严格的遵守上述两个层面的编码规范，写出来的代码也极有可能是“貌合神离”。

但是一个模块的设计是否守遵循了特定的设计原则，或者在多大程度上符合设计原则的约定，是不好定出指标，也无法进行量化的。这可能也是一般编码规范中不包含设计原则的原因。每个人对设计原则的理解和领悟不尽相同。因此即使编码规范中包含了设计原则，怎样执行也是一个很伤脑筋的事情，有时还需要接合流程来进行实施。设计原则以及如何贯彻设计原则又是超出本文范围的两个话题了，有机会我会在其他文章中进行专门讨论。设计原则，建议可以直接参照《敏捷软件开发：原则、模式与实践》一书。

编码规范的贯彻实施

好的编码规范还只是一个开头，如果不能贯彻实施下去，那也只是一纸空文，没有任何用处。但要真正贯彻下去也不容易，毕竟代码不像一般的工业产品，可以量一下，称一下。我见过太多的项目，编码规范写得很好，但到了实际的代码上就完全是另外一回事了。也有一些是项目做到后期，代码维护的问题暴露出来后才开始实施和推行编码规范的，搞得所有人都异常的痛苦。要在以前的代码上改点东西，新代码按编码规范来写吧，混在旧代码中看起来很异类；把旧代码也按编码规范来重构了吧，一搞起来就无边无际了。所以推行编码规范一定要趁早。改掉一个坏习惯的成本绝对要高于养成一个好习惯。何况编码规范不是个人努力就可以了，是整个团队的事情。
在贯彻编码规范方面，我曾经工作过的一个开发团队有个很好的方法，说来也简单，就是review。每周划出固定的时间，轮流review开发团队中每个人的代码，一般一次一个人。找个有投影的会议室，被review的人在上面讲一下大致的流程和进行一些必要的介绍，其他三四个人在下面看。看到有问题了就讨论，确认是一个缺陷后就记下来，下去再修改。这样不但可以发现绝大部分的命名方面的问题，还可以发现不少架构设计上的问题。
这个方法虽然简单，但是很有效，关键是要持之以恒。由其是在新的团队里，在项目刚开始的时候实施。看似比较费时，其实是磨刀不误砍柴工。
我现在的团队，也采用了这一方法。因为是新成员，新团队。所以这个代码review的环节特别重要。在review代码的同时，其实也让大家熟悉了彼此的代码，不至于一个人的代码只有那个人能维护。而且review的过程也是一个大家一起学习的过程，可以交流很多的思想和经验。记得刚开始的时候，每周要review一天，很吃力。后来变为半天，上午周会，下午review。最后变成合并到和周会一起。
经过最开始的适应期以后，按编码规范进行编码就会变成很自然的事情，代码到了一定的规模后，新加进来的同事，也很容易被“同化”，这样就形成了一个良性循环。
在实施编码规范时，有一些团队使用“罚款”的方式来推行。如果发现有违反编码规范的地方就进行处罚。我个人很反感这种方式，编程本来是一个很愉悦的过程，这么一来就变成了一个战战兢兢很难受的事情了。就像我在谈过程改进时说的，从某种程度上来划分，开发过程可以分为两类：一类认为开发人员是积极主动的，因此它会减少对开发人员的约束，让开发人员发挥自己的热情和潜能；另一类认为开发人员是懒惰的，因此它通过定义详尽的流程，文档，事后评审，来对开发人员进行约束。编码规范在某种程度上也是这样的，有些是为了帮助开发人员写出更好的代码，它提供指引，提供参考，进行善意的约束，但最大限度的保留编程本身的快乐；有些则充斥着硬性规定和霸王条款，在这样的编码规范下写代码，编程的乐趣自然是遗失殆尽。
]]></description>
			<content:encoded><![CDATA[<p><strong><span style="color: #008000;"><br />
促使程序员对架构和设计进行思考<br />
</span></strong></p>
<p>这样的编码规范就很少见了，呵呵。其实这些内容是不是应该放在编码规范当中也是有争议的。不过我个人认为，不但应该放在编码规范中，而且应该是编码规范中最重要的部分。编码规范的本质和作用是什么，就是让代码更具弹性，更具可维护性。而架构设计对这个方面的影响力是最大的，没有理由不在编码规范中进行说明和规定。如果说命名规范或是其他的某种具体实践方法是“地方法规”，那么设计原则就是“宪法”。“宪法”决定了整个国家的法律框架。</p>
<p>如果不能在设计原则上达成统一和一致，即便是大家严格的遵守上述两个层面的编码规范，写出来的代码也极有可能是“貌合神离”。</p>
<p><span id="more-700"></span></p>
<p>但是一个模块的设计是否守遵循了特定的设计原则，或者在多大程度上符合设计原则的约定，是不好定出指标，也无法进行量化的。这可能也是一般编码规范中不包含设计原则的原因。每个人对设计原则的理解和领悟不尽相同。因此即使编码规范中包含了设计原则，怎样执行也是一个很伤脑筋的事情，有时还需要接合流程来进行实施。设计原则以及如何贯彻设计原则又是超出本文范围的两个话题了，有机会我会在其他文章中进行专门讨论。设计原则，建议可以直接参照《敏捷软件开发：原则、模式与实践》一书。</p>
<p><strong><span style="color: #008000;"><br />
编码规范的贯彻实施<br />
</span></strong></p>
<p>好的编码规范还只是一个开头，如果不能贯彻实施下去，那也只是一纸空文，没有任何用处。但要真正贯彻下去也不容易，毕竟代码不像一般的工业产品，可以量一下，称一下。我见过太多的项目，编码规范写得很好，但到了实际的代码上就完全是另外一回事了。也有一些是项目做到后期，代码维护的问题暴露出来后才开始实施和推行编码规范的，搞得所有人都异常的痛苦。要在以前的代码上改点东西，新代码按编码规范来写吧，混在旧代码中看起来很异类；把旧代码也按编码规范来重构了吧，一搞起来就无边无际了。所以推行编码规范一定要趁早。改掉一个坏习惯的成本绝对要高于养成一个好习惯。何况编码规范不是个人努力就可以了，是整个团队的事情。</p>
<p>在贯彻编码规范方面，我曾经工作过的一个开发团队有个很好的方法，说来也简单，就是review。每周划出固定的时间，轮流review开发团队中每个人的代码，一般一次一个人。找个有投影的会议室，被review的人在上面讲一下大致的流程和进行一些必要的介绍，其他三四个人在下面看。看到有问题了就讨论，确认是一个缺陷后就记下来，下去再修改。这样不但可以发现绝大部分的命名方面的问题，还可以发现不少架构设计上的问题。</p>
<p>这个方法虽然简单，但是很有效，关键是要持之以恒。由其是在新的团队里，在项目刚开始的时候实施。看似比较费时，其实是磨刀不误砍柴工。</p>
<p>我现在的团队，也采用了这一方法。因为是新成员，新团队。所以这个代码review的环节特别重要。在review代码的同时，其实也让大家熟悉了彼此的代码，不至于一个人的代码只有那个人能维护。而且review的过程也是一个大家一起学习的过程，可以交流很多的思想和经验。记得刚开始的时候，每周要review一天，很吃力。后来变为半天，上午周会，下午review。最后变成合并到和周会一起。</p>
<p>经过最开始的适应期以后，按编码规范进行编码就会变成很自然的事情，代码到了一定的规模后，新加进来的同事，也很容易被“同化”，这样就形成了一个良性循环。</p>
<p>在实施编码规范时，有一些团队使用“罚款”的方式来推行。如果发现有违反编码规范的地方就进行处罚。我个人很反感这种方式，编程本来是一个很愉悦的过程，这么一来就变成了一个战战兢兢很难受的事情了。就像我在谈<a title="过程改进的一点想法" href="http://www.a.com" target="_blank">过程改进</a>时说的，从某种程度上来划分，开发过程可以分为两类：一类认为开发人员是积极主动的，因此它会减少对开发人员的约束，让开发人员发挥自己的热情和潜能；另一类认为开发人员是懒惰的，因此它通过定义详尽的流程，文档，事后评审，来对开发人员进行约束。编码规范在某种程度上也是这样的，有些是为了帮助开发人员写出更好的代码，它提供指引，提供参考，进行善意的约束，但最大限度的保留编程本身的快乐；有些则充斥着硬性规定和霸王条款，在这样的编码规范下写代码，编程的乐趣自然是遗失殆尽。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.devxx.com/2009/08/700.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>关于编码规范（一）</title>
		<link>http://www.devxx.com/2009/08/695.html</link>
		<comments>http://www.devxx.com/2009/08/695.html#comments</comments>
		<pubDate>Mon, 10 Aug 2009 12:03:31 +0000</pubDate>
		<dc:creator>阿凯</dc:creator>
				<category><![CDATA[我的编程实践总结]]></category>
		<category><![CDATA[技术类]]></category>
		<category><![CDATA[代码review]]></category>
		<category><![CDATA[代码格式]]></category>
		<category><![CDATA[命名法]]></category>
		<category><![CDATA[架构设计]]></category>
		<category><![CDATA[测试驱动]]></category>
		<category><![CDATA[编码规范]]></category>
		<category><![CDATA[编程实践]]></category>
		<category><![CDATA[编程规范]]></category>
		<category><![CDATA[设计原则]]></category>
		<category><![CDATA[过程改进]]></category>

		<guid isPermaLink="false">http://www.devxx.com/?p=695</guid>
		<description><![CDATA[好的编码习惯一定要尽早的养成，就象小孩要从小养成良好的生活习惯一样。好的生活学习习惯会对小孩的一生产生巨大的影响，编码规范对于开发人员和开发团队来说，效果也一样。因此对于一个开发团队来说，在一开始就确立一个好的编码规范，并且所有人都严格执行，比什么都重要。等到发现代码杂乱无章，无法很好的理解各自的代码时，再来推行一个编码规范就晚了，要么矫正的成本太高，要么就很难真正的推行下去。
那么一个什么样的编码规范才算是一个好的编码规范呢？一个好的编码规范应该包含有哪些内容，有哪些特点呢？


对命名和代码格式进行规范

一般的编码规范至少会规定各种命名方法(文件，类，方法，变量等)，以及如何格式化代码。这些当然重要，统一的命名规范和代码格式使整个项目的代码至少在外观上统一，有助于代码维护。坊间流行的命名方法和代码格式多种多样，有的人喜欢微软的匈牙利命名法，有的人喜欢JAVA风格的命名方法，有的人喜欢Unix风格的命名方法。关于各种命名法和代码格式优劣的争论从来没有停止过，其实命名规范的关键是使得代码有良好的可读性并且有一个统一的风格，仅此而已。具体使用哪一种并不重要，关键是自始至终的贯彻下去。关于命名还有一个值得注意的地方，就是命名一定要充分，要能表达命名主体(变量、方法、类等)的具体含义，不能是含混不清的，不要滥用缩写（除非大家公认的缩写，比如len代表length），尽量用完整的单词。现在的编辑器都支持自动补全，名字长一点，多几个字母没什么大不了，有意义，便于理解的命名对后继维护将带来很大的便利。
我见过很多在命名和代码格式方面规范的很细致，很“死板”的编码规范。有的甚至对注释的行数进行了硬性的规定(要求至少和代码数量相当)。我自己是最反感教条式的“八股文”。规范并不等同于教条，其实规范命名的本质是让代码更可读，达到这个目的也就OK了。因此能放宽的地方应该尽量放宽，让程序员有足够的自由度。由其是注释，注释的目的是对代码进行补充说明。换句话说，应该尽量的鼓励程序员写出不言自明的代码，写出不需要注释也可以轻松看懂的代码。在无法达到上述目的的时候，再用注释进行补充说明。（关于怎样写出不言自明的代码，我们在另一篇文章里介绍）注释一多，维护注释和代码的同步也不是件轻松的事，项目周期越长，这个问题越明显。

促进、引导程序员进行好的编程实践

好的编码规范会引导、促使或是“强迫”程序员使用一些好的编程实践。比如，对类或是方法的规模进行要求。庞大的类和方法一般都是代码缺乏良好划分和规划的结果，这样的代码即不利于理解，也不好维护。对类和方法的规模进行要求，至少会强迫程序员对代码进行思考，进行规划和拆分。经过这样的规划和拆分后的代码往往更利于重构和重用。
有的编码规范会明确的引入一些好的编程实践。比如，要求为所有的接口和重要的方法建立测试案例，以测试驱动的方式开发。(关于测试驱动开发(TDD)的好处，有机会单独写个文章来阐述)。有些会将代码重构的一些指导性原则列入到编码规范中。还有一些使用了私有架构的，也会结合具体的架构，对编码进行要求。比如有些私有架构自己实现了单根的对象层次和垃圾回收，这些对系统中类的实现都有特殊的要求。
（未完待续）
]]></description>
			<content:encoded><![CDATA[<p>好的编码习惯一定要尽早的养成，就象小孩要从小养成良好的生活习惯一样。好的生活学习习惯会对小孩的一生产生巨大的影响，编码规范对于开发人员和开发团队来说，效果也一样。因此对于一个开发团队来说，在一开始就确立一个好的编码规范，并且所有人都严格执行，比什么都重要。等到发现代码杂乱无章，无法很好的理解各自的代码时，再来推行一个编码规范就晚了，要么矫正的成本太高，要么就很难真正的推行下去。</p>
<p>那么一个什么样的编码规范才算是一个好的编码规范呢？一个好的编码规范应该包含有哪些内容，有哪些特点呢？</p>
<p><span id="more-695"></span></p>
<p><strong><span style="color: #008000;"><br />
对命名和代码格式进行规范<br />
</span></strong></p>
<p>一般的编码规范至少会规定各种命名方法(文件，类，方法，变量等)，以及如何格式化代码。这些当然重要，统一的命名规范和代码格式使整个项目的代码至少在外观上统一，有助于代码维护。坊间流行的命名方法和代码格式多种多样，有的人喜欢微软的匈牙利命名法，有的人喜欢JAVA风格的命名方法，有的人喜欢Unix风格的命名方法。关于各种命名法和代码格式优劣的争论从来没有停止过，其实命名规范的关键是使得代码有良好的可读性并且有一个统一的风格，仅此而已。具体使用哪一种并不重要，关键是自始至终的贯彻下去。关于命名还有一个值得注意的地方，就是命名一定要充分，要能表达命名主体(变量、方法、类等)的具体含义，不能是含混不清的，不要滥用缩写（除非大家公认的缩写，比如len代表length），尽量用完整的单词。现在的编辑器都支持自动补全，名字长一点，多几个字母没什么大不了，有意义，便于理解的命名对后继维护将带来很大的便利。</p>
<p>我见过很多在命名和代码格式方面规范的很细致，很“死板”的编码规范。有的甚至对注释的行数进行了硬性的规定(要求至少和代码数量相当)。我自己是最反感教条式的“八股文”。规范并不等同于教条，其实规范命名的本质是让代码更可读，达到这个目的也就OK了。因此能放宽的地方应该尽量放宽，让程序员有足够的自由度。由其是注释，注释的目的是对代码进行补充说明。换句话说，应该尽量的鼓励程序员写出不言自明的代码，写出不需要注释也可以轻松看懂的代码。在无法达到上述目的的时候，再用注释进行补充说明。（关于怎样写出不言自明的代码，我们在另一篇文章里介绍）注释一多，维护注释和代码的同步也不是件轻松的事，项目周期越长，这个问题越明显。</p>
<p><strong><span style="color: #008000;"><br />
促进、引导程序员进行好的编程实践<br />
</span></strong></p>
<p>好的编码规范会引导、促使或是“强迫”程序员使用一些好的编程实践。比如，对类或是方法的规模进行要求。庞大的类和方法一般都是代码缺乏良好划分和规划的结果，这样的代码即不利于理解，也不好维护。对类和方法的规模进行要求，至少会强迫程序员对代码进行思考，进行规划和拆分。经过这样的规划和拆分后的代码往往更利于重构和重用。</p>
<p>有的编码规范会明确的引入一些好的编程实践。比如，要求为所有的接口和重要的方法建立测试案例，以测试驱动的方式开发。(关于测试驱动开发(TDD)的好处，有机会单独写个文章来阐述)。有些会将代码重构的一些指导性原则列入到编码规范中。还有一些使用了私有架构的，也会结合具体的架构，对编码进行要求。比如有些私有架构自己实现了单根的对象层次和垃圾回收，这些对系统中类的实现都有特殊的要求。</p>
<p><strong>（未完待续）</strong></p>
]]></content:encoded>
			<wfw:commentRss>http://www.devxx.com/2009/08/695.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>标记待实现功能</title>
		<link>http://www.devxx.com/2009/08/690.html</link>
		<comments>http://www.devxx.com/2009/08/690.html#comments</comments>
		<pubDate>Fri, 07 Aug 2009 04:10:38 +0000</pubDate>
		<dc:creator>阿凯</dc:creator>
				<category><![CDATA[小技巧]]></category>
		<category><![CDATA[我的编程实践总结]]></category>
		<category><![CDATA[技术类]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[代码标记]]></category>
		<category><![CDATA[宏]]></category>
		<category><![CDATA[预处理指令]]></category>

		<guid isPermaLink="false">http://www.devxx.com/?p=690</guid>
		<description><![CDATA[有时为了快速的把代码的架子搭起来，并编译通过。会把一些负责具体实现细节的方法先空置。整个代码能编译通过后，再一边调试一边把那些空方法“填”好。
我一般是在这类空方法中加一个注释进行标注， 并让方法直接返回一个错误值。比如返回指针的方法就让它返回NULL，返回BOOL值的方法就让它返回false。有时还会在方法里放一个失败的断言，比如：

BOOL some_function()
{
   //PK Not implement yet
   assert(false);
   return FALSE;
}


这样如果实在忘了实现这个方法了，在调试时当代码运动到这里时就会触发断言。
最近在新版的《windows核心编程》的附录里看到一个超好的技巧，更好的解决了标注的问题。象上面我用的那两个方法，还不是很方便。注释法，需要自己记得不停去全局搜特定的注释，如果忘了搜，或是输入注释时敲错了单词，就容易漏掉。如果程序的逻辑没有运行到放置了断言的方法中，断言法也就不起作用了。
《windows核心编译》里提供的这个方法更好的解决了这些问题。它通过一个预处理指令(#pragma message)在编译时输出一行指定的信息，并且通过一个宏定义在指定的信息前输出这个指令所在的文件名和行号。这样我们就可以象操作编译错误时一样，在output窗口中双击那一行信息，自动定位到指令所在的行上。
下面就是这个宏的定义：

#define chSTR2(x)	#x
#define chSTR(x)	chSTR2(x)
#define chMSG(desc)     message(__FILE__"("chSTR(__LINE__)"):"#desc)

这个定义虽然简单但很精巧，__FILE__和__LINE__是两个预处理器定义的宏，值分别为当前的文件名和行号。#将它后面的的宏变量求值并字符串化。
建议直接把这三行COPY到stdafx.h头文件中，这样在工程的所有地方都可以引用。
利用这个宏，我们只需要在代码里需要标注的地方输入如下的行，注意括号内的内容不需要用引用括住，因为我们用了#进行字符串化。

BOOL some_function()
{
#pragma chMSG(not implement yet)
   return FALSE;
}

编译时会在output窗口中产生类似下面的输出：

1>d:\my_project\trunk\aural_superman\write_component_collection.cpp(86):not implement yet

双击就可以跳到相应的代码行。
我在这个基础上又加了一个宏：

#define NOT_IMPLEMENT_YET chMSG(not implement yet)

这样只需要以下行：

#pragma NOT_IMPLEMENT_YET

就可以做标记了。
用这种方式，还可以定义很多其他的标记。比如：对需要在以后进行优化的地方，我们可以定义以下的标记宏：

#define OPTIMIZATION chMSG(should be optimized)

由于#在预处理指令里有特殊功能，而预处理系统又不提供转义符。所以我没找到方法把#pragma也塞到宏定义里面去。如果可以放进去的话，会更简洁。哪位达人如果知道方法的话，请记得告诉我。
]]></description>
			<content:encoded><![CDATA[<p>有时为了快速的把代码的架子搭起来，并编译通过。会把一些负责具体实现细节的方法先空置。整个代码能编译通过后，再一边调试一边把那些空方法“填”好。</p>
<p>我一般是在这类空方法中加一个注释进行标注， 并让方法直接返回一个错误值。比如返回指针的方法就让它返回NULL，返回BOOL值的方法就让它返回false。有时还会在方法里放一个失败的断言，比如：</p>
<pre name="code" class="cpp">
BOOL some_function()
{
   //PK Not implement yet
   assert(false);
   return FALSE;
}
</pre>
<p><span id="more-690"></span></p>
<p>这样如果实在忘了实现这个方法了，在调试时当代码运动到这里时就会触发断言。</p>
<p>最近在新版的《windows核心编程》的附录里看到一个超好的技巧，更好的解决了标注的问题。象上面我用的那两个方法，还不是很方便。注释法，需要自己记得不停去全局搜特定的注释，如果忘了搜，或是输入注释时敲错了单词，就容易漏掉。如果程序的逻辑没有运行到放置了断言的方法中，断言法也就不起作用了。</p>
<p>《windows核心编译》里提供的这个方法更好的解决了这些问题。它通过一个预处理指令(#pragma message)在编译时输出一行指定的信息，并且通过一个宏定义在指定的信息前输出这个指令所在的文件名和行号。这样我们就可以象操作编译错误时一样，在output窗口中双击那一行信息，自动定位到指令所在的行上。</p>
<p>下面就是这个宏的定义：</p>
<pre name="code" class="cpp">
#define chSTR2(x)	#x
#define chSTR(x)	chSTR2(x)
#define chMSG(desc)     message(__FILE__"("chSTR(__LINE__)"):"#desc)
</pre>
<p>这个定义虽然简单但很精巧，__FILE__和__LINE__是两个预处理器定义的宏，值分别为当前的文件名和行号。#将它后面的的宏变量求值并字符串化。</p>
<p>建议直接把这三行COPY到stdafx.h头文件中，这样在工程的所有地方都可以引用。</p>
<p>利用这个宏，我们只需要在代码里需要标注的地方输入如下的行，注意括号内的内容不需要用引用括住，因为我们用了#进行字符串化。</p>
<pre name="code" class="cpp">
BOOL some_function()
{
#pragma chMSG(not implement yet)
   return FALSE;
}
</pre>
<p>编译时会在output窗口中产生类似下面的输出：</p>
<pre name="code" class="txt">
1>d:\my_project\trunk\aural_superman\write_component_collection.cpp(86):not implement yet
</pre>
<p>双击就可以跳到相应的代码行。</p>
<p>我在这个基础上又加了一个宏：</p>
<pre name="code" class="cpp">
#define NOT_IMPLEMENT_YET chMSG(not implement yet)
</pre>
<p>这样只需要以下行：</p>
<pre name="code" class="cpp">
#pragma NOT_IMPLEMENT_YET
</pre>
<p>就可以做标记了。</p>
<p>用这种方式，还可以定义很多其他的标记。比如：对需要在以后进行优化的地方，我们可以定义以下的标记宏：</p>
<pre name="code" class="cpp">
#define OPTIMIZATION chMSG(should be optimized)
</pre>
<p>由于#在预处理指令里有特殊功能，而预处理系统又不提供转义符。所以我没找到方法把#pragma也塞到宏定义里面去。如果可以放进去的话，会更简洁。哪位达人如果知道方法的话，请记得告诉我。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.devxx.com/2009/08/690.html/feed</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
	</channel>
</rss>
