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

<channel>
	<title>随.心.所.记 &#187; Nabialek</title>
	<atom:link href="http://www.realdodo.com/tag/nabialek/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.realdodo.com</link>
	<description>享受生活的乐趣与烦恼</description>
	<lastBuildDate>Fri, 25 Jun 2010 18:49:41 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>设计哲学：职责单一还是接口单一</title>
		<link>http://www.realdodo.com/2009/03/26/321/</link>
		<comments>http://www.realdodo.com/2009/03/26/321/#comments</comments>
		<pubDate>Thu, 26 Mar 2009 14:22:21 +0000</pubDate>
		<dc:creator>realdodo</dc:creator>
				<category><![CDATA[技术文章]]></category>
		<category><![CDATA[boost]]></category>
		<category><![CDATA[boost.spirit]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[C++模板]]></category>
		<category><![CDATA[Nabialek]]></category>
		<category><![CDATA[工作]]></category>
		<category><![CDATA[康神]]></category>
		<category><![CDATA[程序]]></category>
		<category><![CDATA[设计模式]]></category>

		<guid isPermaLink="false">http://www.realdodo.com/?p=321</guid>
		<description><![CDATA[今天跟康神讨论设计，他提到一个设计哲学（这个词就很让人崇拜），很是有道理。大概意思是说，在设计中，有人喜欢职责单一，所有东西分的很细，有人喜欢接口单一，保证接口稳定，这是个设计哲学问题，各有优缺。
我自己是一个极度喜欢保持职责单一的人，面对康神设计的接口单一但内部逻辑较复杂的模块，多少有点想重构的冲动。其实这两种设计哲学都没太大问题，确实得看需求来确定到底用什么。
接口单一的设计，最典型例子的就是大家都知道的printf，这个东西博大精深，可以组合打印任意格式的文字，如果C标准委员会乐意，还可以通过扩展格式字符串来实现更多的打印选项，同时保证100%向下兼容。还有很多接口，它们通过传递一个结构来传递参数，这个结构拥有众多成员，每次调用接口时只用其中一部分。这些接口也能像printf一样易扩展，康神设计的就是后面这种接口。
这样设计的好处是保持整个系统接口的统一和稳定，这对于一个大型工业系统来说至关重要，因为牵一发动全身，改变任何一个通用接口都会付出很大代价。但它的缺点也十分明显，主要体现是效率不高和模块职责不明。
可以想象，printf的运行效率并不高，对于%以及后面参数的解析是在运行时完成的，每次运行都会在解析格式字符串上花时间。传结构的缺点是内存使用效率不高，并且随着业务逻辑不断变复杂，结构会大到不能接受的地步，最终肯定在结构中设置一个类似于格式字符串或者表达式的东西，逐渐转变成printf的设计模式。
同时，由于接口过于强大，在接口背后隐藏的逻辑也必定复杂无比。想想解析格式字符串或解析结构时该需要多少烦人的if&#8230;else，就知道要实现这种设计将会写出多少逻辑复杂的代码，会产生多少的bad smell，给后来维护的人造成不少困扰，甚至会引入不少潜在的错误。
反观职责单一的设计，没有接口单一设计的缺点，但劣势也很明显，就是没有统一的接口，使得整个系统模块之间的调用方式始终难以稳定。如果整个系统由一个人开发还好，如果多人协作，则会大大降低开发效率，会将大量时间用在沟通接口和解决联调问题上面。公平的说，职责单一的设计也可以用facade模式来提供统一接口，但是facade模式的实现代码又会变得和接口统一时一样，没有根本改变，只是一个折中和权衡。
那是否说明“完美”的职责单一的设计不适合大型开发呢？也不一定，因为我们有C++和无比变态的模板技巧。
比如我们可以设计出一个这样的print，功能类似于printf。注意，这个函数设计成只在print的时候才输出，expression类不会造成任何输出。

pattern p = builder() &#60;&#60; "uid: " &#60;&#60; uid &#60;&#60; " uname: " &#60;&#60; name;
uid = 1000;
name = "realdodo";
print(p); // 打印出"uid: 1000 uname: realdodo"

如果熟悉boost.spirit的朋友肯定觉得这个代码很眼熟，没办法，谁要我是这个库的忠实粉丝呢。在这里，pattern_builder类只是一个模板生成器，而不进行任何计算（不同于sstream这样的类），它用&#60;&#60;运算符针对各种类型生成各种特化的模板类，类似于一个嵌套的pair&#60;&#62;。这些模板类层层嵌套，但是pattern类可以轻松搞定，方法就是设计一个concrete_pattern类。

struct concrete_pattern_base
{
    concrete_pattern_base() {}
    virtual ~concrete_pattern_base() {}
    virtual string to_string() const = 0;
};

tempate &#60;typename builder&#62;
struct concrete_pattern : public concrete_pattern_base
{
    builder builder_;
    concrete_pattern(const builder &#38; b)
        : builder_(b)
    {}

    virtual string to_string() const
    {
        // 用模板特化的方法实现to_string()方法，这里略去
    }
};

这样，pattern的实现就非常简单。

class pattern
{
    concrete_pattern_base * pattern_;

public:
    template &#60;typename builder&#62;
pattern(const builder [...]]]></description>
			<content:encoded><![CDATA[<p>今天跟<a title="拜……" href="http://blog.kangkang.org/">康神</a>讨论设计，他提到一个设计哲学（这个词就很让人崇拜），很是有道理。大概意思是说，在设计中，有人喜欢职责单一，所有东西分的很细，有人喜欢接口单一，保证接口稳定，这是个设计哲学问题，各有优缺。</p>
<p>我自己是一个极度喜欢保持职责单一的人，面对<a title="拜……" href="http://blog.kangkang.net/">康神</a>设计的接口单一但内部逻辑较复杂的模块，多少有点想重构的冲动。其实这两种设计哲学都没太大问题，确实得看需求来确定到底用什么。</p>
<p>接口单一的设计，最典型例子的就是大家都知道的printf，这个东西博大精深，可以组合打印任意格式的文字，如果C标准委员会乐意，还可以通过扩展格式字符串来实现更多的打印选项，同时保证100%向下兼容。还有很多接口，它们通过传递一个结构来传递参数，这个结构拥有众多成员，每次调用接口时只用其中一部分。这些接口也能像printf一样易扩展，康神设计的就是后面这种接口。</p>
<p>这样设计的好处是保持整个系统接口的统一和稳定，这对于一个大型工业系统来说至关重要，因为牵一发动全身，改变任何一个通用接口都会付出很大代价。但它的缺点也十分明显，主要体现是效率不高和模块职责不明。</p>
<p>可以想象，printf的运行效率并不高，对于%以及后面参数的解析是在运行时完成的，每次运行都会在解析格式字符串上花时间。传结构的缺点是内存使用效率不高，并且随着业务逻辑不断变复杂，结构会大到不能接受的地步，最终肯定在结构中设置一个类似于格式字符串或者表达式的东西，逐渐转变成printf的设计模式。</p>
<p>同时，由于接口过于强大，在接口背后隐藏的逻辑也必定复杂无比。想想解析格式字符串或解析结构时该需要多少烦人的if&#8230;else，就知道要实现这种设计将会写出多少逻辑复杂的代码，会产生多少的bad smell，给后来维护的人造成不少困扰，甚至会引入不少潜在的错误。</p>
<p>反观职责单一的设计，没有接口单一设计的缺点，但劣势也很明显，就是没有统一的接口，使得整个系统模块之间的调用方式始终难以稳定。如果整个系统由一个人开发还好，如果多人协作，则会大大降低开发效率，会将大量时间用在沟通接口和解决联调问题上面。公平的说，职责单一的设计也可以用facade模式来提供统一接口，但是facade模式的实现代码又会变得和接口统一时一样，没有根本改变，只是一个折中和权衡。</p>
<p>那是否说明“完美”的职责单一的设计不适合大型开发呢？也不一定，因为我们有C++和无比变态的模板技巧。</p>
<p>比如我们可以设计出一个这样的print，功能类似于printf。注意，这个函数设计成只在print的时候才输出，expression类不会造成任何输出。<br />
<code><br />
pattern p = builder() &lt;&lt; "uid: " &lt;&lt; uid &lt;&lt; " uname: " &lt;&lt; name;<br />
uid = 1000;<br />
name = "realdodo";<br />
print(p); // 打印出"uid: 1000 uname: realdodo"<br />
</code><br />
如果熟悉<a href="http://www.boost.org/doc/libs/1_38_0/libs/spirit/index.html">boost.spirit</a>的朋友肯定觉得这个代码很眼熟，没办法，谁要我是这个库的忠实粉丝呢。在这里，pattern_builder类只是一个模板生成器，而不进行任何计算（不同于sstream这样的类），它用&lt;&lt;运算符针对各种类型生成各种特化的模板类，类似于一个嵌套的pair&lt;&gt;。这些模板类层层嵌套，但是pattern类可以轻松搞定，方法就是设计一个concrete_pattern类。<br />
<code><br />
struct concrete_pattern_base<br />
{<br />
    concrete_pattern_base() {}<br />
    virtual ~concrete_pattern_base() {}<br />
    virtual string to_string() const = 0;<br />
};</code><br />
<code><br />
tempate &lt;typename builder&gt;<br />
struct concrete_pattern : public concrete_pattern_base<br />
{<br />
    builder builder_;<br />
    concrete_pattern(const builder &amp; b)<br />
        : builder_(b)<br />
    {}</code><br />
<code><br />
    virtual string to_string() const<br />
    {<br />
        // 用模板特化的方法实现to_string()方法，这里略去<br />
    }<br />
};<br />
</code><br />
这样，pattern的实现就非常简单。<br />
<code><br />
class pattern<br />
{<br />
    concrete_pattern_base * pattern_;</code><br />
<code><br />
public:<br />
    template &lt;typename builder&gt;<br />
pattern(const builder &amp; b)<br />
    {<br />
        pattern_ = new concrete_pattern(b);<br />
    }</code><br />
<code><br />
public:<br />
    // 其他方法……如果需要获得当前字符串，只需调用pattern_-&gt;to_string()<br />
};<br />
</code><br />
注意，按照print的语义，pattern虽然也要实现一个类似于to_string()的方法，但它完全没有必要从concrete_pattern_base派生。</p>
<p>这种设计的好处是在保证职责单一的时候同时维护了接口统一。实际上，那个复杂的接口逻辑还是存在，但是由编译器在编译期间完成了。程序员只用关注builder、pattern和print的实现，它们三个一个负责生成表达式，一个负责计算表达式，一个负责输出，职责明确，泾渭分明，超级高效，实现各种新的需求也毫无问题。</p>
<p>可惜，这世界上没有银弹，这种滥用模板技巧的方法也有很大的问题。最大问题是可维护性。模板编程的复杂度远超过普通面向对象编程的复杂度，如果开发/维护者没有很深的基础，很可能都无法看懂这些代码。次要问题是扩展成RPC调用非常困难。对于大型工业应用，很可能一个接口因为某种原因要变成跨进程甚至于跨机器的调用，这就需要序列化所有的参数。模板是静态技巧，要序列化就一定会生成不亚于printf格式字符串的东西，而且还要实现一个客户端解析这个烦人的东西，那真是吃力不讨好，还不如直接实现一个printf好呢。</p>
<p>所以，最后的结论是没有结论，设计要随着需求而定，这才是王道。<strong>没有万能的设计，只有最符合需求的设计，并且设计要随着需求改变而改变，不能止境</strong>。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.realdodo.com/2009/03/26/321/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>什么是“Nabialek trick”？</title>
		<link>http://www.realdodo.com/2007/05/09/180/</link>
		<comments>http://www.realdodo.com/2007/05/09/180/#comments</comments>
		<pubDate>Wed, 09 May 2007 15:40:00 +0000</pubDate>
		<dc:creator>realdodo</dc:creator>
				<category><![CDATA[技术文章]]></category>
		<category><![CDATA[boost]]></category>
		<category><![CDATA[boost.spirit]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[C++模板]]></category>
		<category><![CDATA[Nabialek]]></category>
		<category><![CDATA[trick]]></category>
		<category><![CDATA[程序]]></category>

		<guid isPermaLink="false">http://www.realdodo.com/2007/05/09/180/</guid>
		<description><![CDATA[现在是时候了，经过这些天的学习和尝试，我终于知道boost.spirit里谈到的Nabialek trick是什么东西了，当然应该大方的与大家分享。
这个Nabialek trick其实并不是编译期间的技巧，而是利用查表的方式将一些静态的类型推导变成简单的运行时调用，用性能损失换取编译期间的复杂，是一种相反的过程。可以说，Nabialek trick给人的感觉是完完全全的没有意义，要知道C++的template就是为了提升运行时性能而增加编译期间运算，这个反其道而行意义何在？其实说白了，意义就在于：消除模板技巧带来的疯狂的自动类型推导，以及解除对某些类型的静态绑定。前者意义在于可以提升编译速度，而且也可以避免达到模板嵌套上限（比如MSVC 7.1，最多只能嵌套256层，这已经算不错了），后者意义在于可以让复杂的表达式变得更容易匹配、更快的执行和更加灵活（所谓的将“线性非决定式”转化为“决定式”，将“或”逻辑从表达式中剔除，避免不必要回溯以加快速度），但是由于创建查找表的过程涉及到内存分配，所以后者并不能总是达到很好效果。
我们再从正面来想想Expression Templates给编程带来的困难，请看下面的示例代码（来自boost.spirit的帮助/libs/spirit/doc/techniques.html，我做了一些简化和修改）：

r =
"//" &#62;&#62; *(anychar_p - 'n') &#62;&#62; 'n'
&#124; "/*" &#62;&#62; *(anychar_p - "*/") &#62;&#62; "*/"
;

在这里，r的类型就是：

alternative&#60;sequence&#60;sequence&#60;strlit&#60;const char*&#62;, kleene_star&#60;difference&#60;anychar_parser, chlit&#60;char&#62; &#62; &#62; &#62;, chlit&#60;char&#62; &#62;, sequence&#60;sequence&#60;
strlit&#60;const char*&#62;, kleene_star&#60;difference&#60;anychar_parser, strlit&#60;const char*&#62; &#62; &#62; &#62;, strlit&#60;const char*&#62; &#62; &#62;

是的，我没指望任何人能够看懂……不过假如做一下的赋值：

line1 = "//" &#62;&#62; *(anychar_p - 'n') &#62;&#62; 'n';
line2 = "/*" &#62;&#62; *(anychar_p - "*/") &#62;&#62; "*/";
r = line1 &#124; line2;

这样的代码应该好理解一些，但是没有本质不同。再来改改：

line1 [...]]]></description>
			<content:encoded><![CDATA[<p>现在是时候了，经过这些天的学习和尝试，我终于知道boost.spirit里谈到的Nabialek trick是什么东西了，当然应该大方的与大家分享。</p>
<p>这个Nabialek trick其实并不是编译期间的技巧，而是利用查表的方式将一些静态的类型推导变成简单的运行时调用，用性能损失换取编译期间的复杂，是一种相反的过程。可以说，Nabialek trick给人的感觉是完完全全的没有意义，要知道C++的template就是为了提升运行时性能而增加编译期间运算，这个反其道而行意义何在？其实说白了，意义就在于：消除模板技巧带来的疯狂的自动类型推导，以及解除对某些类型的静态绑定。前者意义在于可以提升编译速度，而且也可以避免达到模板嵌套上限（比如MSVC 7.1，最多只能嵌套256层，这已经算不错了），后者意义在于可以让复杂的表达式变得更容易匹配、更快的执行和更加灵活（所谓的将“线性非决定式”转化为“决定式”，将“或”逻辑从表达式中剔除，避免不必要回溯以加快速度），但是由于创建查找表的过程涉及到内存分配，所以后者并不能总是达到很好效果。</p>
<p>我们再从正面来想想Expression Templates给编程带来的困难，请看下面的示例代码（来自boost.spirit的帮助/libs/spirit/doc/techniques.html，我做了一些简化和修改）：</p>
<p><code><br />
r =<br />
"//" &gt;&gt; *(anychar_p - 'n') &gt;&gt; 'n'<br />
| "/*" &gt;&gt; *(anychar_p - "*/") &gt;&gt; "*/"<br />
;<br />
</code></p>
<p>在这里，r的类型就是：</p>
<p><code><br />
alternative&lt;sequence&lt;sequence&lt;strlit&lt;const char*&gt;, kleene_star&lt;difference&lt;anychar_parser, chlit&lt;char&gt; &gt; &gt; &gt;, chlit&lt;char&gt; &gt;, sequence&lt;sequence&lt;<br />
strlit&lt;const char*&gt;, kleene_star&lt;difference&lt;anychar_parser, strlit&lt;const char*&gt; &gt; &gt; &gt;, strlit&lt;const char*&gt; &gt; &gt;<br />
</code></p>
<p>是的，我没指望任何人能够看懂……不过假如做一下的赋值：<br />
<code><br />
line1 = "//" &gt;&gt; *(anychar_p - 'n') &gt;&gt; 'n';<br />
line2 = "/*" &gt;&gt; *(anychar_p - "*/") &gt;&gt; "*/";<br />
r = line1 | line2;<br />
</code></p>
<p>这样的代码应该好理解一些，但是没有本质不同。再来改改：</p>
<p><code><br />
line1 = "//" &gt;&gt; *(anychar_p - 'n') &gt;&gt; 'n';<br />
line2 = "/*" &gt;&gt; *(anychar_p - "*/") &gt;&gt; "*/";<br />
some_helper helper;  // some_helper并不是boost.spirit的类，是我虚构出来的。<br />
helper.add("//", &amp;line1);<br />
helper.add("/*", &amp;line2);<br />
r = helper;<br />
</code></p>
<p>这里可以清晰地看到，只要这个some_helper的add()能够支持line1和line2类型，并且line1和line2的类型都实现了一个公共的抽象接口，那么r的类型就很单纯了。不过boost.spirit出于各种考虑，整个类体系非常复杂，包括rule&lt;&gt;、parser&lt;&gt;、abstract_parser&lt;&gt;、sequence&lt;&gt;等，它们的关系我花了好几天终于弄明白了，不过要说清楚恐怕还要花费写口舌，我等以后有时间以后再来详细介绍吧。</p>
<p>有了这个helper（在boost.spirit里面实际上是symbols&lt;&gt;），我们就可以抛弃“或”逻辑，表达式匹配也就没有回溯的问题，提高匹配的速度。同时可以看出，这种“优化”在很大程度上似乎并不是特别必要，毕竟Expression Templates是可以在编译期间决定这种优化方式的（甚至于可以通过模板的偏特化优化运算顺序），所以我真的觉得这种技巧在这种应用环境里面恐怕不会存在太久……</p>
<p><img id="myFxSearchImg" style="border: medium none; position: absolute; z-index: 2147483647; opacity: 0.6; display: none;" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAADsElEQVR4nK2VTW9VVRSGn33OPgWpYLARbKWhQlCHTogoSkjEkQwclEQcNJEwlfgD/AM6NBo1xjhx5LyJ0cYEDHGkJqhtBGKUpm3SFii3vb2956wPB/t+9raEgSs52fuus89613rftdcNH8/c9q9++oe/Vzb5P+3McyNcfm2CcPj9af9w6gwjTwzvethx3Bx3x8xwd1wNM8dMcTNUHTfFLPnX6nVmZpeIYwf3cWD/PhbrvlPkblAzVFurKS6GmmGqqComaS+qmBoTI0Ncu3mXuGvWnrJ+ZSxweDgnkHf8ndVTdbiT3M7cQp2Z31dRTecHAfqydp4ejhwazh6Zezfnu98E1WIQwB3crEuJ2Y45PBTAQUVR9X4At66AppoEVO1Q8sgAOKJJjw6Am6OquDmvHskZ3R87gW+vlHz98zpmiqphkkRVbQtsfPTOC30lJKFbFTgp83bWh7Zx/uX1B6w3hI3NkkZTqEpBRDBRzG2AQHcwcYwEkOGkTERREbLQ/8HxJwuW7zdYrzfZ2iopy4qqEspKaDYravVm33k1R91Q69FA1VBRzFIVvXbx5AgXT44A8MWP81yfu0utIR2aVK3vfCnGrcUNxp8a7gKYKiLCvY2SUvo/aNtnM3e49ucK9S3p0aDdaT0UAVsKi2tVi6IWwNL9JvdqTdihaz79/l+u/rHMxmaJVMLkS2OoKKLWacdeE3IsSxctc2D5Qcl6vUlVVgNt+fkPPcFFmTw1xruvT7SCd7nuVhDQvECzJH90h0azRKoKFRkAmP5lKTWAGRdefoZL554FQNUxB92WvYeA5UN4PtSqwB2phKqsqMpBgAunRhFR3j49zuU3jnX8k6fHEQKXzh1jbmGDuYU6s4t1rt6socUeLLZHhYO2AHSHmzt19ihTZ48O8Hzl/AmunD/BjTvrvPfNX3hWsNpwJCvwYm+ngug4UilSCSq6k8YPtxDwfA+WRawIWFbgscDiULcCEaWqBFOlrLazurupOSHLqGnEKJAY8TwBEHumqUirAjNm52vEPPRV4p01XXMPAQhUBjcWm9QZwijwokgAeYHlHYA06KR1cT6ZvoV56pDUJQEjw0KeaMgj1hPEY4vz2A4eW0/e1qA7KtQdsxTYAG0H3iG4xyK1Y+xm7XmEPOJZDiENzLi2WZHngeOjj2Pe+sMg4GRYyLAsx7ME4FnsyTD9pr0PEc8zPGRAwKXBkYOPEd96cZRvf11g9MDe7e3R4Z4Q+vyEnn3P4t0XzK/W+ODN5/kPfRLewAJVEQ0AAAAASUVORK5CYII%3D" alt="" width="24" height="24" /></p>
]]></content:encoded>
			<wfw:commentRss>http://www.realdodo.com/2007/05/09/180/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
