<?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; 设计模式</title>
	<atom:link href="http://www.realdodo.com/tag/%e8%ae%be%e8%ae%a1%e6%a8%a1%e5%bc%8f/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>
	</channel>
</rss>
