<?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="https://blog.gmem.cc/tag/%e8%ae%be%e8%ae%a1%e6%a8%a1%e5%bc%8f/feed" rel="self" type="application/rss+xml" />
	<link>https://blog.gmem.cc</link>
	<description></description>
	<lastBuildDate>Sun, 19 Apr 2026 07:54:29 +0000</lastBuildDate>
	<language>en-US</language>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.9.14</generator>
	<item>
		<title>反应器模式</title>
		<link>https://blog.gmem.cc/reactor-pattern</link>
		<comments>https://blog.gmem.cc/reactor-pattern#comments</comments>
		<pubDate>Sat, 21 Mar 2015 08:48:28 +0000</pubDate>
		<dc:creator><![CDATA[Alex]]></dc:creator>
				<category><![CDATA[Architecture]]></category>
		<category><![CDATA[架构模式]]></category>
		<category><![CDATA[设计模式]]></category>

		<guid isPermaLink="false">https://blog.gmem.cc/?p=25349</guid>
		<description><![CDATA[<p>模式定义 反应器模式（Reactor pattern）是一种事件处理模式，用于处理从一个或多个输入并发的发送给单个服务处理器（service handler）的请求。服务处理器对请求进行多路分用（demultiplexes） —— 解析并分发（dispatcher）给请求处理器（service handler）。 反应器模式通常是单线程的，但是它可以在多线程系统中运行。 模式结构和说明 角色 资源 能够为系统提供输入，或者消费系统的输出。 同步事件多路分离器 即Synchronous Event Demultiplexer，负责运行事件循环， 在所有资源上阻塞。当可以在资源上执行异步操作时，此分离器将资源转给分发器（dispatcher）。 分发器 能够注册、解除注册请求处理器，分发来自Demultiplexer的资源给相应的请求处理器。 请求处理器 处理特定类型的资源。 应用举例 <a class="read-more" href="https://blog.gmem.cc/reactor-pattern">[...]</a></p>
<p>The post <a rel="nofollow" href="https://blog.gmem.cc/reactor-pattern">反应器模式</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></description>
				<content:encoded><![CDATA[<div class="wri_content_clear_both"><div class="blog_h1"><span class="graybg">模式定义</span></div>
<p>反应器模式（Reactor pattern）是一种<span style="background-color: #c0c0c0;">事件处理模式</span>，用于处理从一个或多个输入<span style="background-color: #c0c0c0;">并发的发送</span>给单个服务处理器（service handler）的请求。服务处理器对请求进行多路分用（demultiplexes） —— 解析并分发（dispatcher）给请求处理器（service handler）。</p>
<p>反应器模式通常是单线程的，但是它可以在多线程系统中运行。</p>
<div class="blog_h1"><span class="graybg">模式结构和说明</span></div>
<div class="blog_h2"><span class="graybg">角色</span></div>
<div class="blog_h3"><span class="graybg">资源</span></div>
<p>能够为系统提供输入，或者消费系统的输出。</p>
<div class="blog_h3"><span class="graybg">同步事件多路分离器</span></div>
<p>即Synchronous Event Demultiplexer，<span style="background-color: rgb(192, 192, 192);">负责运行事件循环</span>， 在所有资源上阻塞。当<span style="background-color: rgb(192, 192, 192);">可以在资源上执行异步操作时</span>，此分离器将资源转给分发器（dispatcher）。</p>
<div class="blog_h3"><span class="graybg">分发器</span></div>
<p>能够注册、解除注册请求处理器，分发来自Demultiplexer的资源给相应的请求处理器。</p>
<div class="blog_h3"><span class="graybg">请求处理器</span></div>
<p>处理特定类型的资源。</p>
<div class="blog_h1"><span class="graybg">应用举例</span></div>
<div class="blog_h2"><span class="graybg">libevent</span></div>
<p>libevent是一个事件驱动编程库，可以在文件描述符上发生特定事件、超时后，执行相应的回调函数。回调函数还可以由信号、定时器触发。</p>
</div><p>The post <a rel="nofollow" href="https://blog.gmem.cc/reactor-pattern">反应器模式</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.gmem.cc/reactor-pattern/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>服务定位器模式</title>
		<link>https://blog.gmem.cc/service-locator-pattern</link>
		<comments>https://blog.gmem.cc/service-locator-pattern#comments</comments>
		<pubDate>Wed, 25 May 2011 07:40:53 +0000</pubDate>
		<dc:creator><![CDATA[Alex]]></dc:creator>
				<category><![CDATA[Architecture]]></category>
		<category><![CDATA[设计模式]]></category>

		<guid isPermaLink="false">https://blog.gmem.cc/?p=16824</guid>
		<description><![CDATA[<p>模式定义 这种设计模式引入一个强大的抽象层，此层对获得服务的过程进行封装。该模式使用一个名为服务定位器的注册中心来进行服务的定位发现，对服务器进行请求，可以获得使用某种服务的所有必要信息。 模式结构和说明 此模式中包含三个角色：客户端、服务定位器、服务。 此模式的优势： 服务定位器可以作为简单的“运行时链接器”使用。组件可以在运行时加入到应用中，整个应用不需要重新编译，某些情况下甚至不需要重启应用 服务定位器可以在运行时动态添加、替换、移除服务，例如替换掉一个XML解析器的实现 应用和库可以完全隔离，它们仅仅通过服务定位器交互 此模式的缺点： 服务定位器对于系统的其它部分是黑盒。服务定位器发生错误将难以检测和恢复，从而导致整个系统的可靠性下降 服务定位器通常是单例的，这可能导致性能瓶颈 服务定位器可能是一个安全风险点，因为它允许外部代码的注入 服务定位器隐藏了它的类依赖，某些本应该在编译器暴露的错误以运行时错误的形式出现 增加了测试的难度，因为所有测试用例都需要和服务定位器交互，才能获得一个服务的Mock 应用举例 JNDI Java命名和目录服务可以认为是服务定位器模式的实现。此服务就相当于一个定位器，它提供了发布、获取服务的接口。 ZooKeeper ZooKeeper可以作为服务定位器使用，但这是一种变体： 定位器不是单例的，ZooKeeper通常是集群部属，避免了单点故障 ZooKeeper都用于分布式架构中，所谓服务也需要远程调用，而不是进程内基于内存地址的直接调用</p>
<p>The post <a rel="nofollow" href="https://blog.gmem.cc/service-locator-pattern">服务定位器模式</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></description>
				<content:encoded><![CDATA[<div class="wri_content_clear_both"><div class="blog_h2"><span class="graybg">模式定义</span></div>
<p>这种设计模式引入一个强大的抽象层，此层对获得服务的过程进行封装。该模式使用一个名为服务定位器的注册中心来进行服务的定位发现，对服务器进行请求，可以获得使用某种服务的所有必要信息。</p>
<div class="blog_h2"><span class="graybg">模式结构和说明</span></div>
<p>此模式中包含三个角色：客户端、服务定位器、服务。</p>
<p>此模式的优势：</p>
<ol>
<li>服务定位器可以作为简单的“运行时链接器”使用。组件可以在运行时加入到应用中，整个应用不需要重新编译，某些情况下甚至不需要重启应用</li>
<li>服务定位器可以在运行时动态添加、替换、移除服务，例如替换掉一个XML解析器的实现</li>
<li>应用和库可以完全隔离，它们仅仅通过服务定位器交互</li>
</ol>
<p>此模式的缺点：</p>
<ol>
<li>服务定位器对于系统的其它部分是黑盒。服务定位器发生错误将难以检测和恢复，从而导致整个系统的可靠性下降</li>
<li>服务定位器通常是单例的，这可能导致性能瓶颈</li>
<li>服务定位器可能是一个安全风险点，因为它允许外部代码的注入</li>
<li>服务定位器隐藏了它的类依赖，某些本应该在编译器暴露的错误以运行时错误的形式出现</li>
<li>增加了测试的难度，因为所有测试用例都需要和服务定位器交互，才能获得一个服务的Mock</li>
</ol>
<div class="blog_h2"><span class="graybg">应用举例</span></div>
<div class="blog_h3"><span class="graybg">JNDI</span></div>
<p>Java命名和目录服务可以认为是服务定位器模式的实现。此服务就相当于一个定位器，它提供了发布、获取服务的接口。</p>
<div class="blog_h3"><span class="graybg">ZooKeeper</span></div>
<p>ZooKeeper可以作为服务定位器使用，但这是一种变体：</p>
<ol>
<li>定位器不是单例的，ZooKeeper通常是集群部属，避免了单点故障</li>
<li>ZooKeeper都用于分布式架构中，所谓服务也需要远程调用，而不是进程内基于内存地址的直接调用</li>
</ol>
</div><p>The post <a rel="nofollow" href="https://blog.gmem.cc/service-locator-pattern">服务定位器模式</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.gmem.cc/service-locator-pattern/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>访问者模式</title>
		<link>https://blog.gmem.cc/visitor-pattern</link>
		<comments>https://blog.gmem.cc/visitor-pattern#comments</comments>
		<pubDate>Wed, 09 Feb 2011 08:09:45 +0000</pubDate>
		<dc:creator><![CDATA[Alex]]></dc:creator>
				<category><![CDATA[Architecture]]></category>
		<category><![CDATA[设计模式]]></category>

		<guid isPermaLink="false">http://blog.gmem.cc/?p=7841</guid>
		<description><![CDATA[<p>模式定义 访问者模式表示一个作用于某对象结构中各元素的操作。 它允许在不改变各元素的类的前提下定义作用于这些元素的新操作。在GOF95中，该模式被分类为行为型模式。 模式结构与说明 Visitor：访问者接口，为所有元素声明一个visit方法，用来代表为此元素添加的功能 ConcreteVisitor：具体访问者实现，实现一组完整的应用到对象结构的功能 Element：抽象元素，代表对象结构中具体的元素，定义接收访问的操作 ConcreteElement：具体元素，亦即被访问对象，通常会回调访问者的真实功能，同时开放自身的数据供访问者使用 ObjectStructure：对象结构，通常包含多个可被访问的元素，它可以遍历多个被访问的元素，也可以让访问者直接访问元素。该对象可以持有若干Element的集合，或者具有多个Element类型的字段 访问者模式的价值在于可以透明的为对象结构添加新功能，避免对这一系列的对象进行修改。 访问者模式的优点： 更好的可扩展性：允许通过创建新的访问者来增加行为，而无需改变对象结构本身 行为的代码被放在一起，统一位于访问者内，易于维护 分离无关行为：相互之间无关的行为被封装到不同的访问者中 访问者模式的缺点： 允许访问者对这些内部元素进行访问，对象结构的内部封装被打破 由于访问者对结构进行遍历，对象结构的修改变得困难，因为牵涉到所有访问者的修改 访问模式的适用场景： 如果需要对一个对象结构实施一些依赖于具体元素的操作 如果需要对一个对象结构中的各元素实施很多不同、不相关的操作 如果对象结构很少变动，但是经常需要对元素添加新功能 经典应用 <a class="read-more" href="https://blog.gmem.cc/visitor-pattern">[...]</a></p>
<p>The post <a rel="nofollow" href="https://blog.gmem.cc/visitor-pattern">访问者模式</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></description>
				<content:encoded><![CDATA[<div class="wri_content_clear_both"><div class="blog_h2"><span class="graybg">模式定义</span></div>
<p>访问者模式表示一个作用于某对象结构中<span style="background-color: #c0c0c0;">各元素</span>的操作。 它允许在<span style="background-color: #c0c0c0;">不改变各元素的类</span>的前提下定义作用于这些元素的<span style="background-color: #c0c0c0;">新操作</span>。在GOF95中，该模式被分类为行为型模式。</p>
<div class="blog_h2"><span class="graybg">模式结构与说明</span></div>
<p><img class="aligncenter size-full wp-image-7847" src="https://blog.gmem.cc/wp-content/uploads/2011/02/patterns_VisitorPattern.png" alt="patterns_VisitorPattern" width="516" height="427" /></p>
<ol>
<li>Visitor：访问者接口，为所有元素声明一个visit方法，用来代表为此元素添加的功能</li>
<li>ConcreteVisitor：具体访问者实现，实现一组完整的应用到对象结构的功能</li>
<li>Element：抽象元素，代表对象结构中具体的元素，定义接收访问的操作</li>
<li>ConcreteElement：具体元素，亦即被访问对象，通常会<span style="background-color: #c0c0c0;">回调访问者的真实功能</span>，同时开放自身的数据供访问者使用</li>
<li>ObjectStructure：对象结构，通常包含多个可被访问的元素，它可以遍历多个被访问的元素，也可以让访问者直接访问元素。该对象可以持有若干Element的集合，或者具有多个Element类型的字段</li>
</ol>
<p>访问者模式的价值在于可以透明的为对象结构添加新功能，避免对这一系列的对象进行修改。</p>
<p>访问者模式的优点：</p>
<ol>
<li>更好的可扩展性：允许通过创建新的访问者来增加行为，而无需改变对象结构本身</li>
<li>行为的代码被放在一起，统一位于访问者内，易于维护</li>
<li>分离无关行为：相互之间无关的行为被封装到不同的访问者中</li>
</ol>
<p>访问者模式的缺点：</p>
<ol>
<li>允许访问者对这些内部元素进行访问，对象结构的内部封装被打破</li>
<li>由于访问者对结构进行遍历，对象结构的修改变得困难，因为牵涉到所有访问者的修改</li>
</ol>
<p>访问模式的适用场景：</p>
<ol>
<li>如果需要对一个对象结构实施一些依赖于具体元素的操作</li>
<li>如果需要对一个对象结构中的各元素实施很多不同、不相关的操作</li>
<li>如果对象结构很少变动，但是经常需要对元素添加新功能</li>
</ol>
<div class="blog_h2"><span class="graybg">经典应用</span></div>
<div class="blog_h3"><span class="graybg">java.lang.model包中的访问者模式</span></div>
<p><img class="aligncenter size-full wp-image-7854" src="https://blog.gmem.cc/wp-content/uploads/2011/02/patterns_VisitorPattern_Java_Annotation.png" alt="patterns_VisitorPattern_Java_Annotation" width="672" height="626" /></p>
<div class="blog_h3"><span class="graybg">java.nio.file包中的访问者模式</span></div>
<p><img class="aligncenter size-full wp-image-7855" src="https://blog.gmem.cc/wp-content/uploads/2011/02/patterns_VisitorPattern_NIO.png" alt="patterns_VisitorPattern_NIO" width="608" height="268" /></p>
<p>这是一个变形的访问者模式，元素：文件、目录等没有统一的接口，也不能accept()访问者，实质上，对访问者的回调是由FileTreeWalker.walk()方法手动判断和完成的。FileTreeWalker相当于ObjectStructure角色。</p>
<div class="blog_h3"><span class="graybg">ASM框架</span></div>
<p>ASM是Java的一个字节码处理框架，它定义的访问者模式允许访问类中的各种元素：类本身、方法、字段、注解等：</p>
<p><img class="aligncenter size-full wp-image-7857" src="https://blog.gmem.cc/wp-content/uploads/2011/02/patterns_VisitorPattern_ASM.png" alt="patterns_VisitorPattern_ASM" width="95%" /></p>
<div class="blog_h3"><span class="graybg">Dom4j中的访问者模式</span></div>
<p>Dom4j是一个XML解析框架，它实现了非常典型的访问者模式：Visitor接口定义了访问各种XML节点的操作，包括文档、元素、属性、文本等。Node接口相当于Element角色，它为所有类型的XML节点定义了accept()方法。Document接口的实现AbstractDocument则充当了ObjectStructure角色：</p>
<pre class="crayon-plain-tag">public abstract class AbstractDocument extends AbstractBranch implements Document {
    public void accept(Visitor visitor) {
        visitor.visit(this); //先让访问者访问自己
        DocumentType docType = getDocType();
        if (docType != null) {
            visitor.visit(docType);  //访问文档类型
        }
        List content = content();
        //遍历，访问所有子节点
        if (content != null) {
            for (Iterator iter = content.iterator(); iter.hasNext();) {
                Object object = iter.next();
                if (object instanceof String) {
                    Text text = getDocumentFactory()
                            .createText((String) object);
                    visitor.visit(text);  //访问文本
                } else {
                    //如果是节点，那么调用相应的accept，递归的处理整个XML文档
                    Node node = (Node) object;
                    node.accept(visitor);
                }
            }
        }
    }
}</pre>
<p> Dom4j的访问者模式类图如下：</p>
<p><img class="aligncenter size-full wp-image-7859" src="https://blog.gmem.cc/wp-content/uploads/2011/02/patterns_VisitorPattern_Dom4j.png" alt="patterns_VisitorPattern_Dom4j" width="95%" /></p>
<div class="blog_h2"><span class="graybg">模式演变</span></div>
<ol>
<li>与缺省适配模式联用：如果元素种类特别多，则应当引入一个实现了缺省适配的访问者抽象类，提供缺省适配，这样可以减少具体访问者的代码量</li>
<li>与组合模式联用：将Component中定义的业务方法抽取出来，改用accept()方法，然后将那些业务方法的逻辑转移到访问者中</li>
<li>退化：取消Element接口。这种情况下，各元素没有一致的接口，不能accept()访问者。那么把哪些元素传递给访问者对应方法的职责就落到ObjectStructure头上了</li>
</ol>
</div><p>The post <a rel="nofollow" href="https://blog.gmem.cc/visitor-pattern">访问者模式</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.gmem.cc/visitor-pattern/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>不变模式</title>
		<link>https://blog.gmem.cc/immutable-pattern</link>
		<comments>https://blog.gmem.cc/immutable-pattern#comments</comments>
		<pubDate>Wed, 19 May 2010 07:39:05 +0000</pubDate>
		<dc:creator><![CDATA[Alex]]></dc:creator>
				<category><![CDATA[Architecture]]></category>
		<category><![CDATA[设计模式]]></category>

		<guid isPermaLink="false">http://blog.gmem.cc/?p=7834</guid>
		<description><![CDATA[<p>模式定义 对象创建后，即不允许改变其内部状态。  模式结构与说明 不变模式不需要类图来描述。 不变模式分为两者形式： 弱不变模式：当前类的实例的状态是不会变化的，但是其子类的实例状态则可能变化 强不变模式：不但当前类的实例状态不会变化，子类实例的状态也不会变化 不变模式的优点： 允许多个Client共享一个对象 允许安全性浅拷贝 允许避免并发访问时的同步开销  经典应用 Java基础类型中的不变模式 Java的字符串和所有数字类型，都实现了不变模式，不提供任何操作来修改对象的内部状态： 一些“貌似”修改对状态的方法，实质上是返回了一个新的对象，例如String.trim()、BigDecimal.add()： [crayon-69e5926aac085179975688/] C++语言级别的不变模式支持 在C++中，可以使用const关键字强制施加不变性约束： [crayon-69e5926aac089310277597/] 模式演变 与享元模式联用：由于不变模式的对象的内部状态不会改变，因而它可以被安全的享元</p>
<p>The post <a rel="nofollow" href="https://blog.gmem.cc/immutable-pattern">不变模式</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></description>
				<content:encoded><![CDATA[<div class="wri_content_clear_both"><div class="blog_h2"><span class="graybg">模式定义</span></div>
<p>对象创建后，即不允许改变其内部状态。 </p>
<div class="blog_h2"><span class="graybg">模式结构与说明</span></div>
<p>不变模式不需要类图来描述。</p>
<p>不变模式分为两者形式：</p>
<ol>
<li>弱不变模式：当前类的实例的状态是不会变化的，但是其子类的实例状态则可能变化</li>
<li>强不变模式：不但当前类的实例状态不会变化，子类实例的状态也不会变化</li>
</ol>
<p>不变模式的优点：</p>
<ol>
<li>允许多个Client共享一个对象</li>
<li>允许安全性浅拷贝</li>
<li>允许避免并发访问时的同步开销 </li>
</ol>
<div class="blog_h2"><span class="graybg">经典应用</span></div>
<div class="blog_h3"><span class="graybg">Java基础类型中的不变模式</span></div>
<p>Java的字符串和所有数字类型，都实现了不变模式，不提供任何操作来修改对象的内部状态：</p>
<p><img class="aligncenter size-full wp-image-7838" src="https://blog.gmem.cc/wp-content/uploads/2010/05/patterns_ImmutablePattern_Java_1.png" alt="patterns_ImmutablePattern_Java_1" width="614" height="420" /></p>
<p>一些“貌似”修改对状态的方法，实质上是返回了一个新的对象，例如String.trim()、BigDecimal.add()：</p>
<pre class="crayon-plain-tag">public BigDecimal add(BigDecimal augend) {
    ...
    return (fst.signum == snd.signum) ? 
        new BigDecimal(sum, INFLATED, rscale, 0) :
        new BigDecimal(sum, compactValFor(sum), rscale, 0);
}</pre>
<div class="blog_h3"><span class="graybg">C++语言级别的不变模式支持</span></div>
<p>在C++中，可以使用const关键字强制施加不变性约束：</p>
<pre class="crayon-plain-tag">class Immutable
{
    private:
        int field;

    public:
        Immutable( int f ) :
                field( f )
        {
        }
        void op()
        {
        }
        //声明为常方法，即不会改变对象的任何内部状态
        void constOp() const
        {
            op(); //无法通过编译，常方法不得调用非常方法
            this-&gt;field = 1; //无法通过编译，常方法不得对字段进行写操作
            Immutable::staticField = 1; //静态字段的修改则不在限制
        }
};</pre>
<div class="blog_h2"><span class="graybg">模式演变</span></div>
<ol>
<li>与享元模式联用：由于不变模式的对象的内部状态不会改变，因而它可以被安全的享元</li>
</ol>
</div><p>The post <a rel="nofollow" href="https://blog.gmem.cc/immutable-pattern">不变模式</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.gmem.cc/immutable-pattern/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>原型模式</title>
		<link>https://blog.gmem.cc/prototype-pattern</link>
		<comments>https://blog.gmem.cc/prototype-pattern#comments</comments>
		<pubDate>Tue, 12 Jan 2010 06:40:03 +0000</pubDate>
		<dc:creator><![CDATA[Alex]]></dc:creator>
				<category><![CDATA[Architecture]]></category>
		<category><![CDATA[设计模式]]></category>

		<guid isPermaLink="false">http://blog.gmem.cc/?p=7825</guid>
		<description><![CDATA[<p>模式定义 使用原型实例指定创建对象的种类，并通过拷贝这些原型对象创建新的对象。 在GOF95中原型模式被分类为创建型模式。 模式结构与说明 Prototype：声明一个克隆自身的接口，用来约束想要克隆自己的类，让它们都要实现这里定义的克隆方法 ConcretePrototype：实现Prototype接口的类，真正实现了对自身的克隆功能 Client：客户端，首先获取原型实例对象，然后通过克隆原型来创建新的实例 原型模式优点： 向客户隐藏创建新实例的复杂性 在某些情况下，复制对象比创建新对象更有效  经典应用 Java的克隆机制 在Java语言中，所有类型的根类java.lang.Object支持克隆方法： [crayon-69e5926aac293987997394/] 需要支持克隆功能的类型必须首先标记接口： [crayon-69e5926aac297674596302/] 可以看到，在JDK中已经内置的原型模式的支持。  JavaScript的原型机制 JavaScript从语言层次上支持了原型模式，实际上，JavaScript的继承机制就是基于原型的继承，而不是基于类的继承。 设置一个构造函数的prototype属性为一个对象，这个对象就成为此构造函数创建出的所有对象的原型： [crayon-69e5926aac29a916399083/] 模式演变 <a class="read-more" href="https://blog.gmem.cc/prototype-pattern">[...]</a></p>
<p>The post <a rel="nofollow" href="https://blog.gmem.cc/prototype-pattern">原型模式</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></description>
				<content:encoded><![CDATA[<div class="wri_content_clear_both"><div class="blog_h2"><span class="graybg">模式定义</span></div>
<p>使用原型实例指定创建对象的种类，并通过拷贝这些原型对象创建新的对象。 在GOF95中原型模式被分类为创建型模式。</p>
<div class="blog_h2"><span class="graybg">模式结构与说明</span></div>
<p><img class="aligncenter size-full wp-image-7828" src="https://blog.gmem.cc/wp-content/uploads/2010/01/patterns_PrototypePattern.png" alt="patterns_PrototypePattern" width="499" height="288" /></p>
<ol>
<li>Prototype：声明一个克隆自身的接口，用来约束想要克隆自己的类，让它们都要实现这里定义的克隆方法</li>
<li>ConcretePrototype：实现Prototype接口的类，真正实现了对自身的克隆功能</li>
<li>Client：客户端，首先获取原型实例对象，然后通过克隆原型来创建新的实例</li>
</ol>
<p>原型模式优点：</p>
<ol>
<li>向客户隐藏创建新实例的复杂性</li>
<li>在某些情况下，复制对象比创建新对象更有效 </li>
</ol>
<div class="blog_h2"><span class="graybg">经典应用</span></div>
<div class="blog_h3"><span class="graybg">Java的克隆机制</span></div>
<p>在Java语言中，所有类型的根类java.lang.Object支持克隆方法：</p>
<pre class="crayon-plain-tag">public class Object
{
    /**
     * 返回当前对象的一个拷贝，拷贝的含义依赖于对象的真实类型，但是一般意味着：
     * x.clone() != x &amp;&amp; x.clone().getClass() == x.getClass() &amp;&amp; x.equals(x.clone())
     * 
     * 根据惯例，该方法返回值应当和当前对象是相互独立的，这意味着可能需要将super.clone()
     * 返回的对象进行一定的处理，这个修改往往意味着对可变（mutable）对象进行深拷贝，如果当前
     * 类型只包含基本类型，则不需要这一处理过程
     * 
     * 当前实现实际上实现了浅拷贝行为：
     * 1、如果当前对象没有实现Cloneable，抛出CloneNotSupportedException
     *    注意所有数组类型被认为实现了Cloneable
     * 2、否则，创建一个新实例，并把当前对象的所有字段赋值给这个实例
     *    注意任何字段本身都不会被clone()
     */
    protected native Object clone() throws CloneNotSupportedException;
}</pre>
<p>需要支持克隆功能的类型必须首先标记接口：</p>
<pre class="crayon-plain-tag">public interface Cloneable { }</pre>
<p>可以看到，在JDK中已经内置的原型模式的支持。 </p>
<div class="blog_h3"><span class="graybg">JavaScript的原型机制</span></div>
<p>JavaScript从语言层次上支持了原型模式，实际上，JavaScript的继承机制就是基于原型的继承，而不是基于类的继承。</p>
<p>设置一个构造函数的prototype属性为一个对象，这个对象就成为此构造函数创建出的所有对象的原型：</p>
<pre class="crayon-plain-tag">var protoObject = { x: 1, y: 2, z: [1, 2] };
var ConstructFunc = function(){};

var obj1 = new ConstructFunc();
console.log(obj1.z); // 从原型继承得到的属性 [1, 2]
obj1.z.push(3);

var obj2 = new ConstructFunc();
console.log(obj2.z); //[1, 2, 3] 原型对象的属性是浅复制的</pre>
<div class="blog_h2"><span class="graybg">模式演变</span></div>
<ol>
<li>深克隆与浅克隆：在使用原型模式的时候，涉及到两种克隆方式。深克隆会递归的克隆所有字段；浅克隆只是拷贝复杂类型的引用地址</li>
<li>与工厂模式的比较：两者都用来创建对象实例，但是原型模式强调使用克隆的方式来创建</li>
<li>与生成器模式联用：原型模式可以用来创建某些部件</li>
</ol>
</div><p>The post <a rel="nofollow" href="https://blog.gmem.cc/prototype-pattern">原型模式</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.gmem.cc/prototype-pattern/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>备忘录模式</title>
		<link>https://blog.gmem.cc/memonto-pattern</link>
		<comments>https://blog.gmem.cc/memonto-pattern#comments</comments>
		<pubDate>Fri, 19 Jun 2009 01:29:44 +0000</pubDate>
		<dc:creator><![CDATA[Alex]]></dc:creator>
				<category><![CDATA[Architecture]]></category>
		<category><![CDATA[设计模式]]></category>

		<guid isPermaLink="false">http://blog.gmem.cc/?p=7811</guid>
		<description><![CDATA[<p>模式定义 在不破坏封装性的前提下，捕获一个对象的内部状态，并在该对象之外保存这一状态。以后可以将对象恢复到原先保存的状态。备忘录模式在GOF95中被分类为行为型模式。  模式结构与说明 Memento：备忘录接口，用来存放原发器对象某个瞬间的内部状态，该接口通常是空接口或者窄接口，不会声明任何获取原发器内部状态的方法 MementoImpl：备忘录实现类，为了不破坏封装性，备忘录的实现类往往作为原发器私有内部类实现，备忘录的实例应该由原发器自己生成 Originator：原发器，其状态需要保存和恢复，提供这两个接口 CareTaker：备忘录管理者，负责保存和取回备忘录 备忘录模式的优点： 将被存储的状态放在外面，不和关键对象混在一起，提高了后者内聚性 保持了关键对象的数据封装 提供了容易实现的恢复能力 备忘录的缺点： 存储和恢复状态的过程可能比较耗时 如果存储的备忘录数量很大，可能导致不必要的开销 备忘录的适用场景： 需要持久化对象状态，以便未来恢复时 经典应用 Java的串行化机制 串行化机制可以认为是一种备忘录模式，任何实现java.io.Serializable接口的类都相当于Originator角色： [crayon-69e5926aac4b3461408034/] 而Memento角色则由使用二进制数据流承担，没有对应的Java对象。CareTaker角色由java.io包中一些类承担： [crayon-69e5926aac4b7409353003/] <a class="read-more" href="https://blog.gmem.cc/memonto-pattern">[...]</a></p>
<p>The post <a rel="nofollow" href="https://blog.gmem.cc/memonto-pattern">备忘录模式</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></description>
				<content:encoded><![CDATA[<div class="wri_content_clear_both"><div class="blog_h2"><span class="graybg">模式定义</span></div>
<p>在不破坏封装性的前提下，捕获一个对象的内部状态，并在该对象之外保存这一状态。以后可以将对象恢复到原先保存的状态。备忘录模式在GOF95中被分类为行为型模式。 </p>
<div class="blog_h2"><span class="graybg">模式结构与说明</span></div>
<p><img class="aligncenter size-full wp-image-7819" src="https://blog.gmem.cc/wp-content/uploads/2009/06/patterns_MementoPattern.png" alt="patterns_MementoPattern" width="564" height="330" /></p>
<ol>
<li>Memento：备忘录接口，用来存放原发器对象某个瞬间的内部状态，该接口通常是空接口或者窄接口，不会声明任何获取原发器内部状态的方法</li>
<li>MementoImpl：备忘录实现类，为了不破坏封装性，备忘录的实现类往往作为原发器私有内部类实现，备忘录的实例应该由原发器自己生成</li>
<li>Originator：原发器，其状态需要保存和恢复，提供这两个接口</li>
<li>CareTaker：备忘录管理者，负责保存和取回备忘录</li>
</ol>
<p>备忘录模式的优点：</p>
<ol>
<li>将被存储的状态放在外面，不和关键对象混在一起，提高了后者内聚性</li>
<li>保持了关键对象的数据封装</li>
<li>提供了容易实现的恢复能力</li>
</ol>
<p>备忘录的缺点：</p>
<ol>
<li>存储和恢复状态的过程可能比较耗时</li>
<li>如果存储的备忘录数量很大，可能导致不必要的开销</li>
</ol>
<p>备忘录的适用场景：</p>
<ol>
<li>需要持久化对象状态，以便未来恢复时</li>
</ol>
<div class="blog_h2"><span class="graybg">经典应用</span></div>
<div class="blog_h3"><span class="graybg">Java的串行化机制</span></div>
<p>串行化机制可以认为是一种备忘录模式，任何实现java.io.Serializable接口的类都相当于Originator角色：</p>
<pre class="crayon-plain-tag">import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class SerializableObject implements java.io.Serializable
{
    private static final long serialVersionUID = 1L;
    private int               field0;
    private String            field1;
    //transient关键字用来声明不需要保存的状态
    private transient int     field2;
    //特殊签名的方法用来定制备忘录生成的具体规则，承担了createMemento()、restoreMemento()的职责
    private void writeObject( ObjectOutputStream out ) throws IOException {
        out.writeInt( field0 );
        out.writeUTF( field1 );
    }
    private void readObject( ObjectInputStream in ) throws IOException {
        field0 = in.readInt();
        field1 = in.readUTF();
    }
}</pre>
<p>而Memento角色则由使用二进制数据流承担，没有对应的Java对象。CareTaker角色由java.io包中一些类承担：</p>
<pre class="crayon-plain-tag">SerializableObject so = new SerializableObject();
ObjectOutputStream output = new ObjectOutputStream( new FileOutputStream( "SerializableObject" ) );
output.writeObject( so );
output.close();

ObjectInputStream input = new ObjectInputStream( new FileInputStream( "SerializableObject" ) );
so = (SerializableObject) input.readObject();
input.close();</pre>
<div class="blog_h2"><span class="graybg">模式演变</span></div>
<ol>
<li>增量备忘录：如果原发器的状态很多，而修改的状态很少，可以使用增量方式存储备忘录</li>
<li>与原型模式联用：如果原发器的大部分字段都是需要保存的，那么原发器可以实现原型模式，保存备忘录的时候直接保存原发器的克隆</li>
<li>离线存储：标准的备忘录模式没有考虑到离线存储的问题，实际上CareTaker可以将备忘录保存为XML、JSON、Java串行化格式，甚至保存到数据库</li>
<li>与命令模式联用：命令模式支持撤销，这就涉及到对象状态恢复的问题。可以在执行命令时，将出现变化的状态原值保存到备忘录中，在撤销时，取回备忘录恢复</li>
</ol>
</div><p>The post <a rel="nofollow" href="https://blog.gmem.cc/memonto-pattern">备忘录模式</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.gmem.cc/memonto-pattern/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>中介者模式</title>
		<link>https://blog.gmem.cc/mediator-pattern</link>
		<comments>https://blog.gmem.cc/mediator-pattern#comments</comments>
		<pubDate>Tue, 05 Aug 2008 03:52:31 +0000</pubDate>
		<dc:creator><![CDATA[Alex]]></dc:creator>
				<category><![CDATA[Architecture]]></category>
		<category><![CDATA[设计模式]]></category>

		<guid isPermaLink="false">http://blog.gmem.cc/?p=7799</guid>
		<description><![CDATA[<p>模式定义 也译为“调停者模式”，该模式用一个中介对象来封装一系列的对象交互。中介者使得各对象不需要显式的相互引用，从而使其松散耦合，而且可以独立的改变它们之间的交互。在GOF95中中介者模式被分类为行为型模式。  模式结构与说明 Mediator：中介者接口，在里面定义各同事之间交互需要的方法。可以是公共的通信方法，比如changed，大家都用；也可以是小范围的交互方法 ConcreteMediator：具体的中介者实现，持有所有同事的引用，并协调各同事之间的交互关系 Colleague：同事抽象类，主要负责约束同事的类型，不实现同事的公共功能，比如每个同事都应该知道中介者对象（getMediator） ConcreteColleague：具体同事类，实现自己的业务，它们直接绝不存在任何依赖关系。它们如果需要与其它同事通信，就持有中介者的引用 中介者模式很好的体现了迪米特法则，它可以彻底解耦网状的对象关系。 中介者模式的优点： 通过将对象彼此解耦，可以增加对象的复用度 通过将控制逻辑集中，可以简化系统维护 可以让对象之间传递的消息变得简单而且大幅减少 将多对多关系改变为一对多 中介者模式的缺点： 如果设计不当，中介者对象本身会变得过于复杂 中介者模式的适用场景： 如果一组对象之间的通信方式过于复杂，导致结构混乱的互相依赖 用来协调相关的GUI组件 经典应用 Java反射中的中介者模式 [crayon-69e5926aac697232817259/] Method类实现了一种变形的中介者模式，Method相当于Mediator角色，调用invoke()的Client代码相当于Colleague角色，invoke()的obj参数也相当于Colleague角色。虽然Client持有了需要交互的Colleague的引用，但是它不需要知道Colleague类型的任何信息。 <a class="read-more" href="https://blog.gmem.cc/mediator-pattern">[...]</a></p>
<p>The post <a rel="nofollow" href="https://blog.gmem.cc/mediator-pattern">中介者模式</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></description>
				<content:encoded><![CDATA[<div class="wri_content_clear_both"><div class="blog_h2"><span class="graybg">模式定义</span></div>
<p>也译为“调停者模式”，该模式用一个中介对象来封装一系列的对象交互。中介者使得各对象不需要显式的相互引用，从而使其松散耦合，而且可以独立的改变它们之间的交互。在GOF95中中介者模式被分类为行为型模式。 </p>
<div class="blog_h2"><span class="graybg">模式结构与说明</span></div>
<p><img class="aligncenter size-full wp-image-7804" src="https://blog.gmem.cc/wp-content/uploads/2008/08/patterns_MediatorPattern.png" alt="patterns_MediatorPattern" width="551" height="445" /></p>
<ol>
<li>Mediator：中介者接口，在里面定义<span style="background-color: #c0c0c0;">各同事之间交互需要的方法</span>。可以是公共的通信方法，比如changed，大家都用；也可以是小范围的交互方法</li>
<li>ConcreteMediator：具体的中介者实现，持有所有同事的引用，并协调各同事之间的交互关系</li>
<li>Colleague：同事抽象类，主要负责约束同事的类型，不实现同事的公共功能，比如每个同事都应该知道中介者对象（getMediator）</li>
<li>ConcreteColleague：具体同事类，实现自己的业务，它们直接绝不存在任何依赖关系。它们如果需要与其它同事通信，就持有中介者的引用</li>
</ol>
<p>中介者模式很好的体现了迪米特法则，它可以彻底解耦网状的对象关系。</p>
<p>中介者模式的优点：</p>
<ol>
<li>通过将对象彼此解耦，可以增加对象的复用度</li>
<li>通过将控制逻辑集中，可以简化系统维护</li>
<li>可以让对象之间传递的消息变得简单而且大幅减少</li>
<li>将多对多关系改变为一对多</li>
</ol>
<p>中介者模式的缺点：</p>
<ol>
<li>如果设计不当，中介者对象本身会变得过于复杂</li>
</ol>
<p>中介者模式的适用场景：</p>
<ol>
<li>如果一组对象之间的通信方式过于复杂，导致结构混乱的互相依赖</li>
<li>用来协调相关的GUI组件</li>
</ol>
<div class="blog_h2"><span class="graybg">经典应用</span></div>
<div class="blog_h3"><span class="graybg">Java反射中的中介者模式</span></div>
<pre class="crayon-plain-tag">public class Method{
    public Object invoke(Object obj, Object... args)
	throws IllegalAccessException, IllegalArgumentException;
}</pre>
<p>Method类实现了一种变形的中介者模式，Method相当于Mediator角色，调用invoke()的Client代码相当于Colleague角色，invoke()的obj参数也相当于Colleague角色。虽然Client持有了需要交互的Colleague的引用，但是它不需要知道Colleague类型的任何信息。</p>
<div class="blog_h2"><span class="graybg">模式演变</span></div>
<ol>
<li>退化：取消同事的公共抽象类。如果不同的同事类没有什么公共功能，仅仅一个getMediator()往往不值得让它们拥有公共父类</li>
<li>退化：具体中介者作为单例。如果只需要一个中介者对，那么可以用单例实现</li>
<li>退化：取消中介者接口。如果不可能有切换中介者的需求，可以取消此接口 </li>
<li>与外观模式的比较：两者都很好的提现迪米特法则。但是外观模式对系统内部进行封装，对Client提供单向的接口；而中介者模式则来协调系统内部的多个组件，并且通信是双向的</li>
</ol>
</div><p>The post <a rel="nofollow" href="https://blog.gmem.cc/mediator-pattern">中介者模式</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.gmem.cc/mediator-pattern/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>解释器模式</title>
		<link>https://blog.gmem.cc/interpreter-pattern</link>
		<comments>https://blog.gmem.cc/interpreter-pattern#comments</comments>
		<pubDate>Sat, 08 Mar 2008 01:33:05 +0000</pubDate>
		<dc:creator><![CDATA[Alex]]></dc:creator>
				<category><![CDATA[Architecture]]></category>
		<category><![CDATA[设计模式]]></category>

		<guid isPermaLink="false">http://blog.gmem.cc/?p=7782</guid>
		<description><![CDATA[<p>模式定义 给定一个语言，定义它的文法（语法规则）的一种表示，并定义一个解释器，这个解释器使用该表示来解释语言中的句子。在GOF95中解释器模式被分类为行为型模式。  模式结构与说明 AbstractionExpression：定义了解释器的接口，约定解释器的解释操作 TerminalExpression：端点解释器，用来实现语法规则中和端点相关的操作，不包含其它解释器。如果使用组合模式构建语法树的话，就相当于Leaf角色 NonterminalExpression：非端点解释器，用来实现语法规则中非端点相关的操作，通常对应了一个语法规则，可以包含其它解释器。如果使用组合模式构建语法树的话，就相当于Composite角色。非端点解释器可以有多种 Context：上下文，包含各种解释器需要的数据或者公共功能。上下文在解释器模式中非常重要，它会被传递到所有解释器中，解释器可以在上下文中存取数据 Client：解释器的客户端 理解解释器模式的思路，需要首先了解两个重要的概念： 解析器（Parser）：能够把Client的表达式请求解析为抽象语法树形式 解释器（Interpreter）：能够解释抽象语法树，并执行每个节点对应的功能  那么，解析器这个角色谁来承担呢？亦即，谁来构建语法树？如果语法很简单，可以让客户端来手工构建，否则，应当开发单独的解析器，能够把字符串形式的表达式转换为语法树。 解释器模式的优点： 将每个语法规则表示成一个类，方便实现语言 由于语法由许多类表示，因此可以轻易的改变或者扩展此语言 通过在类结构中加入新的方法，可以在解释的同时添加新的行为，例如验证、打印格式美化 解释器模式的缺点： 当语法规则很复杂时，该模式会变得异常复杂 解释器模式的适用场景： 当需要实现一个简单的语言时 有一个简单的语法，并且简单比效率更加重要 应用举例 <a class="read-more" href="https://blog.gmem.cc/interpreter-pattern">[...]</a></p>
<p>The post <a rel="nofollow" href="https://blog.gmem.cc/interpreter-pattern">解释器模式</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></description>
				<content:encoded><![CDATA[<div class="wri_content_clear_both"><div class="blog_h2"><span class="graybg">模式定义</span></div>
<p>给定一个语言，定义它的文法（语法规则）的一种表示，并定义一个解释器，这个解释器使用该表示来解释语言中的句子。在GOF95中解释器模式被分类为行为型模式。 </p>
<div class="blog_h2"><span class="graybg">模式结构与说明</span></div>
<p><img class="aligncenter size-full wp-image-7788" src="https://blog.gmem.cc/wp-content/uploads/2008/03/patterns_InterpreterPattern.png" alt="patterns_InterpreterPattern" width="611" height="270" /></p>
<ol>
<li>AbstractionExpression：定义了解释器的接口，约定解释器的解释操作</li>
<li>TerminalExpression：端点解释器，用来实现语法规则中和端点相关的操作，不包含其它解释器。如果使用组合模式构建语法树的话，就相当于Leaf角色</li>
<li>NonterminalExpression：非端点解释器，用来实现语法规则中非端点相关的操作，通常对应了一个语法规则，可以包含其它解释器。如果使用组合模式构建语法树的话，就相当于Composite角色。非端点解释器可以有多种</li>
<li>Context：上下文，包含各种解释器需要的<span style="background-color: #c0c0c0;">数据</span>或者<span style="background-color: #c0c0c0;">公共功能</span>。上下文在解释器模式中非常重要，它会被传递到所有解释器中，解释器可以在上下文中存取数据</li>
<li>Client：解释器的客户端</li>
</ol>
<p>理解解释器模式的思路，需要首先了解两个重要的概念：</p>
<ol>
<li>解析器（Parser）：能够把Client的表达式请求解析为抽象语法树形式</li>
<li>解释器（Interpreter）：能够解释抽象语法树，并执行每个节点对应的功能 </li>
</ol>
<p>那么，解析器这个角色谁来承担呢？亦即，谁来构建语法树？如果语法很简单，可以让客户端来手工构建，否则，应当开发单独的解析器，能够把字符串形式的表达式转换为语法树。</p>
<p>解释器模式的优点：</p>
<ol>
<li>将每个语法规则表示成一个类，方便实现语言</li>
<li>由于语法由许多类表示，因此可以轻易的改变或者扩展此语言</li>
<li>通过在类结构中加入新的方法，可以在解释的同时添加新的行为，例如验证、打印格式美化</li>
</ol>
<p>解释器模式的缺点：</p>
<ol>
<li>当语法规则很复杂时，该模式会变得异常复杂</li>
</ol>
<p>解释器模式的适用场景：</p>
<ol>
<li>当需要实现一个简单的语言时</li>
<li>有一个简单的语法，并且简单比效率更加重要</li>
</ol>
<div class="blog_h2"><span class="graybg">应用举例</span></div>
<p>考虑一个简单的XPath解释器，支持以下形式的XPath语法：</p>
<pre class="crayon-plain-tag">#选择元素根元素msg下body子元素的el子元素
/msg/body/el
#选择元素根元素msg下body子元素的el子元素的name属性
/msg/body/el/@name</pre>
<p>分析一下可以看到，XML元素可以作为非端点元素或者端点元素，XML属性则只能支持端点元素，因此，我们设计如下的解释器类层次：</p>
<p><img class="aligncenter size-full wp-image-7793" src="https://blog.gmem.cc/wp-content/uploads/2008/03/patterns_InterpreterPattern_XPath.png" alt="patterns_InterpreterPattern_XPath" width="608" height="416" />XPath解释器的实现思路是：</p>
<ol>
<li>通过Dom4j等工具执行XML解析</li>
<li>从根XPathExpression开始解析</li>
<li>如果当前是非端点表达式，那么在Context中设置parent为当前元素</li>
<li>如果当前是端点表达式，那么：
<ol>
<li>对于元素端点，依据parent获取当前元素，进而获取元素的文本</li>
<li>对于属性端点，依据parent获取对应属性，进而获取元素的属性</li>
</ol>
</li>
</ol>
<p>主要代码如下：</p>
<pre class="crayon-plain-tag">/**
 * XPath解析上下文
 * 
 */
public class XPathContext
{

    /* 上下文中包含公共的数据 */

    private Document doc;   //XML文档

    private Element  parent; //当前父元素

    /* 上下文中可以包含公共的功能 */

    public XPathContext( InputStream xml ) throws DocumentException
    {
        this.doc = new SAXReader().read( xml );
    }

    public void setParent( String name )
    {
        if ( parent == null ) parent = doc.getRootElement();
        else parent = parent.element( name );
    }

    public String getElementText( String name )
    {
        return parent.elementText( name );
    }

    public String getAttribute( String name )
    {
        return parent.attributeValue( name );
    }
}

/**
 * XPath表达式超类
 * 
 */
public abstract class XPathExpression
{

    private String name;

    public XPathExpression( String name )
    {
        this.name = name;
    }

    public abstract String interpret( XPathContext ctx );

    public String getName()
    {
        return name;
    }
}

/**
 * XPath元素表达式，非端点
 * 
 */
public class XPathElementExpression extends XPathExpression
{

    public XPathElementExpression( String name )
    {
        super( name );
    }

    private List&lt;XPathExpression&gt; subExprs = new ArrayList&lt;XPathExpression&gt;();

    @Override
    public String interpret( XPathContext ctx )
    {
        ctx.setParent( getName() ); //只是在上下文中设置父元素
        return subExprs.get( 0 ).interpret( ctx ); //然后就遍历子节点
    }

    public void addSubExpression( XPathExpression expr )
    {
        subExprs.add( expr );
    }
}

/**
 * 元素端点
 */
public class XPathTerminalElementExpression extends XPathExpression
{

    public XPathTerminalElementExpression( String name )
    {
        super( name );
    }

    @Override
    public String interpret( XPathContext ctx )
    {
        return ctx.getElementText( getName() );
    }

}

/**
 * 属性端点
 */
public class XPathTerminalAttributeExpression extends XPathExpression
{

    public XPathTerminalAttributeExpression( String name )
    {
        super( name );
    }

    @Override
    public String interpret( XPathContext ctx )
    {
        return ctx.getAttribute( getName() );
    }

}

public class Client
{

    public static void main( String[] args ) throws Throwable
    {
        InputStream is = new FileInputStream( "~/example.xml" );
        XPathContext ctx = new XPathContext( is );
        //调用解析器将文本解析为语法树
        XPathExpression expr = parse( "/msg/body/el/@name" );
        System.out.println( expr.interpret( ctx ) ); //打印属性值
        is.close();
    }

    private static XPathExpression parse( String string )
    {
        return null;
    }
}</pre>
<div class="blog_h2"><span class="graybg">经典应用</span></div>
<div class="blog_h3"><span class="graybg">Java中的正则式</span></div>
<p>java.util.regex.Pattern包含了大量的内部类，组成了一个解释器层次：</p>
<p><img class="aligncenter size-full wp-image-7795" src="https://blog.gmem.cc/wp-content/uploads/2008/03/patterns_InterpreterPattern_Java_Pattern.png" alt="patterns_InterpreterPattern_Java_Pattern" width="95%" /></p>
<p>Node就相当于解释器模式中的Interpreter角色，在执行正则式匹配的时候，Pattern会调用Node的match方法，该方法相当于解释器模式中的interpret()，会调用下一个节点的match()，与标准的解释器模式类似：</p>
<pre class="crayon-plain-tag">static class Start extends Node {
    int minLength;
    Start(Node node) {
        this.next = node;   //下一个节点
        minLength = info.minLength;
    }
    boolean match(Matcher matcher, int i, CharSequence seq) {
        ...
        boolean ret = false;
        int guard = matcher.to - minLength;
        for (; i &lt;= guard; i++) {
            if (ret = next.match(matcher, i, seq)) //调用下一个节点的match()
                break;
            if (i == guard)
                matcher.hitEnd = true;
        }
        ...
    }
}</pre>
<div class="blog_h2"><span class="graybg">模式演变</span></div>
<ol>
<li>与组合模式联用：由于涉及到语法树，解释器模式通常都要和组合模式一起使用，TerminalExpression充当Leaf，NonTerminalExpression充当Composite</li>
<li>与迭代器模式联用：遍历语法树时，可以使用迭代器</li>
</ol>
</div><p>The post <a rel="nofollow" href="https://blog.gmem.cc/interpreter-pattern">解释器模式</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.gmem.cc/interpreter-pattern/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>享元模式</title>
		<link>https://blog.gmem.cc/flyweight-pattern</link>
		<comments>https://blog.gmem.cc/flyweight-pattern#comments</comments>
		<pubDate>Sat, 19 Jan 2008 08:08:20 +0000</pubDate>
		<dc:creator><![CDATA[Alex]]></dc:creator>
				<category><![CDATA[Architecture]]></category>
		<category><![CDATA[设计模式]]></category>

		<guid isPermaLink="false">http://blog.gmem.cc/?p=7757</guid>
		<description><![CDATA[<p>模式定义 享元模式，直译“轻量模式”，它允许通过共享技术有效的支持大量细粒度的对象。在GOF95中轻量模式被分类为结构型模式。  模式结构与说明 Flyweight：抽象享元角色，那些需要外部状态的操作通过方法参数传入，它们改变方法的行为，但是绝不影响享元的内部状态 ConcreteFlyweight：具体享元角色，如果有内部状态，必须负责为内部状态提供存储空间，享元对象的内部状态必须和对象所处的环境无关，从而使得享元对象可以被共享 UnsharedConcreteFlyweight：非共享的具体享元角色。Flyweight接口让共享成为可能而不是必须。UnsharedConcreteFlyweight往往是包含了多个ConcreteFlyweight的组合，由于后者已经是享元了，作为组合的前者就没必要也被共享 FlyweightFactory：享元工厂，负责创建和管理享元角色，必须保证享元对象可以被适当的共享，当客户端调用一个需要一个享元对象时，享元工厂负责检查是否已经存在符合要求的对象，如果没有则创建之 Client：客户端，负责维护对享元对象的引用，并自行存储所有享元的外部状态 通过将那些重复、不变的数据缓存起来并加以复用，轻量模式可以解决大量包含重复数据的细粒度对象占用内存的问题。 享元模式的关键是合理的区分内部、外部状态，并将这两类状态隔离，内部状态与外部环境无关，因而可以保留在享元的内部，减少内存占用。外部状态则通过方法参数传入，由客户端决定。 享元模式的优点： 减少对象数量、节约内存空间 享元模式的缺点： 维护共享对象需要额外开销 经典应用 Ext.dom.AbstractElement.Fly ExtJS中对DOM元素的处理，使用了享元模式。Fly类相当于Flyweight角色，Ext.dom.AbstractElement.fly()则充当了FlyweightFactory角色： Ext.dom.AbstractElement.Fly与Ext.dom.Element具有一样的结构，它允许在不创建Element的前提下来使用Element的接口来操控HTMLElement。Fly类的内部状态并不是固定的，相反，每次调用Ext.fly都会导致Fly.dom属性被设置为最新的HTMLElement，这种设计是为了透明性的考虑——与Element的接口一致。 享元角色Fly通常是单例的，这是因为浏览器中的JavaScript代码不涉及线程问题，一个Fly通常就足够，只需要根据需要切换dom属性即可改变方法的行为。为了解决外部状态：dom入侵导致的共享性问题，ExtJS允许创建多个Fly的实例： [crayon-69e5926aacad2593157339/] Java中的valueOf() Boolean、Byte、Character、Short、Long类的静态方法valueOf()实现了享元模式，例如： <a class="read-more" href="https://blog.gmem.cc/flyweight-pattern">[...]</a></p>
<p>The post <a rel="nofollow" href="https://blog.gmem.cc/flyweight-pattern">享元模式</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></description>
				<content:encoded><![CDATA[<div class="wri_content_clear_both"><div class="blog_h2"><span class="graybg">模式定义</span></div>
<p>享元模式，直译“轻量模式”，它允许通过<span style="background-color: #c0c0c0;">共享技术</span>有效的支持<span style="background-color: #c0c0c0;">大量细粒度</span>的对象。在GOF95中轻量模式被分类为结构型模式。 </p>
<div class="blog_h2"><span class="graybg">模式结构与说明</span></div>
<p><a href="/wp-content/uploads/2008/01/patterns_FlyweightPattern1.png"><img class="aligncenter size-full wp-image-7761" src="https://blog.gmem.cc/wp-content/uploads/2008/01/patterns_FlyweightPattern1.png" alt="patterns_FlyweightPattern" width="636" height="309" /></a></p>
<ol>
<li>Flyweight：抽象享元角色，那些需要外部状态的操作通过方法参数传入，它们改变方法的行为，但是<span style="background-color: #c0c0c0;">绝不影响享元的内部状态</span></li>
<li>ConcreteFlyweight：具体享元角色，如果有内部状态，必须负责为内部状态提供存储空间，享元对象的内部状态必须和对象所处的环境无关，从而使得享元对象可以被共享</li>
<li>UnsharedConcreteFlyweight：非共享的具体享元角色。Flyweight接口让共享成为可能而不是必须。UnsharedConcreteFlyweight往往是包含了多个ConcreteFlyweight的组合，由于后者已经是享元了，作为组合的前者就没必要也被共享</li>
<li>FlyweightFactory：享元工厂，负责创建和管理享元角色，必须保证享元对象可以被适当的共享，当客户端调用一个需要一个享元对象时，享元工厂负责检查是否已经存在符合要求的对象，如果没有则创建之</li>
<li>Client：客户端，负责维护对享元对象的引用，并自行存储所有享元的外部状态</li>
</ol>
<p>通过将那些<span style="background-color: #c0c0c0;">重复、不变</span>的数据缓存起来并加以复用，轻量模式可以解决<span style="background-color: #c0c0c0;">大量</span>包含<span style="background-color: #c0c0c0;">重复数据</span>的<span style="background-color: #c0c0c0;">细粒度对象</span>占用内存的问题。</p>
<p>享元模式的关键是合理的区分内部、外部状态，并将这两类状态隔离，内部状态与外部环境无关，因而可以保留在享元的内部，减少内存占用。外部状态则通过方法参数传入，由客户端决定。</p>
<p>享元模式的优点：</p>
<ol>
<li>减少对象数量、节约内存空间</li>
</ol>
<p>享元模式的缺点：</p>
<ol>
<li>维护共享对象需要额外开销</li>
</ol>
<div class="blog_h2"><span class="graybg">经典应用</span></div>
<div class="blog_h3"><span class="graybg">Ext.dom.AbstractElement.Fly</span></div>
<p>ExtJS中对DOM元素的处理，使用了享元模式。Fly类相当于Flyweight角色，Ext.dom.AbstractElement.fly()则充当了FlyweightFactory角色：</p>
<p><img class="aligncenter size-full wp-image-7770" src="https://blog.gmem.cc/wp-content/uploads/2008/01/patterns_FlyweightPattern_Ext_1.png" alt="patterns_FlyweightPattern_Ext_1" width="482" height="399" />Ext.dom.AbstractElement.Fly与Ext.dom.Element具有一样的结构，它允许在不创建Element的前提下来使用Element的接口来操控HTMLElement。Fly类的内部状态并不是固定的，相反，每次调用Ext.fly都会导致Fly.dom属性被设置为最新的HTMLElement，这种设计是为了透明性的考虑——与Element的接口一致。</p>
<p>享元角色Fly通常是单例的，这是因为浏览器中的JavaScript代码不涉及线程问题，一个Fly通常就足够，只需要根据需要切换dom属性即可改变方法的行为。为了解决外部状态：dom入侵导致的共享性问题，ExtJS允许创建多个Fly的实例：</p>
<pre class="crayon-plain-tag">fly: function(dom, named) {
    var fly = null, _flyweights = AbstractElement._flyweights; //静态变量存储所有享元对象的实例
    //named用来指定使用哪个享元实例，这可以用来避免外部状态dom入侵享元对象导致的不可共享问题
    named = named || '_global';
    dom = Ext.getDom(dom); //获得HTMLElement对象
    if (dom) {
        //如果享元不存在，则创建之
        fly = _flyweights[named] || (_flyweights[named] = new AbstractElement.Fly());
        fly.dom = dom;  //外部状态入侵
        fly.$cache = dom.id ? Ext.cache[dom.id] : null;
    }
    return fly;
}</pre>
<div class="blog_h3"><span class="graybg">Java中的valueOf()</span></div>
<p>Boolean、Byte、Character、Short、Long类的静态方法valueOf()实现了享元模式，例如：</p>
<pre class="crayon-plain-tag">//对于一些较小的整数，使用享元来进行共享，绝对值过大的整数则不使用享元
public class Integer {
    public static Integer valueOf(int i) {
        if(i &gt;= -128 &amp;&amp; i &lt;= IntegerCache.high)
            return IntegerCache.cache[i + 128];
        else
            return new Integer(i);
    }
}</pre>
<p>由于这些作为享元的类都实现了不变模式（Immutable） ，因此调用它们的任何方法，都不会破坏享元的可共享性</p>
<p>Java中的字符串也使用了享元模式，由于String类同样实现了不变模式，因此内容相同的字符串完全可以享元，这一模式已经内置到了Java的语法中：</p>
<pre class="crayon-plain-tag">String s1 = "123";
String s3 = "123"; //自动化的、基于语法的享元
String s2 = String.valueOf( "123" ); //本质上还是一个对象
String s4 = new String( "123" ); //这个就不是享元了，直接在堆上分配的对象
assert ( s1 == s2 &amp;&amp; s1 == s3 );</pre>
<div class="blog_h2"><span class="graybg">模式演变</span></div>
<ol>
<li>与单例模式联用：享元工厂一般实现为单例</li>
<li>与组合模式联用：非共享的享元对象往往是共享享元对象的组合</li>
<li>与状态模式联用：在状态模式中，会存在大量细粒度的状态对象，它们基本是可共享的：它们都针对性处理某一状态；变化的部分一般由Context传入</li>
</ol>
</div><p>The post <a rel="nofollow" href="https://blog.gmem.cc/flyweight-pattern">享元模式</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.gmem.cc/flyweight-pattern/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>职责链模式</title>
		<link>https://blog.gmem.cc/chain-of-responsibility-pattern</link>
		<comments>https://blog.gmem.cc/chain-of-responsibility-pattern#comments</comments>
		<pubDate>Wed, 10 Oct 2007 06:42:35 +0000</pubDate>
		<dc:creator><![CDATA[Alex]]></dc:creator>
				<category><![CDATA[Architecture]]></category>
		<category><![CDATA[设计模式]]></category>

		<guid isPermaLink="false">http://blog.gmem.cc/?p=7748</guid>
		<description><![CDATA[<p>模式定义 职责链模式使多个对象有机会处理请求，从而避免请求的发送者与接收者之间的耦合关系。将这些对象连成一个链条，沿着链条传递该请求，直到有一个对象处理它为止。 在GOF95中该模式被分类为行为型模式。 模式结构与说明   Handler：定义职责的接口，通常包含了处理请求的方法。successor引用链条中下一个Handler ConcreteHandler：具体职责类，该类实现对其职责范围内的请求的处理，如果不处理，就转发给successor Client：职责链的客户端，向链上的具体对象提交请求，让职责链负责处理 职责链模式的优点： 请求者和接收者解耦：请求者不知道是谁处理了请求 职责链的动态组合和排序 职责链模式的缺点： 可能会产生很多细粒度的Handler 不能保证请求被Handler处理，由于链条配置的不正确，可能无任何Handler支持某个请求 应用举例 这是一个简单的帮助系统的例子，如果按钮包含帮助信息的话，就显示当前按钮的帮助，否则按钮类控件的通用信息，如果通用信息也没有的话，则显提示用户“没有相关的帮助”： [crayon-69e5926aacceb990296531/] 经典应用 Servlet的过滤器机制 Sevlet框架中的Filter机制是一种变形的职责链模式。在Servlet的设计中，职责链被具象化为一个接口FilterChain，在作为Handler角色的Filter则不再维护对Successor的引用。 在Tomcat的Servlet实现中，ApplicationFilterChain负责调用配置的第一个Filter，后者则可以决定自己处理，或者通过FilterChain.doFilter调用下一个Filter，例如： [crayon-69e5926aaccef105862049/] <a class="read-more" href="https://blog.gmem.cc/chain-of-responsibility-pattern">[...]</a></p>
<p>The post <a rel="nofollow" href="https://blog.gmem.cc/chain-of-responsibility-pattern">职责链模式</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></description>
				<content:encoded><![CDATA[<div class="wri_content_clear_both"><div class="blog_h2"><span class="graybg">模式定义</span></div>
<p>职责链模式使<span style="background-color: #c0c0c0;">多个对象有机会处理请求</span>，从而避免请求的发送者与接收者之间的耦合关系。将这些对象连成一个链条，沿着链条传递该请求，直到<span style="background-color: #c0c0c0;">有一个对象处理它为止</span>。 在GOF95中该模式被分类为行为型模式。</p>
<div class="blog_h2"><span class="graybg">模式结构与说明</span></div>
<p> <img class="aligncenter size-full wp-image-7751" src="https://blog.gmem.cc/wp-content/uploads/2007/10/patterns_ChainOfRespPattern.png" alt="patterns_ChainOfRespPattern" width="523" height="255" /></p>
<ol>
<li>Handler：定义职责的接口，通常包含了处理请求的方法。successor引用链条中下一个Handler</li>
<li>ConcreteHandler：具体职责类，该类实现对其职责范围内的请求的处理，如果不处理，就转发给successor</li>
<li>Client：职责链的客户端，向链上的具体对象提交请求，让职责链负责处理</li>
</ol>
<p>职责链模式的优点：</p>
<ol>
<li>请求者和接收者解耦：请求者不知道是谁处理了请求</li>
<li>职责链的动态组合和排序</li>
</ol>
<p>职责链模式的缺点：</p>
<ol>
<li>可能会产生很多细粒度的Handler</li>
<li>不能保证请求被Handler处理，由于链条配置的不正确，可能无任何Handler支持某个请求</li>
</ol>
<div class="blog_h2"><span class="graybg">应用举例</span></div>
<p>这是一个简单的帮助系统的例子，如果按钮包含帮助信息的话，就显示当前按钮的帮助，否则按钮类控件的通用信息，如果通用信息也没有的话，则显提示用户“没有相关的帮助”：</p>
<pre class="crayon-plain-tag">class HelpeRequest( object ):
    @property
    def target( self ):
        return self.target 
    def __init__( self, target ):
        self.target = target
    
class HelpHandler( object ):
    @property
    def __init__( self, successor ):
        self.successor = successor
    def successor( self ):
        return self.successor
    def hasHelpContent( self ):
        return False
    def handleHelp( self, request ):
        return self.successor.handleHelp( self, request )

class ButtonHelperHandler( HelpHandler ):
    def handleHelp( self, request ):
        if self.hasHelpContent( request.target ):
            print 'Button Help content.'
        else:
            HelpHandler.handleHelp( self, request )

class WidgetHelperHandler( HelpHandler ):
    def handleHelp( self, request ):
        if self.hasHelpContent( request.target ):
            print 'Widget Help content.'
        else:
            HelpHandler.handleHelp( self, request )
            
class DefaultHelperHandler( HelpHandler ):
    def handleHelp( self, request ):
        print 'No help available.'
        
if __name__ == '__main__':
    # 装配责任链
    dhh = DefaultHelperHandler( None )
    whh = WidgetHelperHandler( dhh )
    bhh = ButtonHelperHandler( whh )
    # 使用责任链
    req = HelpeRequest( 'A button' )
    bhh.handleHelp( req )</pre>
<div class="blog_h2"><span class="graybg">经典应用</span></div>
<div class="blog_h3"><span class="graybg">Servlet的过滤器机制</span></div>
<p>Sevlet框架中的Filter机制是一种变形的职责链模式。在Servlet的设计中，职责链被具象化为一个接口FilterChain，在作为Handler角色的Filter则不再维护对Successor的引用。<br /><img class="aligncenter size-full wp-image-7755" src="https://blog.gmem.cc/wp-content/uploads/2007/10/patterns_ChainOfRespPattern_ServletFilter.png" alt="patterns_ChainOfRespPattern_ServletFilter" width="540" height="361" /></p>
<p>在Tomcat的Servlet实现中，ApplicationFilterChain负责调用配置的第一个Filter，后者则可以决定自己处理，或者通过FilterChain.doFilter调用下一个Filter，例如：</p>
<pre class="crayon-plain-tag">public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
    throws IOException, ServletException {
	//进行各种处理，例如处理编码、处理身份验证
        ……
	// 沿着链条传递请求
	chain.doFilter(request, response);
}</pre>
<div class="blog_h2"><span class="graybg">模式演变</span></div>
<ol>
<li>功能链：在标准的职责链中，一旦有Handler处理请求即结束，功能链允许<span style="background-color: #c0c0c0;">多个Handler完成对请求的一部分处理</span>，典型的例子是Servlet的过滤器机制</li>
<li>与组合模式联用：在必要的情况下，可以使用组合模式构建复杂的树形结构作为Handler的链条</li>
<li>与命令模式的相似点：两者都将请求者和接收者进行解耦</li>
</ol>
</div><p>The post <a rel="nofollow" href="https://blog.gmem.cc/chain-of-responsibility-pattern">职责链模式</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.gmem.cc/chain-of-responsibility-pattern/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
