<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Elian&#39;s blog page</title>
  
  <subtitle>我喜欢你😉</subtitle>
  <link href="https://eliano64.github.io/atom.xml" rel="self"/>
  
  <link href="https://eliano64.github.io/"/>
  <updated>2026-03-27T06:02:37.912Z</updated>
  <id>https://eliano64.github.io/</id>
  
  <author>
    <name>Eliano</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>260326 thinking kmp</title>
    <link href="https://eliano64.github.io/2026/03/26/260326-thinking-kmp/"/>
    <id>https://eliano64.github.io/2026/03/26/260326-thinking-kmp/</id>
    <published>2026-03-26T01:12:15.000Z</published>
    <updated>2026-03-27T06:02:37.912Z</updated>
    
    <content type="html"><![CDATA[<h1 id="1-kmp"><a href="#1-kmp" class="headerlink" title="1. kmp"></a>1. kmp</h1><p>在计算机科学中，字符串匹配是一个非常基础且高频的问题：给定一个主串 <code>S</code> 和一个模式串 <code>P</code>，我们需要在 <code>S</code> 中找到 <code>P</code> 出现的位置。</p><p>最直观的暴力匹配算法（Brute-Force）十分简单粗暴：用两个指针 <code>i</code> 和 <code>j</code> 分别指向 <code>S</code> 和 <code>P</code>，如果字符匹配，就齐头并进；一旦遇到不匹配的字符，主串指针 <code>i</code> 就要被迫“回退”到上一次开始匹配的下一个位置，而模式串指针 <code>j</code> 则回到起点重新开始。这种做法在最坏情况下的时间复杂度高达 $O(n \times m)$。</p><p><strong>KMP算法</strong>（由 Knuth、Morris 和 Pratt 三人共同发明）的伟大之处就在于：<strong>它向我们证明了主串指针 <code>i</code> 是不需要回退的</strong>。</p><p>为什么可以不回退？因为在发生不匹配之前，前面那部分字符我们已经实打实地比较过了。KMP 算法的核心思想就是<strong>榨干这些“已知匹配信息”的全部价值</strong>。既然已经知道前面有哪些字符是匹配的，当在某个位置发生失配时，我们不让 <code>j</code> 回到起点，而是让它滑动到一个“最合理”的位置继续和当前的主串字符比较。</p><p>比如模式串 <code>p=aabaad</code>，主串 <code>s=aabaabaad</code>。当 <code>p[5]</code> 与 <code>s[5]</code> 失配时，我们确切知道前面成功匹配的子串是 <code>aabaa</code>。由于这部分子串存在内部重复的结构（<code>p[0..1] == p[3..4]</code>），我们只需将模式串向右滑动，直接用 <code>p[2]</code> 去和 <code>s[5]</code> 对齐并继续比较即可。这既保证了前面的字符依然完美贴合，又实现了步子迈得最稳的“最小回退”。</p><p>这就引出了一个关键问题：在任何位置失配时，如何决定模式串指针接下来该跳到哪里？</p><p>答案完全取决于模式串本身的结构。发生失配时，我们本质上是在寻找已匹配子串的“最大自我重叠”。我们试图将模式串向右滑动，寻找一个新下标，使得滑动距离最小且滑动后的模式串头部能和失配点前的主串已知字符匹配。</p><p>为了在匹配过程中能以 $O(1)$ 的时间瞬间查出跳跃位置，我们会提前对模式串进行预处理，把每个失配位置对应的“最小回退下标”记录下来，这就构成了 <code>next</code> 数组。</p><h1 id="2-next-数组的推导与实现"><a href="#2-next-数组的推导与实现" class="headerlink" title="2. next 数组的推导与实现"></a>2. <code>next</code> 数组的推导与实现</h1><p><code>next[i]</code>: 当 <code>p[i]</code> 处失配时，模式串指针 <code>i</code> 应该回退到的下标（即滑动后用来与主串失配字符对齐的模式串新下标）。</p><p>很显然，如果<code>p[0]</code>失配，则必然要求<code>p</code>从<code>s</code>的下一个元素开始匹配。于是设<code>next[0]=-1</code>，表示<code>p[0]</code>应移动到<code>s</code>下一个下标再开始比较。</p><p>若<code>p[1]</code>失配，则尝试使用<code>p[0]</code>去匹配，<code>next[1]=0</code></p><p>而在其他情况下，以模式串<code>p=aabaad</code>，求<code>next[5]</code>为例，可以这样计算：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">s: aabaa | (失配)</span><br><span class="line">p: aabaa | d &lt;- i=5</span><br><span class="line">    aaba | a &lt;- i=4,但此时p[1]与p[2]不与s匹配</span><br><span class="line">     aab | a &lt;- i=3,但此时p[1]与p[2]不与s匹配</span><br><span class="line">      aa | b &lt;- i=2,此时匹配，最小回退，next[5]=2，完成.</span><br></pre></td></tr></table></figure><p>若是<code>p=abc</code>，则<code>next[2]=0</code>。因为必须得回退到前面为空才行。空串与任何串匹配。</p><p>代码实现如下：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">bool</span> <span class="title function_">compare</span><span class="params">(<span class="type">char</span> s[], <span class="type">int</span> len, <span class="type">int</span> step)</span>&#123;</span><br><span class="line"><span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>;i+step&lt;len;++i)&#123;</span><br><span class="line"><span class="keyword">if</span>(s[i]!=s[i+step])&#123;</span><br><span class="line"><span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//如果模式串长度&lt;2则退化到单个字符或空串，这种平凡情况不讨论。</span></span><br><span class="line"><span class="type">int</span> *next = (<span class="type">int</span> *)<span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(<span class="type">int</span>)*pLen);</span><br><span class="line">next[<span class="number">0</span>] = <span class="number">-1</span>;</span><br><span class="line">next[<span class="number">1</span>] = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">2</span>;i&lt;pLen;++i)&#123;</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> j=<span class="number">1</span>;j&lt;=i;++j)&#123;</span><br><span class="line">        <span class="keyword">if</span>(compare(p,i,j))&#123;</span><br><span class="line">            next[i]=i-j;</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>使用即：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">findSubStr</span><span class="params">(<span class="type">char</span> * s, <span class="type">char</span> * p, <span class="type">int</span> * next, <span class="type">int</span> sLen, <span class="type">int</span> pLen)</span>&#123;</span><br><span class="line">    <span class="keyword">while</span> (i &lt; sLen &amp;&amp; j &lt; pLen) &#123;</span><br><span class="line">        <span class="keyword">if</span> (j == <span class="number">-1</span> || s[i] == p[j]) &#123;</span><br><span class="line">            i++;</span><br><span class="line">            j++;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            j = next[j];</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (j == pLen) &#123;</span><br><span class="line">        <span class="keyword">return</span> i - j;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="3-next数组的改进"><a href="#3-next数组的改进" class="headerlink" title="3. next数组的改进"></a>3. <code>next</code>数组的改进</h1><p>但<code>next</code>数组仍会带来多余的比较：若<code>next[i]==j</code>且<code>p[i]==p[j]</code>时，当<code>i</code>处失配，说明主串在该处必然不是<code>p[i]</code>即<code>p[j]</code>，退到<code>j</code>必然还会再退回<code>next[j]</code>。那么<code>p[j]</code>与之比较就多余。</p><p>于是做一下改进，即在给<code>next[i]</code>赋值时加上：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span>(p[i] = =p[next[i]])&#123;</span><br><span class="line">    next[i] = next[next[i]];</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;1-kmp&quot;&gt;&lt;a href=&quot;#1-kmp&quot; class=&quot;headerlink&quot; title=&quot;1. kmp&quot;&gt;&lt;/a&gt;1. kmp&lt;/h1&gt;&lt;p&gt;在计算机科学中，字符串匹配是一个非常基础且高频的问题：给定一个主串 &lt;code&gt;S&lt;/code&gt; 和一个模式串 </summary>
      
    
    
    
    <category term="data structure &amp; algorithm" scheme="https://eliano64.github.io/categories/data-structure-algorithm/"/>
    
    
    <category term="data structure &amp; algorithm" scheme="https://eliano64.github.io/tags/data-structure-algorithm/"/>
    
    <category term="kmp" scheme="https://eliano64.github.io/tags/kmp/"/>
    
  </entry>
  
  <entry>
    <title>251122 tool Nginx</title>
    <link href="https://eliano64.github.io/2025/11/29/251122-tool-Nginx/"/>
    <id>https://eliano64.github.io/2025/11/29/251122-tool-Nginx/</id>
    <published>2025-11-29T08:53:12.000Z</published>
    <updated>2025-11-29T09:29:25.983Z</updated>
    
    <content type="html"><![CDATA[<h1 id="1-问题情景"><a href="#1-问题情景" class="headerlink" title="1. 问题情景"></a>1. 问题情景</h1><p>在aliyun服务器（ubuntu）上部署了一个后端服务，端口为 8080。然而，由于安全策略的限制，外部无法直接<code>&lt;ip&gt;:8080</code>访问该端口。</p><h1 id="2-解决方案"><a href="#2-解决方案" class="headerlink" title="2. 解决方案"></a>2. 解决方案</h1><p>配置 Nginx 作为反向代理，将外部请求转发到该后端服务即可。</p><h2 id="2-1-配置Nginx"><a href="#2-1-配置Nginx" class="headerlink" title="2.1. 配置Nginx"></a>2.1. 配置Nginx</h2><ol><li>安装Nginx</li></ol><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> apt update</span><br><span class="line"><span class="built_in">sudo</span> apt install nginx</span><br></pre></td></tr></table></figure><h2 id="2-2-反向代理"><a href="#2-2-反向代理" class="headerlink" title="2.2. 反向代理"></a>2.2. 反向代理</h2><p>编辑Nginx配置文件</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> vim /etc/nginx/sites-available/default</span><br></pre></td></tr></table></figure><p>在 <code>server</code> 块中添加以下配置：</p><figure class="highlight nginx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="section">location</span> /api/ &#123;</span><br><span class="line">    <span class="attribute">proxy_pass</span> http://localhost:8080/;</span><br><span class="line">    <span class="attribute">proxy_set_header</span> Host <span class="variable">$host</span>;</span><br><span class="line">    <span class="attribute">proxy_set_header</span> X-Real-IP <span class="variable">$remote_addr</span>;</span><br><span class="line">    <span class="attribute">proxy_set_header</span> X-Forwarded-For <span class="variable">$proxy_add_x_forwarded_for</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>保存并退出编辑器, 然后重启Nginx</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 检查配置是否正确</span></span><br><span class="line"><span class="built_in">sudo</span> nginx -t</span><br><span class="line"><span class="comment"># 重启Nginx</span></span><br><span class="line"><span class="built_in">sudo</span> systemctl restart nginx</span><br></pre></td></tr></table></figure><p>这样配置后，外部就可以通过 <code>&lt;ip&gt;/api/</code> 访问到后端服务了。实质是Nginx将外部请求<code>&lt;ip&gt;:80/api/</code> 转发到服务器的 <code>localhost:8080</code> 上。</p><h1 id="3-其他相关配置"><a href="#3-其他相关配置" class="headerlink" title="3. 其他相关配置"></a>3. 其他相关配置</h1><h2 id="3-1-服务器采用SSL"><a href="#3-1-服务器采用SSL" class="headerlink" title="3.1. 服务器采用SSL"></a>3.1. 服务器采用SSL</h2><p>使用 certbot 自动申请 Let’s Encrypt 证书：</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> apt install certbot python3-certbot-nginx</span><br><span class="line"><span class="built_in">sudo</span> certbot --nginx -d &lt;your-domain&gt;</span><br></pre></td></tr></table></figure><p>按提示操作，certbot 会自动修改 Nginx 配置以启用 HTTPS（监听 443 端口），并复用你之前定义的转发规则（如 <code>/api/</code> 转发到 8080）。</p><h2 id="3-2-将React前端打包后静态托管"><a href="#3-2-将React前端打包后静态托管" class="headerlink" title="3.2. 将React前端打包后静态托管"></a>3.2. 将React前端打包后静态托管</h2><p>通常在本地或 CI 环境执行构建，然后将产物部署到服务器。</p><p>执行构建命令 <code>npm run build</code>，然后将生成的 <code>dist</code> 目录下的所有文件上传到服务器的 <code>/var/www/html</code> 目录。</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 先清空服务器目标目录里旧的静态文件</span></span><br><span class="line">ssh user@&lt;server-ip&gt; <span class="string">&quot;rm -rf /var/www/html/*&quot;</span></span><br></pre></td></tr></table></figure><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 使用 scp 上传</span></span><br><span class="line">scp -r dist/* user@&lt;server-ip&gt;:/var/www/html/</span><br></pre></td></tr></table></figure><p>然后在 Nginx 配置中, 添加以下 location 块来托管这些静态文件：</p><figure class="highlight nginx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="section">location</span> / &#123;</span><br><span class="line">    <span class="attribute">root</span> /var/www/html;</span><br><span class="line">    <span class="attribute">index</span> index.html;</span><br><span class="line">    <span class="attribute">try_files</span> <span class="variable">$uri</span> <span class="variable">$uri</span>/ /index.html;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>然后重启Nginx</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> nginx -t</span><br><span class="line"><span class="built_in">sudo</span> systemctl restart nginx</span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;1-问题情景&quot;&gt;&lt;a href=&quot;#1-问题情景&quot; class=&quot;headerlink&quot; title=&quot;1. 问题情景&quot;&gt;&lt;/a&gt;1. 问题情景&lt;/h1&gt;&lt;p&gt;在aliyun服务器（ubuntu）上部署了一个后端服务，端口为 8080。然而，由于安全策略的限制，外</summary>
      
    
    
    
    <category term="tool" scheme="https://eliano64.github.io/categories/tool/"/>
    
    
    <category term="tool" scheme="https://eliano64.github.io/tags/tool/"/>
    
    <category term="Nginx" scheme="https://eliano64.github.io/tags/Nginx/"/>
    
  </entry>
  
  <entry>
    <title>251122 thinking visitor</title>
    <link href="https://eliano64.github.io/2025/11/29/251122-thinking-visitor/"/>
    <id>https://eliano64.github.io/2025/11/29/251122-thinking-visitor/</id>
    <published>2025-11-29T08:32:16.000Z</published>
    <updated>2025-11-29T10:13:50.515Z</updated>
    
    <content type="html"><![CDATA[<h1 id="1-什么是访问者模式"><a href="#1-什么是访问者模式" class="headerlink" title="1. 什么是访问者模式"></a>1. 什么是访问者模式</h1><p>访问者模式（Visitor Pattern）是一种行为型设计模式，它允许你在不改变对象结构（类）的情况下，定义作用于这些对象的新操作。</p><p>核心思想是将<strong>数据结构</strong>与<strong>数据操作</strong>分离。</p><p>通常情况下，对象的方法（操作）是定义在对象内部的。但在访问者模式中，我们将操作逻辑提取出来，封装在一个独立的“访问者”对象中。当我们需要对一组对象执行操作时，我们让这些对象“接受”访问者，然后访问者会根据对象的具体类型执行相应的逻辑。</p><h1 id="2-为什么需要访问者模式"><a href="#2-为什么需要访问者模式" class="headerlink" title="2. 为什么需要访问者模式"></a>2. 为什么需要访问者模式</h1><p>在软件开发中，我们经常面临这样的困境：<br>我们有一个稳定的对象结构（例如一个包含不同类型节点的语法树，或者一个包含不同几何形状的绘图系统），但我们需要经常在这个结构上定义新的操作（例如语法检查、代码生成、计算面积、导出 XML 等）。</p><p>传统的做法往往会导致问题。首先，如果在节点或形状类中堆砌各种业务逻辑（如绘图、导出、计算），就会严重违背<strong>单一职责原则</strong>，让类变得臃肿不堪。其次，每当我们需要新增一种操作时，就不得不去修改所有的类，这无疑打破了<strong>开闭原则</strong>。最后，相关的业务逻辑被分散在各个角落，难以集中管理和维护。</p><p><strong>访问者模式巧妙地化解了这些矛盾</strong>。它实现了<strong>关注点分离</strong>，让具体的形状类只专注于管理自身的数据结构，而将复杂的业务逻辑交由访问者处理。这样一来，扩展性得到了极大的提升——当我们需要新增功能（比如“导出 JSON”）时，只需添加一个新的访问者类即可，无需改动现有的任何代码。</p><p>因此，访问者模式非常适合那些<strong>对象结构相对稳定，但操作逻辑经常变化</strong>的场景，特别是当你需要对一组对象执行多种互不相关的操作时。</p><p>不过需要注意的是，它也有短板：如果你需要频繁增加新的元素类型（比如新增一个 <code>Triangle</code> 形状），就需要修改 Visitor 接口及所有实现类，这反而违背了开闭原则。所以，选用此模式前，请确保你的对象结构是足够稳定的。</p><h1 id="3-访问者模式的实现（go）"><a href="#3-访问者模式的实现（go）" class="headerlink" title="3. 访问者模式的实现（go）"></a>3. 访问者模式的实现（go）</h1><p>假设我们有一个几何形状库，包含 <code>Circle</code>（圆形）和 <code>Rectangle</code>（矩形）。我们希望对这些形状执行两种不同的操作：</p><ol><li><strong>AreaCalculator</strong>：计算面积。</li><li><strong>JsonExporter</strong>：导出形状的 JSON 描述。</li></ol><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 1. 定义元素接口 (Element)</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// Shape 接口代表“元素”，它定义了一个 Accept 方法，</span></span><br><span class="line"><span class="comment">// 用于接收访问者。</span></span><br><span class="line"><span class="keyword">type</span> Shape <span class="keyword">interface</span> &#123;</span><br><span class="line">Accept(v Visitor)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 2. 定义具体元素 (Concrete Element)</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// Circle 圆形</span></span><br><span class="line"><span class="keyword">type</span> Circle <span class="keyword">struct</span> &#123;</span><br><span class="line">Radius <span class="type">float64</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Accept 实现 Shape 接口</span></span><br><span class="line"><span class="comment">// 关键点：利用 Go 的类型系统，将自己(c)传给访问者的 VisitCircle 方法</span></span><br><span class="line"><span class="comment">// 这就是“双重分派”的第一步</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(c *Circle)</span></span> Accept(v Visitor) &#123;</span><br><span class="line">v.VisitCircle(c)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Rectangle 矩形</span></span><br><span class="line"><span class="keyword">type</span> Rectangle <span class="keyword">struct</span> &#123;</span><br><span class="line">Width, Height <span class="type">float64</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Accept 实现 Shape 接口</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(r *Rectangle)</span></span> Accept(v Visitor) &#123;</span><br><span class="line">v.VisitRectangle(r)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 3. 定义访问者接口 (Visitor)</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// Visitor 接口定义了对每种具体元素的操作方法</span></span><br><span class="line"><span class="keyword">type</span> Visitor <span class="keyword">interface</span> &#123;</span><br><span class="line">VisitCircle(c *Circle)</span><br><span class="line">VisitRectangle(r *Rectangle)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 4. 定义具体访问者 (Concrete Visitor)</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// --- 访问者 1: 面积计算器 ---</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> AreaCalculator <span class="keyword">struct</span> &#123;</span><br><span class="line">area <span class="type">float64</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(a *AreaCalculator)</span></span> VisitCircle(c *Circle) &#123;</span><br><span class="line">fmt.Println(<span class="string">&quot;AreaCalculator: Calculating area for Circle&quot;</span>)</span><br><span class="line">a.area += math.Pi * c.Radius * c.Radius</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(a *AreaCalculator)</span></span> VisitRectangle(r *Rectangle) &#123;</span><br><span class="line">fmt.Println(<span class="string">&quot;AreaCalculator: Calculating area for Rectangle&quot;</span>)</span><br><span class="line">a.area += r.Width * r.Height</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// --- 访问者 2: JSON 导出器 ---</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> JsonExporter <span class="keyword">struct</span>&#123;&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(j *JsonExporter)</span></span> VisitCircle(c *Circle) &#123;</span><br><span class="line">fmt.Printf(<span class="string">&quot;JSON: &#123; \&quot;type\&quot;: \&quot;circle\&quot;, \&quot;radius\&quot;: %.2f &#125;\n&quot;</span>, c.Radius)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(j *JsonExporter)</span></span> VisitRectangle(r *Rectangle) &#123;</span><br><span class="line">fmt.Printf(<span class="string">&quot;JSON: &#123; \&quot;type\&quot;: \&quot;rectangle\&quot;, \&quot;width\&quot;: %.2f, \&quot;height\&quot;: %.2f &#125;\n&quot;</span>, r.Width, r.Height)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>客户端代码</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line"><span class="comment">// 创建一组形状（对象结构）</span></span><br><span class="line">shapes := []Shape&#123;</span><br><span class="line">&amp;Circle&#123;Radius: <span class="number">5</span>&#125;,</span><br><span class="line">&amp;Rectangle&#123;Width: <span class="number">4</span>, Height: <span class="number">6</span>&#125;,</span><br><span class="line">&amp;Circle&#123;Radius: <span class="number">2</span>&#125;,</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 场景 1: 使用“面积计算”访问者</span></span><br><span class="line">fmt.Println(<span class="string">&quot;--- Calculating Area ---&quot;</span>)</span><br><span class="line">areaCalc := &amp;AreaCalculator&#123;&#125;</span><br><span class="line"><span class="keyword">for</span> _, s := <span class="keyword">range</span> shapes &#123;</span><br><span class="line">s.Accept(areaCalc)</span><br><span class="line">&#125;</span><br><span class="line">fmt.Printf(<span class="string">&quot;Total Area: %.2f\n&quot;</span>, areaCalc.area)</span><br><span class="line"></span><br><span class="line">fmt.Println(<span class="string">&quot;\n--- Exporting JSON ---&quot;</span>)</span><br><span class="line"><span class="comment">// 场景 2: 使用“JSON 导出”访问者</span></span><br><span class="line">jsonExp := &amp;JsonExporter&#123;&#125;</span><br><span class="line"><span class="keyword">for</span> _, s := <span class="keyword">range</span> shapes &#123;</span><br><span class="line">s.Accept(jsonExp)</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>如果我们现在想增加一个“计算周长”功能，只需要创建一个新的 <code>PerimeterCalculator</code> 结构体并实现 <code>Visitor</code> 接口即可。<code>Circle</code> 和 <code>Rectangle</code> 的代码完全不需要动。这就是<strong>开闭原则</strong>的体现。</p><p>类图</p><p><img src="/../asserts/visitor_figure_1.png"></p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;1-什么是访问者模式&quot;&gt;&lt;a href=&quot;#1-什么是访问者模式&quot; class=&quot;headerlink&quot; title=&quot;1. 什么是访问者模式&quot;&gt;&lt;/a&gt;1. 什么是访问者模式&lt;/h1&gt;&lt;p&gt;访问者模式（Visitor Pattern）是一种行为型设计模式，它允许</summary>
      
    
    
    
    <category term="Design Pattern" scheme="https://eliano64.github.io/categories/Design-Pattern/"/>
    
    
    <category term="Design Pattern" scheme="https://eliano64.github.io/tags/Design-Pattern/"/>
    
    <category term="go" scheme="https://eliano64.github.io/tags/go/"/>
    
    <category term="Interface Oriented Design" scheme="https://eliano64.github.io/tags/Interface-Oriented-Design/"/>
    
    <category term="Visitor Pattern" scheme="https://eliano64.github.io/tags/Visitor-Pattern/"/>
    
  </entry>
  
  <entry>
    <title>251122 tool swagger</title>
    <link href="https://eliano64.github.io/2025/11/22/251122-tool-swagger/"/>
    <id>https://eliano64.github.io/2025/11/22/251122-tool-swagger/</id>
    <published>2025-11-22T13:33:20.000Z</published>
    <updated>2025-11-24T08:37:44.328Z</updated>
    
    <content type="html"><![CDATA[<p>在后端开发中，接口文档与代码难以同步是常见的问题。Swagger 通过“代码即文档”的方式解决了这一点：开发者编写特定格式的代码注释，工具自动生成标准化的 API 文档和交互式调试页面。</p><p>本文主要介绍如何在 Go 后端项目中集成 Swagger。</p><h1 id="1-环境准备"><a href="#1-环境准备" class="headerlink" title="1. 环境准备"></a>1. 环境准备</h1><p>在项目根目下执行安装命令：</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 1. 安装文档生成器 (CLI)</span></span><br><span class="line">go install github.com/swaggo/swag/cmd/swag@latest</span><br><span class="line"></span><br><span class="line"><span class="comment"># 2. 安装 Gin 适配器</span></span><br><span class="line">go get github.com/swaggo/gin-swagger@latest</span><br><span class="line">go get github.com/swaggo/files@latest</span><br></pre></td></tr></table></figure><h1 id="2-swagger注释语法详解"><a href="#2-swagger注释语法详解" class="headerlink" title="2. swagger注释语法详解"></a>2. swagger注释语法详解</h1><p>Swagger 的核心在于写对注释。注释分为全局配置和接口配置。</p><h2 id="2-1-全局配置-main-go"><a href="#2-1-全局配置-main-go" class="headerlink" title="2.1 全局配置 (main.go)"></a>2.1 全局配置 (main.go)</h2><p>放在 <code>main</code> 函数上方，用于定义文档的通用信息。</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// @title           项目名称 (如: 支付系统 API)</span></span><br><span class="line"><span class="comment">// @version         1.0</span></span><br><span class="line"><span class="comment">// @description     这里写项目的详细描述...</span></span><br><span class="line"><span class="comment">// @host            localhost:8080</span></span><br><span class="line"><span class="comment">// @BasePath        /api/v1</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123; <span class="comment">//... &#125;</span></span><br></pre></td></tr></table></figure><h2 id="2-2-接口配置-Controller"><a href="#2-2-接口配置-Controller" class="headerlink" title="2.2 接口配置 (Controller)"></a>2.2 接口配置 (Controller)</h2><p>放在 <code>Controller/Handler</code> 函数上方。参考下方的语法拆解。</p><p>基础语法结构:</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// @Key          Value...</span></span><br></pre></td></tr></table></figure><p>常用标签速查表</p><table><thead><tr><th>标签</th><th>必填</th><th>说明</th><th>示例</th></tr></thead><tbody><tr><td>@Summary</td><td>是</td><td>接口简短标题</td><td>获取用户列表</td></tr><tr><td>@Description</td><td>否</td><td>详细描述</td><td>支持分页，支持按姓名搜索</td></tr><tr><td>@Tags</td><td>否</td><td>接口分组(分类)</td><td>User</td></tr><tr><td>@Accept</td><td>否</td><td>请求数据类型</td><td>json, multipart&#x2F;form-data</td></tr><tr><td>@Produce</td><td>否</td><td>响应数据类型</td><td>json</td></tr><tr><td>@Param</td><td>否</td><td>参数定义</td><td>见下方详解</td></tr><tr><td>@Success&#x2F;@failure</td><td>是</td><td>成功&#x2F;失败响应</td><td>200 {object} User</td></tr><tr><td>@Router</td><td>是</td><td>路由路径 [方法]</td><td>&#x2F;users [get]</td></tr></tbody></table><p><strong>重点 1：@Param 参数详解</strong></p><p>格式如下：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">// @Param  参数名  参数位置  参数类型  是否必填  &quot;参数描述&quot;</span><br></pre></td></tr></table></figure><ol><li>参数位置 (In) 有哪几种？<br>path: 路径参数，如 &#x2F;users&#x2F;{id} 中的 id。<br>query: URL 问号后面的参数，如 &#x2F;users?page&#x3D;1。<br>body: POST&#x2F;PUT 请求体，通常对应一个结构体。<br>header: 请求头参数，如 Authorization。</li><li>常见场景写法示例：<br>场景 A：路径参数 (GET &#x2F;users&#x2F;:id)<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// @Param  id  path  int  true  &quot;用户ID&quot;</span></span><br></pre></td></tr></table></figure></li></ol><p>场景 B：查询参数 (GET &#x2F;users?name&#x3D;xxx)</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// @Param  name  query  string  false  &quot;用户名&quot;</span></span><br></pre></td></tr></table></figure><p>场景 C：JSON 请求体 (POST &#x2F;users)注意：当位置是 body 时，参数类型是结构体名。</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// @Param  request  body  model.CreateUserRequest  true  &quot;注册信息&quot;</span></span><br></pre></td></tr></table></figure><p><strong>重点 2：@Success 响应详解</strong></p><p>格式如下：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">// @Success  HTTP状态码  &#123;数据类型&#125;  返回结构体  &quot;可选描述&quot;</span><br></pre></td></tr></table></figure><ul><li>返回对象：&#x2F;&#x2F; @Success 200 {object} model.User</li><li>返回数组：&#x2F;&#x2F; @Success 200 {array} model.User</li><li>返回字符串：&#x2F;&#x2F; @Success 200 {string} string “ok”</li><li>出错响应：&#x2F;&#x2F; @Failure 400 {object} map[string]string</li></ul><h1 id="3-完整代码示例"><a href="#3-完整代码示例" class="headerlink" title="3. 完整代码示例"></a>3. 完整代码示例</h1><p>下面展示两个典型接口：一个简单的 GET，一个复杂的 POST。</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="comment">// LoginReq 登录请求参数</span></span><br><span class="line"><span class="keyword">type</span> LoginReq <span class="keyword">struct</span> &#123;</span><br><span class="line">    Username <span class="type">string</span> <span class="string">`json:&quot;username&quot; example:&quot;admin&quot;`</span> <span class="comment">// example 字段会在文档中显示默认值</span></span><br><span class="line">    Password <span class="type">string</span> <span class="string">`json:&quot;password&quot; example:&quot;123456&quot;`</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// User 用户响应结构</span></span><br><span class="line"><span class="keyword">type</span> User <span class="keyword">struct</span> &#123;</span><br><span class="line">    ID   <span class="type">int</span>    <span class="string">`json:&quot;id&quot;`</span></span><br><span class="line">    Name <span class="type">string</span> <span class="string">`json:&quot;name&quot;`</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// GetUserList 获取用户列表 (Query 参数示例)</span></span><br><span class="line"><span class="comment">// @Summary      获取用户列表</span></span><br><span class="line"><span class="comment">// @Tags         User</span></span><br><span class="line"><span class="comment">// @Param        page      query    int     false  &quot;页码&quot;</span></span><br><span class="line"><span class="comment">// @Param        keyword   query    string  false  &quot;搜索关键字&quot;</span></span><br><span class="line"><span class="comment">// @Success      200       &#123;array&#125;  User</span></span><br><span class="line"><span class="comment">// @Router       /users [get]</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">GetUserList</span><span class="params">(c *gin.Context)</span></span> &#123; <span class="comment">/* ... */</span> &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Login 用户登录 (Body 参数示例)</span></span><br><span class="line"><span class="comment">// @Summary      用户登录</span></span><br><span class="line"><span class="comment">// @Tags         Auth</span></span><br><span class="line"><span class="comment">// @Accept       json</span></span><br><span class="line"><span class="comment">// @Produce      json</span></span><br><span class="line"><span class="comment">// @Param        request   body     LoginReq  true  &quot;登录信息&quot;</span></span><br><span class="line"><span class="comment">// @Success      200       &#123;object&#125; User</span></span><br><span class="line"><span class="comment">// @Router       /login [post]</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">Login</span><span class="params">(c *gin.Context)</span></span> &#123; <span class="comment">/* ... */</span> &#125;</span><br></pre></td></tr></table></figure><blockquote><p>Swagger 基于 函数（handler） 生成文档，而不是基于路由表<br>Swagger（swaggo）遵循的规则是：<strong>文档注释必须写在处理该路由的函数（handler）之前。</strong>所以不能使用匿名函数。</p></blockquote><h1 id="4-生成与运行"><a href="#4-生成与运行" class="headerlink" title="4. 生成与运行"></a>4. 生成与运行</h1><h2 id="4-1-第一步：生成文档"><a href="#4-1-第一步：生成文档" class="headerlink" title="4.1 第一步：生成文档"></a>4.1 第一步：生成文档</h2><p>在 main.go 所在目录执行：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">swag init</span><br></pre></td></tr></table></figure><h2 id="4-2-第二步：引入代码"><a href="#4-2-第二步：引入代码" class="headerlink" title="4.2 第二步：引入代码"></a>4.2 第二步：引入代码</h2><p>在 main.go 中加入两行代码：</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> (</span><br><span class="line">    <span class="comment">// ... 其他包</span></span><br><span class="line">    <span class="comment">// 1. 引入 swag 依赖</span></span><br><span class="line">    swaggerFiles <span class="string">&quot;github.com/swaggo/files&quot;</span></span><br><span class="line">    ginSwagger <span class="string">&quot;github.com/swaggo/gin-swagger&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 2. 必须引入生成的 docs 包</span></span><br><span class="line">    _ <span class="string">&quot;your/module/docs&quot;</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">    r := gin.Default()</span><br><span class="line">    <span class="comment">// ... 路由注册 ...</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 3. 注册 Swagger 路由</span></span><br><span class="line">    r.GET(<span class="string">&quot;/swagger/*any&quot;</span>, ginSwagger.WrapHandler(swaggerFiles.Handler))</span><br><span class="line">    r.Run(<span class="string">&quot;:8080&quot;</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="4-3-第三步：验证"><a href="#4-3-第三步：验证" class="headerlink" title="4.3 第三步：验证"></a>4.3 第三步：验证</h2><p>访问 <a href="http://localhost:8080/swagger/index.html">swagger 文档</a>。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;在后端开发中，接口文档与代码难以同步是常见的问题。Swagger 通过“代码即文档”的方式解决了这一点：开发者编写特定格式的代码注释，工具自动生成标准化的 API 文档和交互式调试页面。&lt;/p&gt;
&lt;p&gt;本文主要介绍如何在 Go 后端项目中集成 Swagger。&lt;/p&gt;
&lt;h</summary>
      
    
    
    
    <category term="tool" scheme="https://eliano64.github.io/categories/tool/"/>
    
    
    <category term="tool" scheme="https://eliano64.github.io/tags/tool/"/>
    
    <category term="swagger" scheme="https://eliano64.github.io/tags/swagger/"/>
    
  </entry>
  
  <entry>
    <title>251122 thinking template</title>
    <link href="https://eliano64.github.io/2025/11/22/251122-thinking-template/"/>
    <id>https://eliano64.github.io/2025/11/22/251122-thinking-template/</id>
    <published>2025-11-22T08:47:11.000Z</published>
    <updated>2025-11-22T12:55:58.940Z</updated>
    
    <content type="html"><![CDATA[<h1 id="1-什么是模板方法模式？"><a href="#1-什么是模板方法模式？" class="headerlink" title="1. 什么是模板方法模式？"></a>1. 什么是模板方法模式？</h1><p>模板方法模式（Template Method Pattern）是一种行为设计模式，它在一个方法中定义一个算法的骨架，而将一些步骤的实现延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。核心便是利用了<strong>多态性</strong>，父类定义了算法的骨架，子类实现具体的步骤。</p><p>在 Go 语言中，由于没有经典的类继承，模板方法模式通常通过<strong>接口和结构体嵌入</strong>来模拟。</p><h1 id="2-为什么需要模板方法模式？"><a href="#2-为什么需要模板方法模式？" class="headerlink" title="2. 为什么需要模板方法模式？"></a>2. 为什么需要模板方法模式？</h1><p>当你发现多个类有相似的算法，但只在某些细节上有所不同时，模板方法模式就非常有用。</p><ul><li><strong>代码复用</strong>：将所有子类中通用的算法逻辑提取到唯一的父类中，避免代码重复。</li><li><strong>框架控制</strong>：它定义了一个框架，让子类在不改变框架结构的前提下，填充特定的业务逻辑。这在开发框架或库时非常常见。</li><li><strong>遵循开闭原则</strong>：你可以在不修改模板方法的情况下，引入新的子类来扩展功能。</li></ul><h1 id="3-模板方法模式的实现（go）"><a href="#3-模板方法模式的实现（go）" class="headerlink" title="3. 模板方法模式的实现（go）"></a>3. 模板方法模式的实现（go）</h1><p>以一个通用的“资源下载器”为例。无论从 HTTP 还是 FTP 下载，其核心流程是固定的：<code>初始化 -&gt; 下载数据 -&gt; 保存文件 -&gt; 清理</code>。其中，具体如何下载数据是可变的。</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> downloader</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">&quot;fmt&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Steps <span class="keyword">interface</span> &#123;</span><br><span class="line">fetch(uri <span class="type">string</span>) ([]<span class="type">byte</span>, <span class="type">error</span>)</span><br><span class="line">finish()</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Downloader[T Steps] <span class="keyword">struct</span> &#123;</span><br><span class="line">impl T</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Download 是模板方法，定义了下载资源的流程</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(d *Downloader[T])</span></span> Download(uri <span class="type">string</span>) &#123;</span><br><span class="line">fmt.Println(<span class="string">&quot;Preparing to download...&quot;</span>)</span><br><span class="line">data, err := d.impl.fetch(uri)</span><br><span class="line"><span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">fmt.Printf(<span class="string">&quot;Download failed: %v\n&quot;</span>, err)</span><br><span class="line"><span class="keyword">return</span></span><br><span class="line">&#125;</span><br><span class="line">d.save(data)</span><br><span class="line">d.impl.finish()</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(d *Downloader[T])</span></span> save(data []<span class="type">byte</span>) &#123;</span><br><span class="line">fmt.Printf(<span class="string">&quot;Saving data (size: %d bytes) to a file.\n&quot;</span>, <span class="built_in">len</span>(data))</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> HttpDownloader <span class="keyword">struct</span>&#123;&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(h HttpDownloader)</span></span> fetch(uri <span class="type">string</span>) ([]<span class="type">byte</span>, <span class="type">error</span>) &#123;</span><br><span class="line">fmt.Printf(<span class="string">&quot;Downloading from HTTP URI: %s\n&quot;</span>, uri)</span><br><span class="line"><span class="keyword">return</span> []<span class="type">byte</span>(<span class="string">&quot;http data&quot;</span>), <span class="literal">nil</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(h HttpDownloader)</span></span> finish() &#123;</span><br><span class="line">fmt.Println(<span class="string">&quot;HTTP download finished.&quot;</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>客户端通过 <code>Downloader</code> 接口与具体的下载器进行交互，调用模板方法即可。</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">&quot;downloader&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">httpDownloader := downloader.Downloader[downloader.HttpDownloader]&#123;&#125;</span><br><span class="line">httpDownloader.Download(<span class="string">&quot;http://example.com/file.zip&quot;</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>类图</p><p><img src="/../asserts/template_figure_1.png" alt="template method pattern class diagram"></p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;1-什么是模板方法模式？&quot;&gt;&lt;a href=&quot;#1-什么是模板方法模式？&quot; class=&quot;headerlink&quot; title=&quot;1. 什么是模板方法模式？&quot;&gt;&lt;/a&gt;1. 什么是模板方法模式？&lt;/h1&gt;&lt;p&gt;模板方法模式（Template Method Patte</summary>
      
    
    
    
    <category term="Design Pattern" scheme="https://eliano64.github.io/categories/Design-Pattern/"/>
    
    
    <category term="Design Pattern" scheme="https://eliano64.github.io/tags/Design-Pattern/"/>
    
    <category term="go" scheme="https://eliano64.github.io/tags/go/"/>
    
    <category term="Interface Oriented Design" scheme="https://eliano64.github.io/tags/Interface-Oriented-Design/"/>
    
    <category term="Template Method Pattern" scheme="https://eliano64.github.io/tags/Template-Method-Pattern/"/>
    
  </entry>
  
  <entry>
    <title>251122 thinking strategy</title>
    <link href="https://eliano64.github.io/2025/11/22/251122-thinking-strategy/"/>
    <id>https://eliano64.github.io/2025/11/22/251122-thinking-strategy/</id>
    <published>2025-11-22T08:46:45.000Z</published>
    <updated>2025-11-22T11:46:14.891Z</updated>
    
    <content type="html"><![CDATA[<h1 id="1-什么是策略模式？"><a href="#1-什么是策略模式？" class="headerlink" title="1. 什么是策略模式？"></a>1. 什么是策略模式？</h1><p>策略模式（Strategy Pattern）是一种行为设计模式，它定义了一系列算法，并将每个算法封装起来，使它们可以相互替换。策略模式让算法的变化独立于使用算法的客户端。</p><p>核心思想是：当一个任务有多种处理方式（策略）时，将这些方式抽象成一个共同的接口，并为每种方式提供一个具体的实现类。环境（Context）角色持有一个策略接口的引用，从而能在运行时动态地切换和使用不同的策略。</p><p>与状态模式不同，策略模式的各种策略是独立的，客户端需要知道所有的策略，才能选择合适的策略。而状态模式的各种状态是相关的，客户端只需要知道当前状态，就可以根据状态转换规则自动切换到下一个状态。</p><h1 id="2-为什么需要策略模式？"><a href="#2-为什么需要策略模式？" class="headerlink" title="2. 为什么需要策略模式？"></a>2. 为什么需要策略模式？</h1><p>在软件开发中，我们经常会遇到需要根据不同条件选择不同行为的场景。最直接的方法是使用 <code>if-else</code> 或 <code>switch-case</code> 结构。但当分支逻辑变得复杂，或者需要频繁增删新的分支时，这种方式会导致代码臃肿、难以维护，并且违反了“开闭原则”（对扩展开放，对修改关闭）。</p><p>策略模式正是为了解决这个问题而生。它有以下优点：</p><ul><li><strong>简化条件逻辑</strong>：将复杂的 <code>if-else</code> 链条替换为独立的策略类，使代码更清晰。</li><li><strong>高扩展性</strong>：增加一个新的策略只需添加一个新的实现类，无需修改现有代码，符合开闭原则。</li><li><strong>算法重用</strong>：策略可以被多个上下文共享。</li><li><strong>运行时动态切换</strong>：可以在程序运行时根据需要更换算法策略。</li></ul><h1 id="3-策略模式的实现（go）"><a href="#3-策略模式的实现（go）" class="headerlink" title="3. 策略模式的实现（go）"></a>3. 策略模式的实现（go）</h1><p>让我们以一个数据压缩服务为例。该服务需要支持多种压缩算法，如 <code>zip</code> 和 <code>gzip</code>。</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> compression</span><br><span class="line"></span><br><span class="line"><span class="comment">// Strategy 接口定义了压缩算法</span></span><br><span class="line"><span class="comment">// 首先定义一个策略接口，它包含所有策略都必须实现的 `compress` 方法。</span></span><br><span class="line"><span class="keyword">type</span> Strategy <span class="keyword">interface</span> &#123;</span><br><span class="line">compress(data []<span class="type">byte</span>) ([]<span class="type">byte</span>, <span class="type">error</span>)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 为 `zip` 和 `gzip` 创建具体的策略实现。</span></span><br><span class="line"><span class="comment">// zipStrategy 实现了 zip 压缩</span></span><br><span class="line"><span class="keyword">type</span> zipStrategy <span class="keyword">struct</span>&#123;&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(z *zipStrategy)</span></span> compress(data []<span class="type">byte</span>) ([]<span class="type">byte</span>, <span class="type">error</span>) &#123;</span><br><span class="line"><span class="comment">// 模拟 zip 压缩</span></span><br><span class="line">fmt.Println(<span class="string">&quot;Compressing data using ZIP&quot;</span>)</span><br><span class="line"><span class="keyword">return</span> <span class="built_in">append</span>([]<span class="type">byte</span>(<span class="string">&quot;zip_&quot;</span>), data...), <span class="literal">nil</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// gzipStrategy 实现了 gzip 压缩</span></span><br><span class="line"><span class="keyword">type</span> gzipStrategy <span class="keyword">struct</span>&#123;&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(g *gzipStrategy)</span></span> compress(data []<span class="type">byte</span>) ([]<span class="type">byte</span>, <span class="type">error</span>) &#123;</span><br><span class="line"><span class="comment">// 模拟 gzip 压缩</span></span><br><span class="line">fmt.Println(<span class="string">&quot;Compressing data using GZIP&quot;</span>)</span><br><span class="line"><span class="keyword">return</span> <span class="built_in">append</span>([]<span class="type">byte</span>(<span class="string">&quot;gzip_&quot;</span>), data...), <span class="literal">nil</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 创建上下文（Context）</span></span><br><span class="line"><span class="comment">// 上下文持有对策略接口的引用，并提供一个方法来执行策略。它还允许在运行时更换策略。</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// Compressor 是上下文，它使用一个策略来压缩数据</span></span><br><span class="line"><span class="keyword">type</span> Compressor <span class="keyword">struct</span> &#123;</span><br><span class="line">S *Strategy</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// SetStrategy 允许在运行时更换策略</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(c *Compressor)</span></span> SetStrategy(strategy Strategy) &#123;</span><br><span class="line">c.S = &amp;strategy</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// CompressData 执行压缩</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(c *Compressor)</span></span> CompressData(data []<span class="type">byte</span>) ([]<span class="type">byte</span>, <span class="type">error</span>) &#123;</span><br><span class="line"><span class="keyword">return</span> c.S.compress(data)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>客户端可以根据需要创建和切换策略。</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"><span class="string">&quot;fmt&quot;</span></span><br><span class="line"><span class="string">&quot;log&quot;</span></span><br><span class="line"><span class="string">&quot;compression&quot;</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">data := []<span class="type">byte</span>(<span class="string">&quot;This is some data to be compressed.&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 初始使用 zip 策略</span></span><br><span class="line">zip := &amp;compression.zipStrategy&#123;&#125;</span><br><span class="line">compressor := &amp;compression.Compressor&#123;</span><br><span class="line">S: zip,</span><br><span class="line">&#125;                       </span><br><span class="line"></span><br><span class="line">compressedData, err := compressor.CompressData(data)</span><br><span class="line"><span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">log.Fatal(err)</span><br><span class="line">&#125;</span><br><span class="line">fmt.Printf(<span class="string">&quot;Compressed data: %s&quot;</span>, compressedData)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 在运行时切换到 gzip 策略</span></span><br><span class="line">gzip := &amp;compression.gzipStrategy&#123;&#125;</span><br><span class="line">compressor.SetStrategy(gzip)</span><br><span class="line"></span><br><span class="line">compressedData, err = compressor.CompressData(data)</span><br><span class="line"><span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">log.Fatal(err)</span><br><span class="line">&#125;</span><br><span class="line">fmt.Printf(<span class="string">&quot;Compressed data: %s&quot;</span>, compressedData)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>类图：</p><p><img src="/../asserts/strategy_figure_1.png" alt="strategy pattern class diagram"></p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;1-什么是策略模式？&quot;&gt;&lt;a href=&quot;#1-什么是策略模式？&quot; class=&quot;headerlink&quot; title=&quot;1. 什么是策略模式？&quot;&gt;&lt;/a&gt;1. 什么是策略模式？&lt;/h1&gt;&lt;p&gt;策略模式（Strategy Pattern）是一种行为设计模式，它定义了</summary>
      
    
    
    
    <category term="Design Pattern" scheme="https://eliano64.github.io/categories/Design-Pattern/"/>
    
    
    <category term="Design Pattern" scheme="https://eliano64.github.io/tags/Design-Pattern/"/>
    
    <category term="go" scheme="https://eliano64.github.io/tags/go/"/>
    
    <category term="Interface Oriented Design" scheme="https://eliano64.github.io/tags/Interface-Oriented-Design/"/>
    
    <category term="Strategy Pattern" scheme="https://eliano64.github.io/tags/Strategy-Pattern/"/>
    
  </entry>
  
  <entry>
    <title>251122 thinking state</title>
    <link href="https://eliano64.github.io/2025/11/22/251122-thinking-state/"/>
    <id>https://eliano64.github.io/2025/11/22/251122-thinking-state/</id>
    <published>2025-11-22T08:22:16.000Z</published>
    <updated>2025-11-22T10:46:12.130Z</updated>
    
    <content type="html"><![CDATA[<h1 id="1-什么是状态模式"><a href="#1-什么是状态模式" class="headerlink" title="1. 什么是状态模式"></a>1. 什么是状态模式</h1><p>状态模式（State Pattern）是一种行为型设计模式，它允许对象在其内部状态改变时改变其行为。状态模式将对象的状态封装成独立的类，使客户端可以透明地切换对象的状态。</p><p>为了方便理解，这里引入有限状态机（Finite State Machine）的概念。</p><h2 id="1-1-有限状态机"><a href="#1-1-有限状态机" class="headerlink" title="1.1 有限状态机"></a>1.1 有限状态机</h2><p>有限状态机（Finite State Machine，FSM）是一个数学模型，它包含一组有限的状态、一组输入事件、一个初始状态以及一个状态转移函数。FSM 在任何给定时间点都处于其中一个状态。当接收到输入事件时，它会根据状态转移函数转换到新的状态。</p><p>状态模式可以看作是 FSM 的一种面向对象实现。</p><h1 id="2-为什么需要状态模式"><a href="#2-为什么需要状态模式" class="headerlink" title="2. 为什么需要状态模式"></a>2. 为什么需要状态模式</h1><p>当一个对象的行为取决于它的状态，并且它必须在运行时根据状态改变行为时，可以使用状态模式。状态模式可以将与特定状态相关的行为局部化，并且使得状态转换显式化。</p><h1 id="3-状态模式的实现（go）"><a href="#3-状态模式的实现（go）" class="headerlink" title="3. 状态模式的实现（go）"></a>3. 状态模式的实现（go）</h1><p>让我们以一个自动售货机为例。售货机有以下几种状态：</p><ul><li><code>hasItem</code>: 有商品状态</li><li><code>noItem</code>: 无商品状态</li><li><code>itemRequested</code>: 商品请求状态</li><li><code>hasMoney</code>: 已投币状态</li></ul><p>状态转移如下：</p><ul><li><code>hasItem</code> -&gt; <code>itemRequested</code>：<code>requestItem()</code></li><li><code>itemRequested</code> -&gt; <code>hasMoney</code>：<code>insertMoney()</code></li><li><code>hasMoney</code> -&gt; <code>hasItem</code>：<code>dispenseItem()&gt;0</code></li><li><code>hasMoney</code> -&gt; <code>noItem</code>：<code>dispenseItem()==0</code> </li><li><code>NoItem</code> -&gt; <code>hasItem</code>：<code>addItem(n)</code></li><li><code>HasItem</code> -&gt; <code>HasItem</code>：<code>addItem(n)</code></li></ul><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> vending</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">&quot;fmt&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 状态接口：定义状态的行为</span></span><br><span class="line"><span class="keyword">type</span> state <span class="keyword">interface</span> &#123;</span><br><span class="line">requestItem() <span class="type">error</span></span><br><span class="line">insertMoney() <span class="type">error</span></span><br><span class="line">dispenseItem() <span class="type">int</span></span><br><span class="line">addItem(n <span class="type">int</span>) <span class="type">int</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 上下文：持有当前状态引用</span></span><br><span class="line"><span class="keyword">type</span> context <span class="keyword">struct</span> &#123;</span><br><span class="line">s *state</span><br><span class="line">    count <span class="type">int</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">NewContext</span><span class="params">(itemCount <span class="type">int</span>)</span></span> *context &#123;</span><br><span class="line"><span class="keyword">var</span> s state</span><br><span class="line"><span class="keyword">if</span> itemCount &gt; <span class="number">0</span> &#123;</span><br><span class="line">s = &amp;hasItemState&#123;&#125;</span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">s = &amp;noItemState&#123;&#125;</span><br><span class="line">&#125;</span><br><span class="line">    c := &amp;context&#123;s: s, count: itemCount&#125;</span><br><span class="line">    initAllStates(c)</span><br><span class="line"><span class="keyword">return</span> c</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> (</span><br><span class="line">hasItem       *hasItemState</span><br><span class="line">noItem        *noItemState </span><br><span class="line">itemRequested *itemRequestedState</span><br><span class="line">hasMoney      *hasMoneyState</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment">// initAllStates 初始化所有状态</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">initAllStates</span><span class="params">(c *context)</span></span> &#123;</span><br><span class="line">hasItem       = &amp;hasItemState&#123;c: c&#125;</span><br><span class="line">noItem        = &amp;noItemState&#123;c: c&#125;</span><br><span class="line">itemRequested = &amp;itemRequestedState&#123;c: c&#125;</span><br><span class="line">hasMoney      = &amp;hasMoneyState&#123;c: c&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(c *context)</span></span> RequestItem() <span class="type">error</span>  &#123; <span class="keyword">return</span> c.s.requestItem(c) &#125;</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(c *context)</span></span> InsertMoney() <span class="type">error</span>  &#123; <span class="keyword">return</span> c.s.insertMoney(c) &#125;</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(c *context)</span></span> DispenseItem() <span class="type">error</span> &#123; </span><br><span class="line">    <span class="keyword">if</span> c.s.dispenseItem() == <span class="number">0</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> fmt.Errorf(<span class="string">&quot;can&#x27;t dispense&quot;</span>)</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">nil</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(c *context)</span></span> AddItem(n <span class="type">int</span>) <span class="type">error</span>     &#123; </span><br><span class="line">    <span class="keyword">if</span> c.s.addItem(n) == <span class="number">0</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> fmt.Errorf(<span class="string">&quot;can&#x27;t add item&quot;</span>)</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">nil</span></span><br><span class="line">&#125; </span><br><span class="line"></span><br><span class="line"><span class="comment">// 状态切换</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(c *context)</span></span> setState(s *state) &#123; c.s = s &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//展示数量（非必须）</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(c *context)</span></span> ShowCount() <span class="type">int</span> &#123; <span class="keyword">return</span> c.count &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// NoItem：无货</span></span><br><span class="line"><span class="keyword">type</span> noItemState <span class="keyword">struct</span>&#123;c *context&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(st *noItemState)</span></span> requestItem() <span class="type">error</span>  &#123; <span class="keyword">return</span> fmt.Errorf(<span class="string">&quot;no item&quot;</span>) &#125;</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(st *noItemState)</span></span> insertMoney() <span class="type">error</span>  &#123; <span class="keyword">return</span> fmt.Errorf(<span class="string">&quot;no item&quot;</span>) &#125;</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(st *noItemState)</span></span> dispenseItem() <span class="type">int</span> &#123; <span class="keyword">return</span> <span class="number">0</span> &#125;</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(st *noItemState)</span></span> addItem(n <span class="type">int</span>) <span class="type">int</span> &#123;</span><br><span class="line">st.c.count += n</span><br><span class="line">c.setState(hasItem)</span><br><span class="line"><span class="keyword">return</span> st.c.count</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// HasItem：有货</span></span><br><span class="line"><span class="keyword">type</span> hasItemState <span class="keyword">struct</span>&#123;c *context&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(st *hasItemState)</span></span> requestItem() <span class="type">error</span> &#123;</span><br><span class="line">c.setState(itemRequested)</span><br><span class="line"><span class="keyword">return</span> <span class="literal">nil</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(st *hasItemState)</span></span> insertMoney() <span class="type">error</span>  &#123; <span class="keyword">return</span> fmt.Errorf(<span class="string">&quot;request first&quot;</span>) &#125;</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(st *hasItemState)</span></span> dispenseItem() <span class="type">int</span> &#123; <span class="keyword">return</span> <span class="number">0</span> &#125;</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(st *hasItemState)</span></span> addItem(n <span class="type">int</span>) <span class="type">int</span>      &#123; st.c.count+=n; <span class="keyword">return</span> st.c.count &#125; <span class="comment">// 自环</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// ItemRequested：已请求</span></span><br><span class="line"><span class="keyword">type</span> itemRequestedState <span class="keyword">struct</span>&#123;c *context&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(st *itemRequestedState)</span></span> requestItem() <span class="type">error</span>  &#123; <span class="keyword">return</span> fmt.Errorf(<span class="string">&quot;already requested&quot;</span>) &#125;</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(st *itemRequestedState)</span></span> insertMoney() <span class="type">error</span> &#123;</span><br><span class="line">c.setState(hasMoney)</span><br><span class="line"><span class="keyword">return</span> <span class="literal">nil</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(st *itemRequestedState)</span></span> dispenseItem() <span class="type">int</span> &#123; <span class="keyword">return</span> fmt.Errorf(<span class="string">&quot;insert first&quot;</span>) &#125;</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(st *itemRequestedState)</span></span> addItem(n <span class="type">int</span>) <span class="type">int</span>      &#123; st.c.count+=n; <span class="keyword">return</span> st.c.count &#125; <span class="comment">// 自环</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// HasMoney：已投币</span></span><br><span class="line"><span class="keyword">type</span> hasMoneyState <span class="keyword">struct</span>&#123;c *context&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(st *hasMoneyState)</span></span> requestItem() <span class="type">error</span> &#123; <span class="keyword">return</span> fmt.Errorf(<span class="string">&quot;already requested&quot;</span>) &#125;</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(st *hasMoneyState)</span></span> insertMoney() <span class="type">error</span> &#123; <span class="keyword">return</span> fmt.Errorf(<span class="string">&quot;already has money&quot;</span>) &#125;</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(st *hasMoneyState)</span></span> dispenseItem() <span class="type">int</span> &#123;</span><br><span class="line">st.c.count--</span><br><span class="line"><span class="keyword">if</span> st.c.count &gt; <span class="number">0</span> &#123;</span><br><span class="line">c.setState(hasItem)</span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">c.setState(noItem)</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">return</span> st.c.count</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(st *hasMoneyState)</span></span> addItem(n <span class="type">int</span>) <span class="type">int</span>  &#123; </span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span></span><br><span class="line">&#125; </span><br></pre></td></tr></table></figure><p>客户端</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"><span class="string">&quot;fmt&quot;</span></span><br><span class="line"><span class="string">&quot;log&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="string">&quot;vending&quot;</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">c := vending.NewContext(<span class="number">10</span>) <span class="comment">// 初始状态：hasItem</span></span><br><span class="line">    fmt.Println(<span class="string">&quot;count:&quot;</span>, c.ShowCount())</span><br><span class="line">err := c.RequestItem() <span class="comment">// 状态切换：hasItem -&gt; itemRequested</span></span><br><span class="line"><span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">log.Fatal(err)</span><br><span class="line">&#125;</span><br><span class="line">err = c.InsertMoney() <span class="comment">// 状态切换：itemRequested -&gt; hasMoney</span></span><br><span class="line"><span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">log.Fatal(err)</span><br><span class="line">&#125;</span><br><span class="line">err = c.DispenseItem() <span class="comment">// 状态切换：hasMoney -&gt; hasItem</span></span><br><span class="line"><span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">log.Fatal(err)</span><br><span class="line">&#125;</span><br><span class="line">    fmt.Println(<span class="string">&quot;count:&quot;</span>, c.ShowCount())</span><br><span class="line">    err = c.AddItem(<span class="number">5</span>) <span class="comment">// hasItem -&gt; hasItem</span></span><br><span class="line">    <span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">        log.Fatal(err)</span><br><span class="line">    &#125;</span><br><span class="line">    fmt.Println(<span class="string">&quot;count:&quot;</span>, c.ShowCount())</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>输出：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">count: 10</span><br><span class="line">count: 9</span><br><span class="line">count: 14</span><br></pre></td></tr></table></figure><p>FSM 图如下：</p><p><img src="/../asserts/state_figure_1.png" alt="state_figure_1"></p><p>类图</p><p><img src="/../asserts/state_figure_2.png" alt="state_figure_2"></p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;1-什么是状态模式&quot;&gt;&lt;a href=&quot;#1-什么是状态模式&quot; class=&quot;headerlink&quot; title=&quot;1. 什么是状态模式&quot;&gt;&lt;/a&gt;1. 什么是状态模式&lt;/h1&gt;&lt;p&gt;状态模式（State Pattern）是一种行为型设计模式，它允许对象在其内部状</summary>
      
    
    
    
    <category term="Design Pattern" scheme="https://eliano64.github.io/categories/Design-Pattern/"/>
    
    
    <category term="Design Pattern" scheme="https://eliano64.github.io/tags/Design-Pattern/"/>
    
    <category term="go" scheme="https://eliano64.github.io/tags/go/"/>
    
    <category term="Interface Oriented Design" scheme="https://eliano64.github.io/tags/Interface-Oriented-Design/"/>
    
    <category term="State Pattern" scheme="https://eliano64.github.io/tags/State-Pattern/"/>
    
  </entry>
  
  <entry>
    <title>251114 thinking jwt</title>
    <link href="https://eliano64.github.io/2025/11/14/251114-thinking-jwt/"/>
    <id>https://eliano64.github.io/2025/11/14/251114-thinking-jwt/</id>
    <published>2025-11-14T08:19:10.000Z</published>
    <updated>2025-11-17T08:04:33.806Z</updated>
    
    <content type="html"><![CDATA[<h1 id="1-什么是-JWT？"><a href="#1-什么是-JWT？" class="headerlink" title="1. 什么是 JWT？"></a>1. 什么是 JWT？</h1><p>JWT 全称是 JSON Web Token，由三部分组成，用点 . 隔开。例如： xxxxx.yyyyy.zzzzz</p><ol><li><p>Header (头部) - xxxxx</p><ul><li>内容 ：记录令牌的类型（就是 JWT）和加密算法（比如 HMAC SHA256）。</li><li>作用 ：告诉服务器怎么去解读这张令牌，包括令牌的类型和加密算法。</li></ul></li><li><p>Payload (载荷) - yyyyy</p><ul><li>内容 ：承载声明，存放实际的用户信息。一般为Subject(主体标识)、ExpiresAt(过期时间)、IssuedAt(签发时间)、Issuer(签发者)、NotBefore(生效时间)等。这部分内容是公开的，任何人都能解码看到，所以不能放密码等敏感信息。</li><li>作用 ：让服务器知道当前请求是谁发起的，有什么权限。</li></ul></li><li><p>Signature (签名) - zzzzz</p><ul><li>内容 ：由服务器的密钥对前两部分签名，生成的防伪标识。它是通过把头部和载荷组合起来，再加上一个只有服务器知道的“密钥”（Secret），然后用头部声明的加密算法生成的。</li><li>作用 ：<ul><li>防伪 ：客户端无法伪造签名，因为他们没有服务器的“密钥”。</li><li>防篡改 ：如果 载荷 (Payload) 里的任何信息（比如把普通用户ID改成管理员ID）被修改了，那么用同样的密钥算出来的签名就会和原始签名对不上。服务器一校验，就知道信息被改过了，就会拒绝这个请求。</li></ul></li></ul></li></ol><p>这种方式是“无状态的”，服务器不需要自己存储 session 信息。</p><h1 id="2-工作流程"><a href="#2-工作流程" class="headerlink" title="2. 工作流程"></a>2. 工作流程</h1><ol><li>登录 ：用户使用账号密码登录。</li><li>签发 ：服务器验证账号密码成功后，生成一个包含用户ID等信息的 JWT（就像办了一张有时效的门禁卡），并把这个 JWT 发送给前端。</li><li>携带 ：前端收到 JWT 后，把它存起来（比如存在浏览器的 localStorage ）。之后每次向服务器发请求，都在请求头 Authorization 里带上这个 JWT，格式通常是 Bearer <token> 。</li><li>验证 ：服务器收到请求后，从请求头里拿出 JWT。用自己保存的密钥去验证这个 JWT 的签名是否正确。<ul><li>如果签名正确，且没过期，服务器就相信这个请求是合法的，并从 JWT 的载荷中获取用户信息，处理请求。</li><li>如果签名错误或已过期，就拒绝请求（返回 401 Unauthorized 错误）。</li></ul></li></ol><p><img src="/../asserts/jwt.png" alt="jwt workflow"></p><h1 id="3-后端签发-验证-JWT（go）"><a href="#3-后端签发-验证-JWT（go）" class="headerlink" title="3. 后端签发&#x2F;验证 JWT（go）"></a>3. 后端签发&#x2F;验证 JWT（go）</h1><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line">  <span class="string">&quot;net/http&quot;</span></span><br><span class="line">  <span class="string">&quot;os&quot;</span></span><br><span class="line">  <span class="string">&quot;time&quot;</span></span><br><span class="line">  <span class="string">&quot;github.com/gin-gonic/gin&quot;</span></span><br><span class="line">  <span class="string">&quot;github.com/golang-jwt/jwt/v5&quot;</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment">// definition of user struct, in gorm model</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">  r := gin.Default()</span><br><span class="line">  secret := []<span class="type">byte</span>(<span class="string">&quot;1111111&quot;</span>) <span class="comment">// secret是自己设置的字符串，但实际不应明文写在代码里，实际可写在环境变量里，用secret := []byte(os.Getenv(&quot;JWT_SECRET&quot;))读取；或者写在某配置文件里，用类似于secret := []byte(os.ReadFile(filepath.Join(&quot;.&quot;, &quot;jwt.secret&quot;)))的方式从配置文件读取</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">// 注册路由组，所有 JWT 相关接口都在 /api 下</span></span><br><span class="line">  api := r.Group(<span class="string">&quot;/api&quot;</span>)</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 1. 登录接口：验证用户名密码后签发 JWT</span></span><br><span class="line">  api.POST(<span class="string">&quot;/login&quot;</span>, <span class="function"><span class="keyword">func</span><span class="params">(c *gin.Context)</span></span> &#123;</span><br><span class="line">    <span class="keyword">var</span> req <span class="keyword">struct</span>&#123; Username, Password <span class="type">string</span> &#125;</span><br><span class="line">    <span class="keyword">if</span> err := c.BindJSON(&amp;req); err != <span class="literal">nil</span> &#123; </span><br><span class="line">        c.Status(http.StatusBadRequest)</span><br><span class="line">        <span class="keyword">return</span> </span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 验证用户名密码（这里简单对比，实际应使用gorm从数据库查询）</span></span><br><span class="line">    <span class="keyword">if</span> req.Username != <span class="string">&quot;user&quot;</span> || req.Password != <span class="string">&quot;1&quot;</span> &#123;</span><br><span class="line">      c.Status(http.StatusUnauthorized)</span><br><span class="line">      <span class="keyword">return</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 创建一个待签名的 token，签名算法指定为 HS256</span></span><br><span class="line">    token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.RegisteredClaims&#123;</span><br><span class="line">      Subject:   req.Username, <span class="comment">// 用户名/uid作为 subject</span></span><br><span class="line">      ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)), <span class="comment">// 这里设置过期时间1小时后，可以设置为其他值</span></span><br><span class="line">      IssuedAt:  jwt.NewNumericDate(time.Now()),</span><br><span class="line">      NotBefore: jwt.NewNumericDate(time.Now()),</span><br><span class="line">    &#125;)</span><br><span class="line">    <span class="comment">// 用密钥签名 token</span></span><br><span class="line">    s, err := token.SignedString(secret);</span><br><span class="line">    <span class="keyword">if</span> err != <span class="literal">nil</span> &#123; </span><br><span class="line">        c.JSON(http.StatusInternalServerError, gin.H&#123;<span class="string">&quot;error&quot;</span>: <span class="string">&quot;Error creating token&quot;</span>&#125;)</span><br><span class="line">        <span class="keyword">return</span> </span><br><span class="line">    &#125;</span><br><span class="line">    c.JSON(http.StatusOK, gin.H&#123;<span class="string">&quot;token&quot;</span>: s&#125;)</span><br><span class="line">  &#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 2. 验证受保护接口</span></span><br><span class="line">    <span class="comment">// 中间件：检查请求头 Authorization 是否包含有效的 JWT</span></span><br><span class="line">  auth := <span class="function"><span class="keyword">func</span><span class="params">(c *gin.Context)</span></span> &#123;</span><br><span class="line">    h := c.GetHeader(<span class="string">&quot;Authorization&quot;</span>)</span><br><span class="line">    tokenStr := strings.TrimPrefix(h, <span class="string">&quot;Bearer &quot;</span>)</span><br><span class="line">    <span class="keyword">if</span> <span class="built_in">len</span>(tokenStr) == <span class="number">0</span> || tokenStr == h &#123; </span><br><span class="line">        c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H&#123;<span class="string">&quot;error&quot;</span>: <span class="string">&quot;Invalid token format&quot;</span>&#125;)</span><br><span class="line">        <span class="keyword">return</span> </span><br><span class="line">    &#125;</span><br><span class="line">    claims := &amp;jwt.RegisteredClaims&#123;&#125;</span><br><span class="line">    tok, err := jwt.ParseWithClaims(tokenStr, claims, <span class="function"><span class="keyword">func</span><span class="params">(t *jwt.Token)</span></span> (any, <span class="type">error</span>) &#123;</span><br><span class="line">      <span class="keyword">return</span> secret, <span class="literal">nil</span></span><br><span class="line">    &#125;)</span><br><span class="line">    <span class="keyword">if</span> err != <span class="literal">nil</span> || !tok.Valid &#123; </span><br><span class="line">        c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H&#123;<span class="string">&quot;error&quot;</span>: <span class="string">&quot;Invalid or expired token&quot;</span>&#125;)</span><br><span class="line">        <span class="keyword">return</span> </span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 把用户名设置到上下文，后续处理函数可以从上下文中获取</span></span><br><span class="line">    c.Set(<span class="string">&quot;Subject&quot;</span>, claims.Subject) </span><br><span class="line">    c.Next()</span><br><span class="line">  &#125;</span><br><span class="line">  api.GET(<span class="string">&quot;/me&quot;</span>, auth, <span class="function"><span class="keyword">func</span><span class="params">(c *gin.Context)</span></span> &#123; </span><br><span class="line">    <span class="comment">// 从上下文获取用户名</span></span><br><span class="line">    username, ok := c.Get(<span class="string">&quot;Subject&quot;</span>)</span><br><span class="line">    <span class="keyword">if</span> !ok &#123;</span><br><span class="line">      c.JSON(http.StatusInternalServerError, gin.H&#123;<span class="string">&quot;error&quot;</span>: <span class="string">&quot;User claims not found in context&quot;</span>&#125;)</span><br><span class="line">  <span class="keyword">return</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 这里是根据用户名/uid调用gorm相关查询用户信息的逻辑</span></span><br><span class="line">    <span class="comment">//...</span></span><br><span class="line">    c.JSON(http.StatusOK, gin.H&#123;<span class="string">&quot;username&quot;</span>: username&#125;) <span class="comment">//以json格式返回200状态码以及用户信息，可以根据实际情况添加其他用户信息</span></span><br><span class="line">  &#125;)</span><br><span class="line">  r.Run() <span class="comment">//默认8080。也可以指定端口，如 r.Run(&quot;:3000&quot;)</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="4-前端存储-使用-JWT（JavaScript）"><a href="#4-前端存储-使用-JWT（JavaScript）" class="headerlink" title="4. 前端存储&#x2F;使用 JWT（JavaScript）"></a>4. 前端存储&#x2F;使用 JWT（JavaScript）</h1><p>这里使用 localStorage；生产更推荐使用 httpOnly+Secure 的 Cookie。</p><ol><li>登录后存储 token</li></ol><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="title function_">fetch</span>(<span class="string">&quot;api/login&quot;</span>,&#123;</span><br><span class="line">  <span class="attr">method</span>:<span class="string">&quot;POST&quot;</span>,</span><br><span class="line">  <span class="attr">headers</span>:&#123;<span class="string">&quot;Content-Type&quot;</span>:<span class="string">&quot;application/json&quot;</span>&#125;,</span><br><span class="line">  <span class="attr">body</span>:<span class="title class_">JSON</span>.<span class="title function_">stringify</span>(&#123;<span class="attr">username</span>:<span class="string">&quot;user&quot;</span>,<span class="attr">password</span>:<span class="string">&quot;1&quot;</span>&#125;)</span><br><span class="line">&#125;).<span class="title function_">then</span>(<span class="function"><span class="params">r</span>=&gt;</span>r.<span class="title function_">json</span>()).<span class="title function_">then</span>(<span class="function"><span class="params">d</span>=&gt;</span><span class="variable language_">localStorage</span>.<span class="title function_">setItem</span>(<span class="string">&quot;token&quot;</span>, d.<span class="property">token</span>))</span><br></pre></td></tr></table></figure><ol start="2"><li>携带 token 访问受保护接口</li></ol><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> t = <span class="variable language_">localStorage</span>.<span class="title function_">getItem</span>(<span class="string">&quot;token&quot;</span>)</span><br><span class="line"><span class="title function_">fetch</span>(<span class="string">&quot;api/me&quot;</span>,&#123;<span class="attr">headers</span>:&#123;<span class="title class_">Authorization</span>:<span class="string">`Bearer <span class="subst">$&#123;t&#125;</span>`</span>&#125;&#125;)</span><br><span class="line">  .<span class="title function_">then</span>(<span class="function"><span class="params">r</span>=&gt;</span>r.<span class="title function_">json</span>())</span><br><span class="line">  .<span class="title function_">then</span>(<span class="variable language_">console</span>.<span class="property">log</span>)</span><br></pre></td></tr></table></figure><h1 id="5-secret这个字段是干什么的？"><a href="#5-secret这个字段是干什么的？" class="headerlink" title="5. secret这个字段是干什么的？"></a>5. <code>secret</code>这个字段是干什么的？</h1><p><code>secret</code> 是用于签名和验证 JWT 的密钥，可以把它设置成任何你想要的字符串。这个密钥是 JWT 安全性的核心，它扮演着“盐”的角色。</p><p><em>“盐”是什么意思？</em></p><p>在密码学中，“盐”指的是一小段随机的、附加的数据 ，它被加到原始密码上，然后再一起进行哈希（hash）或加密处理。</p><p>比如做一道炒鸡蛋，这道菜的“配方”（哈希算法）是公开的。</p><p>如果没有盐，则鸡蛋（ 原始数据 ）直接下锅炒。因为配方公开，任何人只要拿到鸡蛋，都能炒出和一模一样味道的菜。如果一个黑客知道使用的是“123456”这个“鸡蛋”，他就能轻松“炒”出一模一样的“菜”（哈希值），从而破解密码。</p><p>而现在，在炒鸡蛋时，jwt往里面加了一撮独有的、保密的“盐”。即使别人知道用的是鸡蛋、也知道配方，但因为不知道“盐”，他就永远也炒不出一模一样的味道。</p><p>所以“盐”的核心作用有两个：</p><ol><li>让结果独一无二。即使两个用户使用完全相同的密码（比如都是 123456 ），因为给他们分配了 不同 的“盐”，他们最终存储在数据库里的哈希值也是完全不同的。</li><li>抵御预计算攻击（如“彩虹表”） ：黑客们会提前计算好常用密码（如“123456”、“password”）的哈希值，做成一张大表（彩虹表），甚至直接查表就能反推出原始密码。但如果加了“盐”，原始数据变了，这张彩虹表就完全失效了，因为黑客不可能为每一个可能的“盐”都预先计算一张表。</li></ol><p>在 JWT 的场景里，则：</p><ul><li>“鸡蛋” ：是 JWT 的 Header (头部) 和 Payload (载荷)。</li><li>“盐” ：<code>secret</code> 。</li><li>“烹饪方法” ：是 Header 中指定的签名算法，比如 HMAC-SHA256 。</li><li>“炒鸡蛋” ：就是 JWT 的第三部分 Signature (签名)。</li></ul><p>它保证了：</p><ol><li>真实性 (Authenticity) ：只有知道 <code>secret</code> 这个“盐”的服务器才能制作出味道正确的“菜”（合法的签名）。任何伪造者都做不出来。</li><li>完整性 (Integrity) ：如果有人篡改 Payload 里的内容，由于无法知道 <code>secret</code>，则该jwt的Signature必定和服务器根据篡改后的Payload和<code>secret</code>重新计算出来的Signature不一致。验证签名时就会失败，服务器就会拒绝它。</li></ol>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;1-什么是-JWT？&quot;&gt;&lt;a href=&quot;#1-什么是-JWT？&quot; class=&quot;headerlink&quot; title=&quot;1. 什么是 JWT？&quot;&gt;&lt;/a&gt;1. 什么是 JWT？&lt;/h1&gt;&lt;p&gt;JWT 全称是 JSON Web Token，由三部分组成，用点 . 隔</summary>
      
    
    
    
    <category term="Base Knowledge" scheme="https://eliano64.github.io/categories/Base-Knowledge/"/>
    
    
    <category term="go" scheme="https://eliano64.github.io/tags/go/"/>
    
    <category term="jwt" scheme="https://eliano64.github.io/tags/jwt/"/>
    
    <category term="javascript" scheme="https://eliano64.github.io/tags/javascript/"/>
    
  </entry>
  
  <entry>
    <title>251114 thinking observer</title>
    <link href="https://eliano64.github.io/2025/11/14/251114-thinking-observer/"/>
    <id>https://eliano64.github.io/2025/11/14/251114-thinking-observer/</id>
    <published>2025-11-14T03:34:00.000Z</published>
    <updated>2025-11-22T11:27:17.589Z</updated>
    
    <content type="html"><![CDATA[<h1 id="1-什么是观察者模式？"><a href="#1-什么是观察者模式？" class="headerlink" title="1. 什么是观察者模式？"></a>1. 什么是观察者模式？</h1><p>观察者模式（Observer Pattern）用于在对象状态变化时，通知依赖它的多个观察者，实现一种一对多的订阅关系。被观察者只负责发布事件，不关心有多少观察者、它们如何处理，达到发布方与订阅方的解耦。</p><h1 id="2-为什么需要观察者模式？"><a href="#2-为什么需要观察者模式？" class="headerlink" title="2. 为什么需要观察者模式？"></a>2. 为什么需要观察者模式？</h1><p>当一个对象的变化需要联动多个组件时，若直接在对象内部逐个调用这些组件，将产生紧耦合和复杂的依赖。当组件增删或行为变化时，被观察者也不得不调整。通过观察者模式，我们将事件的发布与处理分离：被观察者只负责发出信号，观察者各自处理，从而降低耦合、提高扩展性。</p><h1 id="3-观察者模式的实现（go）"><a href="#3-观察者模式的实现（go）" class="headerlink" title="3. 观察者模式的实现（go）"></a>3. 观察者模式的实现（go）</h1><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">type</span> Observer <span class="keyword">interface</span> &#123;</span><br><span class="line">    Update(event <span class="type">string</span>, data any)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Subject <span class="keyword">interface</span> &#123;</span><br><span class="line">    Register(o Observer)</span><br><span class="line">    Unregister(o Observer)</span><br><span class="line">    Notify(event <span class="type">string</span>, data any)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Topic <span class="keyword">struct</span> &#123;</span><br><span class="line">    observers []Observer</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(t *Topic)</span></span> Register(o Observer) &#123;</span><br><span class="line">    t.observers = <span class="built_in">append</span>(t.observers, o)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(t *Topic)</span></span> Unregister(o Observer) &#123;</span><br><span class="line">    i := <span class="number">-1</span></span><br><span class="line">    <span class="keyword">for</span> idx, ob := <span class="keyword">range</span> t.observers &#123;</span><br><span class="line">        <span class="keyword">if</span> ob == o &#123;</span><br><span class="line">            i = idx</span><br><span class="line">            <span class="keyword">break</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> i &gt;= <span class="number">0</span> &#123;</span><br><span class="line">        t.observers = <span class="built_in">append</span>(t.observers[:i], t.observers[i+<span class="number">1</span>:]...)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(t *Topic)</span></span> Notify(event <span class="type">string</span>, data any) &#123;</span><br><span class="line">    <span class="keyword">for</span> _, o := <span class="keyword">range</span> t.observers &#123;</span><br><span class="line">        o.Update(event, data)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> PrintObserver <span class="keyword">struct</span> &#123;</span><br><span class="line">    name <span class="type">string</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(p *PrintObserver)</span></span> Update(event <span class="type">string</span>, data any) &#123;</span><br><span class="line">    fmt.Printf(<span class="string">&quot;[%s] %s: %v\n&quot;</span>, p.name, event, data)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>客户端代码如下：</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">    t := &amp;Topic&#123;&#125;</span><br><span class="line">    a := &amp;PrintObserver&#123;name: <span class="string">&quot;A&quot;</span>&#125;</span><br><span class="line">    b := &amp;PrintObserver&#123;name: <span class="string">&quot;B&quot;</span>&#125;</span><br><span class="line"></span><br><span class="line">    t.Register(a)</span><br><span class="line">    t.Register(b)</span><br><span class="line"></span><br><span class="line">    t.Notify(<span class="string">&quot;price_changed&quot;</span>, <span class="number">42</span>)</span><br><span class="line">    t.Unregister(a)</span><br><span class="line">    t.Notify(<span class="string">&quot;price_changed&quot;</span>, <span class="number">43</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>输出：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">[A] price_changed: 42</span><br><span class="line">[B] price_changed: 42</span><br><span class="line">[B] price_changed: 43</span><br></pre></td></tr></table></figure><p>类图：</p><div class="mermaid-wrap"><pre class="mermaid-src" hidden>    classDiagramclass subject {    &lt;&lt;interface&gt;&gt;    +Register(o observer)    +Unregister(o observer)    +Notify(event,data)}class concreteSubject {    +Register(o observer)    +Unregister(o observer)    +Notify(event,data)}class observer {    &lt;&lt;interface&gt;&gt;    +Update(event,data)}class concreteObserver {    +Update(event,data)}subject &lt;|.. concreteSubjectobserver &lt;|.. concreteObserverconcreteSubject o--&gt; observer : manages  </pre></div>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;1-什么是观察者模式？&quot;&gt;&lt;a href=&quot;#1-什么是观察者模式？&quot; class=&quot;headerlink&quot; title=&quot;1. 什么是观察者模式？&quot;&gt;&lt;/a&gt;1. 什么是观察者模式？&lt;/h1&gt;&lt;p&gt;观察者模式（Observer Pattern）用于在对象状态变化</summary>
      
    
    
    
    <category term="Design Pattern" scheme="https://eliano64.github.io/categories/Design-Pattern/"/>
    
    
    <category term="Design Pattern" scheme="https://eliano64.github.io/tags/Design-Pattern/"/>
    
    <category term="go" scheme="https://eliano64.github.io/tags/go/"/>
    
    <category term="Interface Oriented Design" scheme="https://eliano64.github.io/tags/Interface-Oriented-Design/"/>
    
    <category term="Observer Pattern" scheme="https://eliano64.github.io/tags/Observer-Pattern/"/>
    
  </entry>
  
  <entry>
    <title>251114 thinking memento</title>
    <link href="https://eliano64.github.io/2025/11/14/251114-thinking-memento/"/>
    <id>https://eliano64.github.io/2025/11/14/251114-thinking-memento/</id>
    <published>2025-11-14T03:21:02.000Z</published>
    <updated>2025-11-14T03:46:52.888Z</updated>
    
    <content type="html"><![CDATA[<h1 id="1-什么是备忘录模式？"><a href="#1-什么是备忘录模式？" class="headerlink" title="1. 什么是备忘录模式？"></a>1. 什么是备忘录模式？</h1><p>备忘录模式（Memento Pattern）用于在不破坏封装的前提下，捕获并保存对象的内部状态，以便之后将其恢复。核心思想是：状态快照应对外保持不透明，只有创建它的对象才能读写其内容。</p><h1 id="2-为什么需要备忘录模式？"><a href="#2-为什么需要备忘录模式？" class="headerlink" title="2. 为什么需要备忘录模式？"></a>2. 为什么需要备忘录模式？</h1><p>在需要撤销&#x2F;重做、事务性修改或阶段性试错的场景中，我们希望随时回到某个稳定的历史状态。如果将内部状态直接暴露给外部组件，会破坏封装并引入脆弱耦合。备忘录提供了一种更稳妥的做法：外部只负责保存与传递快照，而不窥视其内容；状态的读写权严格归属于原始对象本身。</p><h1 id="3-备忘录模式的实现（go）"><a href="#3-备忘录模式的实现（go）" class="headerlink" title="3. 备忘录模式的实现（go）"></a>3. 备忘录模式的实现（go）</h1><p>下面的实现采用“封装最严格”的变体：看护者只能看到窄接口 <code>Memento</code>，无法读取或修改状态；只有发起者可以访问具体备忘录以进行恢复。</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">type</span> Memento <span class="keyword">interface</span>&#123; mementoMarker() &#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> memento <span class="keyword">struct</span>&#123;</span><br><span class="line">    content <span class="type">string</span></span><br><span class="line">    cursor  <span class="type">int</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(m *memento)</span></span> mementoMarker()&#123;&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Editor <span class="keyword">struct</span>&#123;</span><br><span class="line">    content <span class="type">string</span></span><br><span class="line">    cursor  <span class="type">int</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(e *Editor)</span></span> SetContent(s <span class="type">string</span>)&#123; e.content = s &#125;</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(e *Editor)</span></span> SetCursor(pos <span class="type">int</span>)&#123; e.cursor = pos &#125;</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(e *Editor)</span></span> Save() Memento &#123; <span class="keyword">return</span> &amp;memento&#123;content: e.content, cursor: e.cursor&#125; &#125;</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(e *Editor)</span></span> Restore(mem Memento)&#123; mm := mem.(*memento); e.content, e.cursor = mm.content, mm.cursor &#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> History <span class="keyword">struct</span>&#123; stack []Memento &#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(h *History)</span></span> Push(m Memento)&#123; h.stack = <span class="built_in">append</span>(h.stack, m) &#125;</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(h *History)</span></span> Pop() &#123;</span><br><span class="line">    <span class="keyword">if</span> <span class="built_in">len</span>(h.stack) == <span class="number">0</span> &#123; <span class="keyword">return</span> &#125;</span><br><span class="line">    i := <span class="built_in">len</span>(h.stack)<span class="number">-1</span></span><br><span class="line">    m := h.stack[i]</span><br><span class="line">    h.stack = h.stack[:i]</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(h *History)</span></span> Top() Memento&#123;</span><br><span class="line">    <span class="keyword">if</span> <span class="built_in">len</span>(h.stack) == <span class="number">0</span> &#123; <span class="keyword">return</span> <span class="literal">nil</span> &#125;</span><br><span class="line">    <span class="keyword">return</span> h.stack[<span class="built_in">len</span>(h.stack)<span class="number">-1</span>]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>客户端代码如下：</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span>&#123;</span><br><span class="line">    e := &amp;Editor&#123;&#125;</span><br><span class="line">    h := &amp;History&#123;&#125;</span><br><span class="line"></span><br><span class="line">    e.SetContent(<span class="string">&quot;a&quot;</span>); e.SetCursor(<span class="number">1</span>); h.Push(e.Save())</span><br><span class="line">    e.SetContent(<span class="string">&quot;ab&quot;</span>); e.SetCursor(<span class="number">2</span>); h.Push(e.Save())</span><br><span class="line">    e.SetContent(<span class="string">&quot;abc&quot;</span>); e.SetCursor(<span class="number">3</span>)</span><br><span class="line"></span><br><span class="line">    h.Pop()</span><br><span class="line">    m := h.Top()</span><br><span class="line">    e.Restore(m)</span><br><span class="line">    fmt.Println(e.content, e.cursor)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>输出：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ab 2</span><br></pre></td></tr></table></figure><p>类图：</p><div class="mermaid-wrap"><pre class="mermaid-src" hidden>    classDiagramclass originator {    +Save() Memento    +Restore(m Memento)}class memento {    &lt;&lt;interface&gt;&gt;}class concreteMemento {    -state}class caretaker {    +Push(Memento)    +Pop() Memento    +Top() Memento}originator ..&gt; memento : Save&#x2F;Restorememento &lt;|.. concreteMementocaretaker o-- memento : stores  </pre></div>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;1-什么是备忘录模式？&quot;&gt;&lt;a href=&quot;#1-什么是备忘录模式？&quot; class=&quot;headerlink&quot; title=&quot;1. 什么是备忘录模式？&quot;&gt;&lt;/a&gt;1. 什么是备忘录模式？&lt;/h1&gt;&lt;p&gt;备忘录模式（Memento Pattern）用于在不破坏封装的前</summary>
      
    
    
    
    <category term="Design Pattern" scheme="https://eliano64.github.io/categories/Design-Pattern/"/>
    
    
    <category term="Design Pattern" scheme="https://eliano64.github.io/tags/Design-Pattern/"/>
    
    <category term="go" scheme="https://eliano64.github.io/tags/go/"/>
    
    <category term="Memento Pattern" scheme="https://eliano64.github.io/tags/Memento-Pattern/"/>
    
  </entry>
  
  <entry>
    <title>251114 thinking mediator</title>
    <link href="https://eliano64.github.io/2025/11/14/251114-thinking-mediator/"/>
    <id>https://eliano64.github.io/2025/11/14/251114-thinking-mediator/</id>
    <published>2025-11-14T02:48:39.000Z</published>
    <updated>2025-11-14T03:51:37.737Z</updated>
    
    <content type="html"><![CDATA[<h1 id="1-什么是中介者模式？"><a href="#1-什么是中介者模式？" class="headerlink" title="1. 什么是中介者模式？"></a>1. 什么是中介者模式？</h1><p>中介者模式（Mediator Pattern）通过引入一个“中介者”对象来封装对象之间的交互，使多个对象不需要显式引用彼此，改为只与中介者通信。它将协作编排从同事对象中抽离出来，降低耦合并集中管理交互规则。</p><h1 id="2-为什么需要中介者模式？"><a href="#2-为什么需要中介者模式？" class="headerlink" title="2. 为什么需要中介者模式？"></a>2. 为什么需要中介者模式？</h1><p>一个复杂的软件系统，若每个对象都与其他许多对象直接关联，则它们之间产生了紧密的耦合。这种设计的后果是，对任何一个对象的修改都可能引发连锁反应，迫使其他相关对象也得跟着改动，这便是“牵一发而动全身”的困境。系统的维护成本因此急剧攀升。</p><p>更糟糕的是，完整的业务流程被切割得支离破碎，散落在各个对象的实现中。想要理解一个完整的协作过程，开发者不得不深入多个类，追踪复杂的调用链，这使得系统的整体行为变得难以把握，也让新功能的添加或问题排查变得困难。同时，由于每个对象都和它的“同事们”绑得太死，想把它单独拿出来在别处复用也几乎不可能。</p><p>中介者模式正是为了解开这个“死结”而提出的。它引入了一个中心协调者——“中介者”，所有对象不再互相直接通信，而是统一与中介者对话。中介者负责接收消息，并根据既定规则将其分发给正确的对象。这样，原本混乱的网状结构被重构为清晰的“星型结构”，所有复杂的交互逻辑都集中到了中介者内部。通过这种方式，对象之间得以解耦，系统也因此变得更加清晰、易于维护和扩展。</p><h1 id="3-中介者模式的实现（go）"><a href="#3-中介者模式的实现（go）" class="headerlink" title="3. 中介者模式的实现（go）"></a>3. 中介者模式的实现（go）</h1><p>下面用一个简化的聊天室演示中介者如何协调用户之间的消息传递。</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line">    <span class="string">&quot;fmt&quot;</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Mediator <span class="keyword">interface</span> &#123;</span><br><span class="line">    Register(c Colleague)</span><br><span class="line">    Send(from <span class="type">string</span>, to <span class="type">string</span>, msg <span class="type">string</span>)</span><br><span class="line">    Broadcast(from <span class="type">string</span>, msg <span class="type">string</span>)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Colleague <span class="keyword">interface</span> &#123;</span><br><span class="line">    Name() <span class="type">string</span></span><br><span class="line">    Receive(from <span class="type">string</span>, msg <span class="type">string</span>)</span><br><span class="line">    Send(to <span class="type">string</span>, msg <span class="type">string</span>)</span><br><span class="line">    Broadcast(msg <span class="type">string</span>)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> ChatRoom <span class="keyword">struct</span> &#123;</span><br><span class="line">    members <span class="keyword">map</span>[<span class="type">string</span>]Colleague</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">NewChatRoom</span><span class="params">()</span></span> *ChatRoom &#123;</span><br><span class="line">    <span class="keyword">return</span> &amp;ChatRoom&#123;members: <span class="built_in">make</span>(<span class="keyword">map</span>[<span class="type">string</span>]Colleague)&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(r *ChatRoom)</span></span> Register(c Colleague) &#123;</span><br><span class="line">    r.members[c.Name()] = c</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(r *ChatRoom)</span></span> Send(from <span class="type">string</span>, to <span class="type">string</span>, msg <span class="type">string</span>) &#123;</span><br><span class="line">    <span class="keyword">if</span> m, ok := r.members[to]; ok &#123;</span><br><span class="line">        m.Receive(from, msg)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(r *ChatRoom)</span></span> Broadcast(from <span class="type">string</span>, msg <span class="type">string</span>) &#123;</span><br><span class="line">    <span class="keyword">for</span> name, m := <span class="keyword">range</span> r.members &#123;</span><br><span class="line">        <span class="keyword">if</span> name == from &#123;</span><br><span class="line">            <span class="keyword">continue</span></span><br><span class="line">        &#125;</span><br><span class="line">        m.Receive(from, msg)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> User <span class="keyword">struct</span> &#123;</span><br><span class="line">    name <span class="type">string</span></span><br><span class="line">    room *ChatRoom</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">NewUser</span><span class="params">(name <span class="type">string</span>, room *ChatRoom)</span></span> *User &#123;</span><br><span class="line">    <span class="keyword">return</span> &amp;User&#123;name: name, room: room&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(u *User)</span></span> Name() <span class="type">string</span> &#123; <span class="keyword">return</span> u.name &#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(u *User)</span></span> Receive(from <span class="type">string</span>, msg <span class="type">string</span>) &#123;</span><br><span class="line">    fmt.Printf(<span class="string">&quot;[%s] &lt;- %s: %s\n&quot;</span>, u.name, from, msg)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(u *User)</span></span> Send(to <span class="type">string</span>, msg <span class="type">string</span>) &#123;</span><br><span class="line">    u.room.Send(u.name, to, msg)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(u *User)</span></span> Broadcast(msg <span class="type">string</span>) &#123;</span><br><span class="line">    u.room.Broadcast(u.name, msg)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">    room := NewChatRoom()</span><br><span class="line"></span><br><span class="line">    alice := NewUser(<span class="string">&quot;alice&quot;</span>, room)</span><br><span class="line">    bob := NewUser(<span class="string">&quot;bob&quot;</span>, room)</span><br><span class="line">    eve := NewUser(<span class="string">&quot;eve&quot;</span>, room)</span><br><span class="line"></span><br><span class="line">    room.Register(alice)</span><br><span class="line">    room.Register(bob)</span><br><span class="line">    room.Register(eve)</span><br><span class="line"></span><br><span class="line">    alice.Send(<span class="string">&quot;bob&quot;</span>, <span class="string">&quot;Hi Bob&quot;</span>)</span><br><span class="line">    bob.Send(<span class="string">&quot;alice&quot;</span>, <span class="string">&quot;Hi Alice&quot;</span>)</span><br><span class="line">    eve.Broadcast(<span class="string">&quot;Hello everyone&quot;</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>要点：用户之间不直接互相引用，交互规则集中在 <code>ChatRoom</code>。当需要增加记录审计、敏感词过滤、分组路由时，仅需扩展中介者。</p><p>类图：</p><div class="mermaid-wrap"><pre class="mermaid-src" hidden>    classDiagramclass mediator {    &lt;&lt;interface&gt;&gt;    +Register(c colleague)    +Send(from,to,msg)    +Broadcast(from,msg)}class concreteMediator {    +Register(c colleague)    +Send(from,to,msg)    +Broadcast(from,msg)}class colleague {    &lt;&lt;interface&gt;&gt;    +Name() string    +Receive(from,msg)    +Send(to,msg)    +Broadcast(msg)}class concreteColleague {    +Name() string    +Receive(from,msg)    +Send(to,msg)    +Broadcast(msg)}mediator &lt;|.. concreteMediatorcolleague &lt;|.. concreteColleagueconcreteMediator --&gt; colleague : Register()concreteColleague --&gt; mediator : Send()&#x2F;Broadcast()concreteMediator --&gt; concreteColleague : Send()&#x2F;Broadcast()  </pre></div>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;1-什么是中介者模式？&quot;&gt;&lt;a href=&quot;#1-什么是中介者模式？&quot; class=&quot;headerlink&quot; title=&quot;1. 什么是中介者模式？&quot;&gt;&lt;/a&gt;1. 什么是中介者模式？&lt;/h1&gt;&lt;p&gt;中介者模式（Mediator Pattern）通过引入一个“中介</summary>
      
    
    
    
    <category term="Design Pattern" scheme="https://eliano64.github.io/categories/Design-Pattern/"/>
    
    
    <category term="Design Pattern" scheme="https://eliano64.github.io/tags/Design-Pattern/"/>
    
    <category term="go" scheme="https://eliano64.github.io/tags/go/"/>
    
    <category term="Mediator Pattern" scheme="https://eliano64.github.io/tags/Mediator-Pattern/"/>
    
  </entry>
  
  <entry>
    <title>251112 thinking binary constraint matching</title>
    <link href="https://eliano64.github.io/2025/11/12/251112-thinking-binary-constraint-matching/"/>
    <id>https://eliano64.github.io/2025/11/12/251112-thinking-binary-constraint-matching/</id>
    <published>2025-11-12T13:57:13.000Z</published>
    <updated>2025-11-14T01:55:45.562Z</updated>
    
    <content type="html"><![CDATA[<h1 id="1-抽象"><a href="#1-抽象" class="headerlink" title="1. 抽象"></a>1. 抽象</h1><p>受到力扣<a href="https://leetcode.cn/problems/two-sum/description/">1. 两数之和</a>以及类似问题的启发，抽象出如下模板：</p><p><strong>给定一个整数数组arr,令$x,y \in arr$,且$x,y$不能是同一个元素。若$f(x,y)&#x3D;K$，给定$f,K$，求所有满足条件的${x,y}$。</strong></p><p>伪代码如下</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">//input : arr, f, K</span><br><span class="line">//output : all pairs of x, y in arr such that f(x, y) = K</span><br><span class="line">//assumption : f(x,y)=k &lt;=&gt; g(x)=h(y), and g,h both are well-defined functions</span><br><span class="line">algorithm binary-constraint-matching(arr, K, f):</span><br><span class="line">    hash table := empty hash table, value -&gt; list</span><br><span class="line">    ans := empty set</span><br><span class="line">    for each x in arr; do</span><br><span class="line">      if h(x) in hash table; then</span><br><span class="line">          for j in hash[h(x)]; do</span><br><span class="line">            add (arr[j], x) to ans</span><br><span class="line">          end loop</span><br><span class="line">      end if</span><br><span class="line">      attempt to add index of x to hash table[g(x)]</span><br><span class="line">    end loop</span><br><span class="line">    return ans</span><br></pre></td></tr></table></figure><p>一个易错点在于：<strong>应该先尝试加入ans，再尝试加入h(x)到哈希表中。</strong>这个顺序是为了避免<code>ans</code>加入重复的元素对。</p><h1 id="2-例"><a href="#2-例" class="headerlink" title="2. 例"></a>2. 例</h1><p><a href="https://leetcode.cn/problems/two-sum/description/">两数之和</a></p><p><a href="https://leetcode.cn/problems/subarray-sums-divisible-by-k/description/">和可被 K 整除的子数组</a></p><p><a href="https://leetcode.cn/problems/number-of-sub-arrays-with-odd-sum/description/">和为奇数的子数组数目</a></p><p><a href="https://leetcode.cn/problems/count-the-number-of-beautiful-subarrays/description/">统计美丽子数组数目</a></p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;1-抽象&quot;&gt;&lt;a href=&quot;#1-抽象&quot; class=&quot;headerlink&quot; title=&quot;1. 抽象&quot;&gt;&lt;/a&gt;1. 抽象&lt;/h1&gt;&lt;p&gt;受到力扣&lt;a href=&quot;https://leetcode.cn/problems/two-sum/descriptio</summary>
      
    
    
    
    <category term="data structure &amp; algorithm" scheme="https://eliano64.github.io/categories/data-structure-algorithm/"/>
    
    
    <category term="data structure &amp; algorithm" scheme="https://eliano64.github.io/tags/data-structure-algorithm/"/>
    
    <category term="go" scheme="https://eliano64.github.io/tags/go/"/>
    
    <category term="hash table" scheme="https://eliano64.github.io/tags/hash-table/"/>
    
  </entry>
  
  <entry>
    <title>251108 thinking iterator</title>
    <link href="https://eliano64.github.io/2025/11/08/251108-thinking-iterator/"/>
    <id>https://eliano64.github.io/2025/11/08/251108-thinking-iterator/</id>
    <published>2025-11-08T08:27:05.000Z</published>
    <updated>2025-11-08T10:15:07.526Z</updated>
    
    <content type="html"><![CDATA[<h1 id="1-什么是迭代器模式"><a href="#1-什么是迭代器模式" class="headerlink" title="1. 什么是迭代器模式"></a>1. 什么是迭代器模式</h1><p>迭代器模式（Iterator Pattern）是一种行为设计模式，它提供一种方法来顺序访问一个聚合对象（Aggregate Object）中的各个元素，而又无需暴露该对象的内部表示。</p><p>无论操作的是一个数组、一个链表、一棵树还是一个哈希表，都可以用同样的客户端方式去遍历它（比如一个 <code>for-each</code> 循环），而不关心底层的具体的遍历方式是dfs还是bf或者其他。</p><p>它将遍历的逻辑从聚合对象中分离出来，放入一个单独的<strong>迭代器</strong>对象中。这样，容器只负责存储数据，而迭代器负责遍历数据。</p><h1 id="2-为什么需要迭代器模式？"><a href="#2-为什么需要迭代器模式？" class="headerlink" title="2. 为什么需要迭代器模式？"></a>2. 为什么需要迭代器模式？</h1><p>如果没有迭代器模式，客户端代码需要知道聚合对象的内部结构才能进行遍历。例如，遍历数组需要用索引，遍历链表需要用 <code>next</code> 指针。这会导致客户端与数据结构紧密耦合。</p><ul><li>客户端无需关心容器的内部实现（是 <code>slice</code>、<code>map</code> 还是 <code>linked list</code>），使得更换或修改容器实现变得容易，不影响客户端代码。</li><li>为不同的数据结构提供了统一的遍历方式，简化了客户端代码。</li><li>可以为同一个聚合对象提供多种不同的迭代器（如正向遍历、反向遍历）。</li><li>符合单一职责原则：聚合对象专注于数据存储，而迭代器专注于遍历，职责清晰。</li></ul><p>Go 语言的 <code>for...range</code> 循环就是迭代器模式的一个典型应用，它为数组、切片、字符串、map 和 channel 提供了统一的遍历语法。</p><h1 id="3-迭代器模式的实现-Go"><a href="#3-迭代器模式的实现-Go" class="headerlink" title="3. 迭代器模式的实现 (Go)"></a>3. 迭代器模式的实现 (Go)</h1><blockquote><p>对一个二叉树实现迭代器模式，要求迭代器能够按<strong>中序遍历</strong>二叉树。</p></blockquote><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Iterator 是泛型迭代器接口</span></span><br><span class="line"><span class="keyword">type</span> Iterator[E any] <span class="keyword">interface</span> &#123;</span><br><span class="line">IsEnd() <span class="type">bool</span></span><br><span class="line">GetCur() E</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Collection 是泛型聚合接口</span></span><br><span class="line"><span class="keyword">type</span> Collection[E any] <span class="keyword">interface</span> &#123;</span><br><span class="line">CreateIterator() Iterator[E]</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// TreeNode</span></span><br><span class="line"><span class="keyword">type</span> TreeNode[T any] <span class="keyword">struct</span> &#123;</span><br><span class="line">Val   T</span><br><span class="line">Left  *TreeNode[T]</span><br><span class="line">Right *TreeNode[T]</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// BinaryTree 是我们的具体聚合类</span></span><br><span class="line"><span class="keyword">type</span> BinaryTree[T any] <span class="keyword">struct</span> &#123;</span><br><span class="line">Root *TreeNode[T]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><a href="https://eliano64.github.io/2025/08/24/250824-thinking-%E4%BA%8C%E5%8F%89%E6%A0%91%E9%81%8D%E5%8E%86%E7%9A%84%E8%BF%AD%E4%BB%A3%E6%B3%95/#2.%20%E4%B8%AD%E5%BA%8F%E9%81%8D%E5%8E%86">InOrder</a></p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// InOrderIterator 是具体迭代器类</span></span><br><span class="line"><span class="keyword">type</span> InOrderIterator[T any] <span class="keyword">struct</span> &#123;</span><br><span class="line">stack []*TreeNode[T]</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// CreateIterator 是 BinaryTree 实现 Collection 接口的方法</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(bt *BinaryTree[T])</span></span> CreateIterator() Iterator[*TreeNode[T]] &#123;</span><br><span class="line">it := &amp;InOrderIterator[T]&#123;stack: <span class="built_in">make</span>([]*TreeNode[T], <span class="number">0</span>)&#125;</span><br><span class="line"><span class="comment">// 初始化时，将根节点及其所有左子节点压入栈</span></span><br><span class="line">it.pushLeft(bt.Root)</span><br><span class="line"><span class="keyword">return</span> it</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// pushLeft 辅助函数，将一个节点及其所有左子节点压入栈</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(it *InOrderIterator[T])</span></span> pushLeft(node *TreeNode[T]) &#123;</span><br><span class="line"><span class="keyword">for</span> node != <span class="literal">nil</span> &#123;</span><br><span class="line">it.stack = <span class="built_in">append</span>(it.stack, node)</span><br><span class="line">node = node.Left</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(it *InOrderIterator[T])</span></span> IsEnd() <span class="type">bool</span> &#123;</span><br><span class="line"><span class="keyword">return</span> <span class="built_in">len</span>(it.stack) &lt;= <span class="number">0</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// GetCur 返回当前迭代器指向的节点，且将迭代器指向下一个节点</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(it *InOrderIterator[T])</span></span> GetCur() *TreeNode[T] &#123;</span><br><span class="line">    <span class="keyword">if</span>(it.IsEnd())&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">nil</span></span><br><span class="line">    &#125;</span><br><span class="line"><span class="comment">// 从栈顶弹出一个节点</span></span><br><span class="line">n := <span class="built_in">len</span>(it.stack) - <span class="number">1</span></span><br><span class="line">node := it.stack[n]</span><br><span class="line">it.stack = it.stack[:n]</span><br><span class="line"></span><br><span class="line"><span class="comment">// 如果该节点有右子树，则将右子节点及其所有左子节点压栈</span></span><br><span class="line"><span class="keyword">if</span> node.Right != <span class="literal">nil</span> &#123;</span><br><span class="line">it.pushLeft(node.Right)</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">return</span> node</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line"><span class="comment">// 构造一棵二叉树</span></span><br><span class="line"><span class="comment">//      4</span></span><br><span class="line"><span class="comment">//     / \</span></span><br><span class="line"><span class="comment">//    2   5</span></span><br><span class="line"><span class="comment">//   / \</span></span><br><span class="line"><span class="comment">//  1   3</span></span><br><span class="line">root := &amp;TreeNode[<span class="type">int</span>]&#123;</span><br><span class="line">Val: <span class="number">4</span>,</span><br><span class="line">Left: &amp;TreeNode[<span class="type">int</span>]&#123;</span><br><span class="line">Val: <span class="number">2</span>,</span><br><span class="line">Left:  &amp;TreeNode[<span class="type">int</span>]&#123;Val: <span class="number">1</span>&#125;,</span><br><span class="line">Right: &amp;TreeNode[<span class="type">int</span>]&#123;Val: <span class="number">3</span>&#125;,</span><br><span class="line">&#125;,</span><br><span class="line">Right: &amp;TreeNode[<span class="type">int</span>]&#123;Val: <span class="number">5</span>&#125;,</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 泛型客户端通过接口使用</span></span><br><span class="line">c := &amp;BinaryTree[<span class="type">int</span>]&#123;Root: root&#125;</span><br><span class="line">it := c.CreateIterator() <span class="comment">// Iterator[*TreeNode[int]]</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> !it.IsEnd() &#123;</span><br><span class="line">fmt.Printf(<span class="string">&quot;%d &quot;</span>, it.GetCur().Val)</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 输出: 1 2 3 4 5</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>类图：</p><div class="mermaid-wrap"><pre class="mermaid-src" hidden>    classDiagram    class Iterator {        &lt;&lt;interface&gt;&gt;        +IsEnd() bool        +GetCur() E    }    class Collection {        &lt;&lt;interface&gt;&gt;        +CreateIterator() Iterator[E]    }    class TreeNode {        +Val T        +Left *TreeNode[T]        +Right *TreeNode[T]    }    class BinaryTree {        +Root *TreeNode[T]      }    class InOrderIterator {        -stack []*TreeNode[T]        -pushLeft(node *TreeNode[T])        +IsEnd() bool        +GetCur() *TreeNode[T]+    }    Iterator &lt;|.. InOrderIterator    Collection &lt;|.. BinaryTree    TreeNode &quot;1&quot; &lt;--* &quot;*&quot; BinaryTree : node    Collection ..&gt; Iterator : Creates  </pre></div>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;1-什么是迭代器模式&quot;&gt;&lt;a href=&quot;#1-什么是迭代器模式&quot; class=&quot;headerlink&quot; title=&quot;1. 什么是迭代器模式&quot;&gt;&lt;/a&gt;1. 什么是迭代器模式&lt;/h1&gt;&lt;p&gt;迭代器模式（Iterator Pattern）是一种行为设计模式，它提供</summary>
      
    
    
    
    <category term="Design Pattern" scheme="https://eliano64.github.io/categories/Design-Pattern/"/>
    
    
    <category term="Interface Oriented Programming" scheme="https://eliano64.github.io/tags/Interface-Oriented-Programming/"/>
    
    <category term="Design Pattern" scheme="https://eliano64.github.io/tags/Design-Pattern/"/>
    
    <category term="go" scheme="https://eliano64.github.io/tags/go/"/>
    
    <category term="Iterator Pattern" scheme="https://eliano64.github.io/tags/Iterator-Pattern/"/>
    
  </entry>
  
  <entry>
    <title>251108 thinking command</title>
    <link href="https://eliano64.github.io/2025/11/08/251108-thinking-command/"/>
    <id>https://eliano64.github.io/2025/11/08/251108-thinking-command/</id>
    <published>2025-11-08T07:59:34.000Z</published>
    <updated>2025-11-08T08:25:47.478Z</updated>
    
    <content type="html"><![CDATA[<h1 id="1-什么是命令模式"><a href="#1-什么是命令模式" class="headerlink" title="1. 什么是命令模式"></a>1. 什么是命令模式</h1><p>命令模式是一种行为设计模式，它将一个请求封装为一个对象，从而使你可用不同的请求对客户进行参数化。简单来说，就是将<strong>请求的发送者</strong>和<strong>请求的具体操作</strong>完全解耦。</p><h1 id="2-为什么需要命令模式？"><a href="#2-为什么需要命令模式？" class="headerlink" title="2. 为什么需要命令模式？"></a>2. 为什么需要命令模式？</h1><p>命令模式的核心价值在于<strong>解耦</strong>。如果没有命令模式，调用者（比如一个按钮 <code>Button</code>）需要直接引用并调用一个具体的操作（比如一个文档 <code>Document</code> 的 <code>save()</code> 方法）。这会导致 <code>Button</code> 和 <code>Document</code> 紧密耦合，<code>Button</code> 无法被复用到其他操作上。</p><p>通过引入命令对象，调用者不再关心命令的接收者是谁，也不关心操作如何执行。它只知道“我需要执行这个命令”。</p><p>这样的话，调用者和接收者之间没有任何直接引用，各自可以独立变化。而且添加新的命令非常容易，只需创建新的具体命令类，而无需修改现有代码，符合开放&#x2F;封闭原则。</p><h1 id="3-命令模式的实现-Go"><a href="#3-命令模式的实现-Go" class="headerlink" title="3. 命令模式的实现 (Go)"></a>3. 命令模式的实现 (Go)</h1><p>中间件：</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Command 接口定义了一个执行操作的方法</span></span><br><span class="line"><span class="keyword">type</span> Command <span class="keyword">interface</span> &#123;</span><br><span class="line">    Execute(receiver *Receiver)</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// Receiver 是请求的接收者，它知道如何执行与请求相关的操作</span></span><br><span class="line"><span class="keyword">type</span> Receiver <span class="keyword">struct</span> &#123;</span><br><span class="line">    <span class="comment">//...</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(r *Receiver)</span></span> Action() &#123;</span><br><span class="line">    <span class="comment">// 执行具体的操作</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Command1 是一个具体的命令类，它实现了 Command 接口</span></span><br><span class="line"><span class="keyword">type</span> Command1 <span class="keyword">struct</span> &#123;</span><br><span class="line">    <span class="comment">//...</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(c *Command1)</span></span> Execute(r *Receiver) &#123;</span><br><span class="line">    r.Action()</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Invoker 是请求的发送者，它持有一个 Command 对象  </span></span><br><span class="line"><span class="keyword">type</span> Invoker <span class="keyword">struct</span> &#123;</span><br><span class="line">    command *Command</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(i *Invoker)</span></span> SetCommand(command *Command) &#123;</span><br><span class="line">    i.command = command</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ExecuteCommand 是 Invoker 用来间接执行命令的方法</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(i *Invoker)</span></span> ExecuteCommand(r *Receiver) &#123;</span><br><span class="line">    i.command.Execute(r)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>客户端</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">receiver := &amp;Receiver&#123;&#125;</span><br><span class="line">command := &amp;Command1&#123;&#125;</span><br><span class="line">invoker := &amp;Invoker&#123;&#125;</span><br><span class="line">invoker.SetCommand(command)</span><br><span class="line">invoker.ExecuteCommand(receiver)</span><br></pre></td></tr></table></figure><p>类图：</p><div class="mermaid-wrap"><pre class="mermaid-src" hidden>    classDiagram    class Invoker {        -command: *Command        +SetCommand(command: *Command)        +ExecuteCommand(receiver: *Receiver)    }    class Command {        &lt;&lt;interface&gt;&gt;        +Execute(receiver: *Receiver)    }    class Command1 {        +Execute(receiver: *Receiver)    }    class Receiver {        &#x2F;&#x2F;...        +Action()    }    Invoker &quot;*&quot; o--&gt; &quot;*&quot; Command: holds    Command1  ..&gt;  Receiver: executes    Command1 ..|&gt; Command : implements  </pre></div>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;1-什么是命令模式&quot;&gt;&lt;a href=&quot;#1-什么是命令模式&quot; class=&quot;headerlink&quot; title=&quot;1. 什么是命令模式&quot;&gt;&lt;/a&gt;1. 什么是命令模式&lt;/h1&gt;&lt;p&gt;命令模式是一种行为设计模式，它将一个请求封装为一个对象，从而使你可用不同的请求对</summary>
      
    
    
    
    <category term="Design Pattern" scheme="https://eliano64.github.io/categories/Design-Pattern/"/>
    
    
    <category term="Interface Oriented Programming" scheme="https://eliano64.github.io/tags/Interface-Oriented-Programming/"/>
    
    <category term="Design Pattern" scheme="https://eliano64.github.io/tags/Design-Pattern/"/>
    
    <category term="go" scheme="https://eliano64.github.io/tags/go/"/>
    
    <category term="Command Pattern" scheme="https://eliano64.github.io/tags/Command-Pattern/"/>
    
  </entry>
  
  <entry>
    <title>251108 thinking chain-of-responsibility</title>
    <link href="https://eliano64.github.io/2025/11/08/251108-thinking-chain-of-responsibility/"/>
    <id>https://eliano64.github.io/2025/11/08/251108-thinking-chain-of-responsibility/</id>
    <published>2025-11-08T07:30:06.000Z</published>
    <updated>2025-11-08T08:11:36.778Z</updated>
    
    <content type="html"><![CDATA[<h1 id="1-什么是责任链模式？"><a href="#1-什么是责任链模式？" class="headerlink" title="1. 什么是责任链模式？"></a>1. 什么是责任链模式？</h1><p>责任链模式（Chain of Responsibility Pattern）是一种行为型设计模式，它将请求的发送者和接收者解耦，将这些处理者连成一条链。链上的每个处理者都有一个成员变量来保存对于下一处理者的引用。除了处理请求外，处理者还负责沿着链传递请求。请求会在链上移动，直至所有处理者都有机会对其进行处理，或是中途退出。</p><h1 id="2-为什么需要责任链模式？"><a href="#2-为什么需要责任链模式？" class="headerlink" title="2. 为什么需要责任链模式？"></a>2. 为什么需要责任链模式？</h1><p>当处理一个请求、但处理步骤有多种、且依赖于不同的条件时，一种最直观的写法可能是一个巨大的 <code>if-else</code> 结构：</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">handle</span><span class="params">(request *Request)</span></span> &#123;</span><br><span class="line">    <span class="keyword">if</span> conditionA(request) &#123;</span><br><span class="line">        <span class="comment">// 处理 A</span></span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> conditionB(request) &#123;</span><br><span class="line">        <span class="comment">// 处理 B</span></span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> conditionC(request) &#123;</span><br><span class="line">        <span class="comment">// 处理 C</span></span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">// 默认处理</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这种代码的问题显而易见，每当需要增加一种新的处理方式，都必须修改 <code>handle</code> 函数，风险很高。</p><p>责任链模式就是为了解决这些问题而生的。它将这些处理逻辑拆分成独立的<strong>处理者</strong>对象，并将它们链接成一条<strong>链</strong>。当请求到来时，它会沿着链传递，直到处理流程结束为止。</p><p><strong>核心收益：</strong></p><ol><li><strong>发送者与接收者解耦</strong>：发送者只需要知道链的第一个处理者，而不需要关心请求最终由谁处理，也不需要知道链的结构。</li><li><strong>灵活性高</strong>：你可以在客户端中随时增加、删除或重新排列链中的处理者，而无需改动现有处理者的代码。</li><li><strong>符合单一职责原则</strong>：每个处理者都只关心自己的处理逻辑，代码更清晰、更易于维护。</li></ol><p>每个<code>Handler</code>之间的调用关系有点类似于组合模式，但是责任链模式是<strong>链式调用</strong>，而组合模式是<strong>树形结构</strong>。</p><h1 id="3-责任链模式的实现-Go"><a href="#3-责任链模式的实现-Go" class="headerlink" title="3. 责任链模式的实现 (Go)"></a>3. 责任链模式的实现 (Go)</h1><p>首先需要定义<code>handler</code>接口，它包含一个方法<code>Execute</code>，用于处理请求，以及一个方法<code>SetNext</code>，用于设置下一个处理者。</p><p>以及具体的处理者实现：</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 处理者接口</span></span><br><span class="line"><span class="keyword">type</span> Handler <span class="keyword">interface</span> &#123;</span><br><span class="line">    Execute(p *client)</span><br><span class="line">    SetNext(next *Handler)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 具体的处理者实现</span></span><br><span class="line"><span class="keyword">type</span> Handler1 <span class="keyword">struct</span> &#123;</span><br><span class="line">    next *Handler</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 具体的处理者实现</span></span><br><span class="line"><span class="keyword">type</span> Handler2 <span class="keyword">struct</span> &#123;</span><br><span class="line">    next *Handler</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(h *Handler1)</span></span> Execute(p *client) &#123;</span><br><span class="line">    <span class="comment">//...Handle 1处理逻辑，然后调用下一个处理者</span></span><br><span class="line">    <span class="keyword">if</span> h.next != <span class="literal">nil</span> &#123;</span><br><span class="line">        h.next.Execute(p)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(h *Handler1)</span></span> SetNext(next *Handler) &#123;</span><br><span class="line">    h.next = next</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(h *Handler2)</span></span> Execute(p *client) &#123;</span><br><span class="line">    <span class="comment">//...Handle 2处理逻辑，然后调用下一个处理者</span></span><br><span class="line">    <span class="keyword">if</span> h.next != <span class="literal">nil</span> &#123;</span><br><span class="line">        h.next.Execute(p)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(h *Handler2)</span></span> SetNext(next *Handler) &#123;</span><br><span class="line">    h.next = next</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>若客户端处理流程为：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">client -&gt; Handler1 -&gt; Handler2-&gt;over</span><br></pre></td></tr></table></figure><p>则</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">h1 := &amp;Handler1&#123;&#125;</span><br><span class="line">h2 := &amp;Handler2&#123;&#125;</span><br><span class="line">h1.SetNext(h2)</span><br><span class="line">c := &amp;client&#123;&#125;</span><br><span class="line">h1.Execute(c)</span><br></pre></td></tr></table></figure><p>类图:</p><div class="mermaid-wrap"><pre class="mermaid-src" hidden>    classDiagram    class Handler {        &lt;&lt;interface&gt;&gt;        +Execute(p *client)        +SetNext(next *Handler)    }    class Handler1 {        -next: *Handler        +Execute(p *client)        +SetNext(next *Handler)    }    class Handler2 {        -next: *Handler        +Execute(p *client)        +SetNext(next *Handler)    }    class client {        &#x2F;&#x2F;...    }    Handler &lt;|.. Handler1    Handler &lt;|.. Handler2    Handler1 &quot;1&quot; o--&gt; &quot;1&quot; Handler2 : next    client &quot;1&quot; &lt;.. &quot;1&quot; Handler1 : Execute  </pre></div>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;1-什么是责任链模式？&quot;&gt;&lt;a href=&quot;#1-什么是责任链模式？&quot; class=&quot;headerlink&quot; title=&quot;1. 什么是责任链模式？&quot;&gt;&lt;/a&gt;1. 什么是责任链模式？&lt;/h1&gt;&lt;p&gt;责任链模式（Chain of Responsibility Pa</summary>
      
    
    
    
    <category term="Design Pattern" scheme="https://eliano64.github.io/categories/Design-Pattern/"/>
    
    
    <category term="Interface Oriented Programming" scheme="https://eliano64.github.io/tags/Interface-Oriented-Programming/"/>
    
    <category term="Design Pattern" scheme="https://eliano64.github.io/tags/Design-Pattern/"/>
    
    <category term="go" scheme="https://eliano64.github.io/tags/go/"/>
    
    <category term="Chain of Responsibility Pattern" scheme="https://eliano64.github.io/tags/Chain-of-Responsibility-Pattern/"/>
    
  </entry>
  
  <entry>
    <title>251101 record closest-equal-element-queries</title>
    <link href="https://eliano64.github.io/2025/11/01/251101-record-closest-equal-element-queries/"/>
    <id>https://eliano64.github.io/2025/11/01/251101-record-closest-equal-element-queries/</id>
    <published>2025-11-01T06:57:02.000Z</published>
    <updated>2025-11-01T07:06:00.966Z</updated>
    
    <content type="html"><![CDATA[<p><a href="https://leetcode.cn/problems/closest-equal-element-queries">题目</a></p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">vector&lt;<span class="type">int</span>&gt; <span class="title">solveQueries</span><span class="params">(vector&lt;<span class="type">int</span>&gt;&amp; nums, vector&lt;<span class="type">int</span>&gt;&amp; queries)</span> </span>&#123;</span><br><span class="line">        map&lt;<span class="type">int</span>,vector&lt;<span class="type">int</span>&gt;&gt;mp;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>;i&lt;nums.<span class="built_in">size</span>();++i)&#123;</span><br><span class="line">            mp[nums[i]].<span class="built_in">push_back</span>(i);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">int</span> n = queries.<span class="built_in">size</span>();</span><br><span class="line">        vector&lt;<span class="type">int</span>&gt;<span class="built_in">ans</span>(n,<span class="number">-1</span>);</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> j=<span class="number">0</span>;j&lt;n;++j)&#123;</span><br><span class="line">            <span class="type">int</span> q = queries[j];</span><br><span class="line">            <span class="keyword">auto</span> &amp; vec = mp[nums[q]];</span><br><span class="line">            <span class="keyword">if</span>(vec.<span class="built_in">size</span>()==<span class="number">1</span>)&#123;</span><br><span class="line">                <span class="keyword">continue</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="type">int</span> idx = ranges::<span class="built_in">lower_bound</span>(vec,q)-vec.<span class="built_in">begin</span>();</span><br><span class="line">            </span><br><span class="line">            ans[j] = <span class="built_in">min</span>((vec[(idx<span class="number">+1</span>)%vec.<span class="built_in">size</span>()]-vec[idx]+nums.<span class="built_in">size</span>())%nums.<span class="built_in">size</span>(),(vec[idx] - vec[(idx<span class="number">-1</span>+vec.<span class="built_in">size</span>())%vec.<span class="built_in">size</span>()]+nums.<span class="built_in">size</span>())%nums.<span class="built_in">size</span>());</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> ans;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>跟它类似的还有比如<a href="https://leetcode.cn/problems/time-based-key-value-store/">981. 基于时间的键值存储</a></p><p>这类题型的本质是：<strong>对于某个属性相同的元素，需要在它们的位置序列中进行高效查找。</strong></p><p>流程：</p><div class="mermaid-wrap"><pre class="mermaid-src" hidden>    flowchart TD    A[开始：读取输入数据] --&gt; B[建立值-vector映射表]    B --&gt; C{遍历原数组&#x2F;序列}    C --&gt; D[将每个元素的值作为key&lt;br&#x2F;&gt;将其序号加入对应的vector]    D --&gt; E{是否遍历完成?}    E --&gt;|否| C    E --&gt;|是| F[处理查询]        F --&gt; G{遍历每个查询}    G --&gt; H[获取查询的目标值]    H --&gt; I{映射表中是否存在该值?}    I --&gt;|否| J[返回-1或默认值]    I --&gt;|是| K[获取该值对应的位置序列]    K --&gt; L[在位置序列上进行二分查找]    L --&gt; M[使用lower_bound&#x2F;upper_bound&lt;br&#x2F;&gt;找到合适的位置]    M --&gt; N[检查前后相邻位置]    N --&gt; O[计算距离&#x2F;时间差&lt;br&#x2F;&gt;选择最优解]    O --&gt; P[记录查询结果]    P --&gt; Q{是否处理完所有查询?}    Q --&gt;|否| G    Q --&gt;|是| R[返回所有查询结果]        J --&gt; Q        style A fill:#e1f5fe    style B fill:#f3e5f5    style L fill:#fff3e0    style R fill:#e8f5e8        classDef keyStep fill:#ffeb3b,stroke:#f57f17,stroke-width:2px    class B,L keyStep  </pre></div>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;&lt;a href=&quot;https://leetcode.cn/problems/closest-equal-element-queries&quot;&gt;题目&lt;/a&gt;&lt;/p&gt;
&lt;figure class=&quot;highlight c++&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutte</summary>
      
    
    
    
    <category term="record" scheme="https://eliano64.github.io/categories/record/"/>
    
    
    <category term="C++" scheme="https://eliano64.github.io/tags/C/"/>
    
    <category term="Record" scheme="https://eliano64.github.io/tags/Record/"/>
    
    <category term="leetcode" scheme="https://eliano64.github.io/tags/leetcode/"/>
    
    <category term="binary-search" scheme="https://eliano64.github.io/tags/binary-search/"/>
    
  </entry>
  
  <entry>
    <title>251031 thinking proxy</title>
    <link href="https://eliano64.github.io/2025/10/31/251031-thinking-proxy/"/>
    <id>https://eliano64.github.io/2025/10/31/251031-thinking-proxy/</id>
    <published>2025-10-31T08:56:39.000Z</published>
    <updated>2025-11-01T01:22:27.982Z</updated>
    
    <content type="html"><![CDATA[<h1 id="1-什么是代理模式"><a href="#1-什么是代理模式" class="headerlink" title="1. 什么是代理模式"></a>1. 什么是代理模式</h1><p>代理模式是一种结构型设计模式，它通过引入一个代理对象来控制对另一个对象的访问。代理对象可以在客户端和目标对象之间添加额外的功能，如延迟加载、访问控制、日志记录等。</p><h1 id="2-为什么需要代理模式"><a href="#2-为什么需要代理模式" class="headerlink" title="2. 为什么需要代理模式"></a>2. 为什么需要代理模式</h1><p>代理模式的核心价值在于<strong>在不改变原始对象（目标对象）代码的前提下，增加额外的控制和功能</strong>。它像一个中介，隔离了客户端和真实对象，并在中间“做手脚”。</p><h1 id="3-代理模式的实现-Go"><a href="#3-代理模式的实现-Go" class="headerlink" title="3. 代理模式的实现 (Go)"></a>3. 代理模式的实现 (Go)</h1><p>我们用 Go 实现一个<strong>保护代理</strong>的例子。假设我们有一个 Web 服务器，希望通过一个 Nginx 代理来限制用户的访问速率。</p><ol><li>客户端、代理和真实服务都应遵循同一个接口，这样代理才能“伪装”成真实服务。</li></ol><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Server 是代理和真实服务器的通用接口</span></span><br><span class="line"><span class="keyword">type</span> Server <span class="keyword">interface</span> &#123;</span><br><span class="line">HandleRequest(url, method <span class="type">string</span>) (<span class="type">int</span>, <span class="type">string</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol start="2"><li>创建真实服务 (RealSubject)</li></ol><p>这是我们真正要保护的后端应用。</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Application 是我们的后端真实服务</span></span><br><span class="line"><span class="keyword">type</span> Application <span class="keyword">struct</span>&#123;&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(a *Application)</span></span> HandleRequest(url, method <span class="type">string</span>) (<span class="type">int</span>, <span class="type">string</span>) &#123;</span><br><span class="line"><span class="keyword">if</span> url == <span class="string">&quot;/app/status&quot;</span> &amp;&amp; method == <span class="string">&quot;GET&quot;</span> &#123;</span><br><span class="line"><span class="keyword">return</span> <span class="number">200</span>, <span class="string">&quot;Ok&quot;</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">if</span> url == <span class="string">&quot;/create/user&quot;</span> &amp;&amp; method == <span class="string">&quot;POST&quot;</span> &#123;</span><br><span class="line"><span class="keyword">return</span> <span class="number">201</span>, <span class="string">&quot;User Created&quot;</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">return</span> <span class="number">404</span>, <span class="string">&quot;Not Found&quot;</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol start="3"><li>创建代理 (Proxy)</li></ol><p>Nginx 代理持有对真实服务的引用，并在处理请求前增加了速率限制的逻辑。</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> <span class="string">&quot;fmt&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// Nginx 是一个代理，它控制对 Application 的访问</span></span><br><span class="line"><span class="keyword">type</span> Nginx <span class="keyword">struct</span> &#123;</span><br><span class="line">application      *Application</span><br><span class="line">maxAllowedReq    <span class="type">int</span></span><br><span class="line">rateLimiter      <span class="keyword">map</span>[<span class="type">string</span>]<span class="type">int</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">NewNginxServer</span><span class="params">()</span></span> *Nginx &#123;</span><br><span class="line"><span class="keyword">return</span> &amp;Nginx&#123;</span><br><span class="line">application:      &amp;Application&#123;&#125;,</span><br><span class="line">maxAllowedReq:    <span class="number">2</span>,</span><br><span class="line">rateLimiter:      <span class="built_in">make</span>(<span class="keyword">map</span>[<span class="type">string</span>]<span class="type">int</span>),</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// checkRateLimit 检查并更新请求速率</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(n *Nginx)</span></span> checkRateLimit(url <span class="type">string</span>) <span class="type">bool</span> &#123;</span><br><span class="line">n.rateLimiter[url]++</span><br><span class="line"><span class="keyword">if</span> n.rateLimiter[url] &gt; n.maxAllowedReq &#123;</span><br><span class="line"><span class="keyword">return</span> <span class="literal">false</span> <span class="comment">// 超出速率限制</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">return</span> <span class="literal">true</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// HandleRequest 代理的核心方法</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(n *Nginx)</span></span> HandleRequest(url, method <span class="type">string</span>) (<span class="type">int</span>, <span class="type">string</span>) &#123;</span><br><span class="line"><span class="comment">// 在转发请求前，执行额外的逻辑（访问控制）</span></span><br><span class="line"><span class="keyword">if</span> !n.checkRateLimit(url) &#123;</span><br><span class="line"><span class="keyword">return</span> <span class="number">429</span>, <span class="string">&quot;Too Many Requests&quot;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 将请求转发给真实服务</span></span><br><span class="line"><span class="keyword">return</span> n.application.HandleRequest(url, method)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol start="4"><li>客户端调用</li></ol><p>客户端只与代理（Nginx）交互，并不知道真实服务（Application）的存在。</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">nginx := NewNginxServer()</span><br><span class="line"></span><br><span class="line"><span class="comment">// 第一次请求 /app/status</span></span><br><span class="line">code, body := nginx.HandleRequest(<span class="string">&quot;/app/status&quot;</span>, <span class="string">&quot;GET&quot;</span>)</span><br><span class="line">fmt.Printf(<span class="string">&quot;Url: /app/status, Code: %d, Body: %s\n&quot;</span>, code, body)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 第二次请求 /app/status</span></span><br><span class="line">code, body = nginx.HandleRequest(<span class="string">&quot;/app/status&quot;</span>, <span class="string">&quot;GET&quot;</span>)</span><br><span class="line">fmt.Printf(<span class="string">&quot;Url: /app/status, Code: %d, Body: %s\n&quot;</span>, code, body)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 第三次请求 /app/status，将被限流</span></span><br><span class="line">code, body = nginx.HandleRequest(<span class="string">&quot;/app/status&quot;</span>, <span class="string">&quot;GET&quot;</span>)</span><br><span class="line">fmt.Printf(<span class="string">&quot;Url: /app/status, Code: %d, Body: %s\n&quot;</span>, code, body)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>类图</p><div class="mermaid-wrap"><pre class="mermaid-src" hidden>    classDiagram    class Client    class Server {        &lt;&lt;interface&gt;&gt;        +HandleRequest(url, method)    }    class Application {        +HandleRequest(url, method)    }    class Nginx {        -application: *Application        -maxAllowedReq: int        -rateLimiter: map        +HandleRequest(url, method)        -checkRateLimit(url): bool    }    Client --&gt; Server : uses    Server &lt;|.. Application : implements    Server &lt;|.. Nginx : implements    Nginx o-- Application : aggregates  </pre></div>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;1-什么是代理模式&quot;&gt;&lt;a href=&quot;#1-什么是代理模式&quot; class=&quot;headerlink&quot; title=&quot;1. 什么是代理模式&quot;&gt;&lt;/a&gt;1. 什么是代理模式&lt;/h1&gt;&lt;p&gt;代理模式是一种结构型设计模式，它通过引入一个代理对象来控制对另一个对象的访问。代</summary>
      
    
    
    
    <category term="Design Pattern" scheme="https://eliano64.github.io/categories/Design-Pattern/"/>
    
    
    <category term="Design Pattern" scheme="https://eliano64.github.io/tags/Design-Pattern/"/>
    
    <category term="go" scheme="https://eliano64.github.io/tags/go/"/>
    
    <category term="Proxy Pattern" scheme="https://eliano64.github.io/tags/Proxy-Pattern/"/>
    
  </entry>
  
  <entry>
    <title>251031 thinking flyweight</title>
    <link href="https://eliano64.github.io/2025/10/31/251031-thinking-flyweight/"/>
    <id>https://eliano64.github.io/2025/10/31/251031-thinking-flyweight/</id>
    <published>2025-10-31T08:04:09.000Z</published>
    <updated>2025-11-01T01:38:47.486Z</updated>
    
    <content type="html"><![CDATA[<h1 id="1-什么是享元模式"><a href="#1-什么是享元模式" class="headerlink" title="1. 什么是享元模式"></a>1. 什么是享元模式</h1><p>享元模式是一种结构型设计模式，它通过共享相同状态的对象来减少内存占用和提高性能。</p><h1 id="2-为什么需要享元模式"><a href="#2-为什么需要享元模式" class="headerlink" title="2. 为什么需要享元模式"></a>2. 为什么需要享元模式</h1><p>我们用一个“在地图上种树”的例子来理解。</p><h2 id="2-1-问题：内存爆炸"><a href="#2-1-问题：内存爆炸" class="headerlink" title="2.1. 问题：内存爆炸"></a>2.1. 问题：内存爆炸</h2><p>假设要在地图上种 100 万棵树。每棵树都有：</p><ul><li><strong>坐标</strong>：每棵树都不同。</li><li><strong>类型信息</strong>：如名称（松树）、颜色（绿色）、纹理图片等。类型是有限的，比如只有几种。</li></ul><p>如果为每棵树都创建一个包含所有信息的对象，<code>Texture</code>（纹理图片）这份可能很大的数据会被重复存储 100 万次，导致内存爆炸。</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 传统方式：每个对象都包含所有数据</span></span><br><span class="line"><span class="keyword">type</span> Tree <span class="keyword">struct</span> &#123;</span><br><span class="line">X, Y      <span class="type">int</span></span><br><span class="line">Name      <span class="type">string</span></span><br><span class="line">Color     <span class="type">string</span></span><br><span class="line">Texture   []<span class="type">byte</span> <span class="comment">// 假设这份数据很大</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><em>该如何解决这个问题？</em></p><h2 id="2-2-方案：享元模式"><a href="#2-2-方案：享元模式" class="headerlink" title="2.2. 方案：享元模式"></a>2.2. 方案：享元模式</h2><p>享元模式的核心是<strong>分离并共享相似对象中的共同部分</strong>，以减少内存占用。</p><p>我们将对象的状态分为两种：</p><ul><li>**内部状态 (Intrinsic State)**：可共享、不随环境改变的部分。在例子中是 <code>Name</code>, <code>Color</code>, <code>Texture</code>。</li><li>**外部状态 (Extrinsic State)**：不可共享、随环境改变的部分。在例子中是 <code>X</code>, <code>Y</code> 坐标。</li></ul><p>实现步骤是创建<strong>享元对象</strong>（内部状态），通过<strong>享元工厂</strong>来复用它，并让<strong>上下文对象</strong>（外部状态）持有对享元的引用。</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 1. 享元对象：存储共享的【内部状态】</span></span><br><span class="line"><span class="keyword">type</span> treeType <span class="keyword">struct</span> &#123;</span><br><span class="line">Name    <span class="type">string</span></span><br><span class="line">Color   <span class="type">string</span></span><br><span class="line">Texture []<span class="type">byte</span> <span class="comment">// 这份数据被共享</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 2. 享元工厂：确保相同类型的享元只创建一次</span></span><br><span class="line"><span class="keyword">type</span> treeTypeFactory <span class="keyword">struct</span> &#123;</span><br><span class="line">types <span class="keyword">map</span>[<span class="type">string</span>]*treeType</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(f *treeTypeFactory)</span></span> GetTreeType(name, color <span class="type">string</span>, texture []<span class="type">byte</span>) *treeType &#123;</span><br><span class="line">key := name + <span class="string">&quot;_&quot;</span> + color</span><br><span class="line"><span class="keyword">if</span> t, ok := f.types[key]; ok &#123;</span><br><span class="line"><span class="keyword">return</span> t <span class="comment">// 已存在，直接返回</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 不存在，创建新的并存起来</span></span><br><span class="line">newType := &amp;treeType&#123;name, color, texture&#125;</span><br><span class="line">f.types[key] = newType</span><br><span class="line"><span class="keyword">return</span> newType</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 3. 上下文对象：存储【外部状态】和对享元的引用</span></span><br><span class="line"><span class="keyword">type</span> Tree <span class="keyword">struct</span> &#123;</span><br><span class="line">X, Y <span class="type">int</span></span><br><span class="line">Type *treeType <span class="comment">// 指向共享的享元对象</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Forest <span class="keyword">struct</span> &#123;</span><br><span class="line">trees   []*Tree</span><br><span class="line">factory *treeTypeFactory</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(f *Forest)</span></span> SetFactory() &#123;</span><br><span class="line">f.factory = &amp;treeTypeFactory&#123;</span><br><span class="line">types: <span class="built_in">make</span>(<span class="keyword">map</span>[<span class="type">string</span>]*treeType),</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(f *Forest)</span></span> PlantTree(x, y <span class="type">int</span>, name, color <span class="type">string</span>, texture []<span class="type">byte</span>) &#123;</span><br><span class="line"><span class="comment">// 从工厂获取共享的 treeType</span></span><br><span class="line">treeType := f.factory.GetTreeType(name, color, texture)</span><br><span class="line"><span class="comment">// 创建 Tree，只存储外部状态和引用</span></span><br><span class="line">tree := &amp;Tree&#123;X: x, Y: y, Type: treeType&#125;</span><br><span class="line">f.trees = <span class="built_in">append</span>(f.trees, tree)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(f *Forest)</span></span> GetTrees() []*Tree &#123;</span><br><span class="line"><span class="keyword">return</span> f.trees</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>通过这种方式，<code>TreeType</code> 对象（比如“松树”）只会在内存中存在一份，所有松树实例都共享它。内存占用问题迎刃而解。</p><blockquote><ul><li><em>对于某个类 A 的一些在不同实例中基本相同的属性，我们把它们单独拿出来形成一个类 B，并由一个<strong>缓存池（享元工厂）</strong>来管理。</em></li><li><em>新建 A 时，先在缓存池中查找是否已有相同的 B 实例，如果有就复用它，否则创建新的并放入池中。</em></li><li><em>最终表现为：多个 A 实例共享同一个 B 对象。</em></li></ul></blockquote><h1 id="3-享元模式的实现（go）"><a href="#3-享元模式的实现（go）" class="headerlink" title="3. 享元模式的实现（go）"></a>3. 享元模式的实现（go）</h1><p>如上。</p><p>类图：</p><div class="mermaid-wrap"><pre class="mermaid-src" hidden>    classDiagram    class Forest {        -trees: []*Tree        -factory: *treeTypeFactory        +SetFactory()        +PlantTree(x, y, name, color, texture)        +GetTrees(): []*Tree    }    class treeTypeFactory {        -types: map[string]*treeType        +GetTreeType(name, color, texture): *treeType    }    class Tree {        +X: int        +Y: int        +Type: *treeType    }    class treeType {        &lt;&lt;Flyweight&gt;&gt;        +Name: string        +Color: string        +Texture: []byte    }    Forest &quot;1&quot; o--&gt; &quot;1&quot; treeTypeFactory : uses    Forest &quot;1&quot; o--&gt; &quot;0..*&quot; Tree : contains    Tree &quot;1&quot; o-- &quot;1&quot; treeType : references    treeTypeFactory &quot;1&quot; ..&gt; &quot;0..*&quot; treeType : creates &amp; manages  </pre></div>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;1-什么是享元模式&quot;&gt;&lt;a href=&quot;#1-什么是享元模式&quot; class=&quot;headerlink&quot; title=&quot;1. 什么是享元模式&quot;&gt;&lt;/a&gt;1. 什么是享元模式&lt;/h1&gt;&lt;p&gt;享元模式是一种结构型设计模式，它通过共享相同状态的对象来减少内存占用和提高性能。</summary>
      
    
    
    
    <category term="Design Pattern" scheme="https://eliano64.github.io/categories/Design-Pattern/"/>
    
    
    <category term="Design Pattern" scheme="https://eliano64.github.io/tags/Design-Pattern/"/>
    
    <category term="go" scheme="https://eliano64.github.io/tags/go/"/>
    
    <category term="Flyweight Pattern" scheme="https://eliano64.github.io/tags/Flyweight-Pattern/"/>
    
  </entry>
  
  <entry>
    <title>251025 thinking facade</title>
    <link href="https://eliano64.github.io/2025/10/25/251025-thinking-facade/"/>
    <id>https://eliano64.github.io/2025/10/25/251025-thinking-facade/</id>
    <published>2025-10-25T09:11:36.000Z</published>
    <updated>2025-10-31T08:52:32.190Z</updated>
    
    <content type="html"><![CDATA[<h1 id="1-什么是外观模式"><a href="#1-什么是外观模式" class="headerlink" title="1. 什么是外观模式"></a>1. 什么是外观模式</h1><p>外观模式是一种结构型设计模式， 能为程序库、 框架或其他复杂类提供一个简单的接口。</p><h1 id="2-为什么需要外观模式"><a href="#2-为什么需要外观模式" class="headerlink" title="2. 为什么需要外观模式"></a>2. 为什么需要外观模式</h1><p>外观模式的主要目的是简化复杂系统的使用。它通过提供一个简单的接口，隐藏系统的复杂性，使客户端代码更易于理解和使用。</p><h1 id="3-外观模式的实现（go）"><a href="#3-外观模式的实现（go）" class="headerlink" title="3. 外观模式的实现（go）"></a>3. 外观模式的实现（go）</h1><p>以计算机开机为例，展示外观模式的实现。</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 子系统：CPU</span></span><br><span class="line"><span class="keyword">type</span> CPU <span class="keyword">struct</span>&#123;&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(c *CPU)</span></span> Start()    &#123; fmt.Println(<span class="string">&quot;CPU: start&quot;</span>) &#125;</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(c *CPU)</span></span> Shutdown() &#123; fmt.Println(<span class="string">&quot;CPU: shutdown&quot;</span>) &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 子系统：内存</span></span><br><span class="line"><span class="keyword">type</span> Memory <span class="keyword">struct</span>&#123;&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(m *Memory)</span></span> LoadOS() &#123; fmt.Println(<span class="string">&quot;Memory: load OS into memory&quot;</span>) &#125;</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(m *Memory)</span></span> Clear()  &#123; fmt.Println(<span class="string">&quot;Memory: clear&quot;</span>) &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 子系统：磁盘</span></span><br><span class="line"><span class="keyword">type</span> Disk <span class="keyword">struct</span>&#123;&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(d *Disk)</span></span> ReadBootSector() &#123; fmt.Println(<span class="string">&quot;Disk: read boot sector&quot;</span>) &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 外观：计算机</span></span><br><span class="line"><span class="keyword">type</span> ComputerFacade <span class="keyword">struct</span> &#123;</span><br><span class="line">cpu  *CPU</span><br><span class="line">mem  *Memory</span><br><span class="line">disk *Disk</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">NewComputerFacade</span><span class="params">()</span></span> *ComputerFacade &#123;</span><br><span class="line"><span class="keyword">return</span> &amp;ComputerFacade&#123;</span><br><span class="line">cpu:  &amp;CPU&#123;&#125;,</span><br><span class="line">mem:  &amp;Memory&#123;&#125;,</span><br><span class="line">disk: &amp;Disk&#123;&#125;,</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 对外暴露的简化接口：开机流程</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(c *ComputerFacade)</span></span> PowerOn() &#123;</span><br><span class="line">fmt.Println(<span class="string">&quot;== PowerOn ==&quot;</span>)</span><br><span class="line">c.disk.ReadBootSector()</span><br><span class="line">c.cpu.Start()</span><br><span class="line">c.mem.LoadOS()</span><br><span class="line">fmt.Println(<span class="string">&quot;System is up.&quot;</span>)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 对外暴露的简化接口：关机流程</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(c *ComputerFacade)</span></span> PowerOff() &#123;</span><br><span class="line">fmt.Println(<span class="string">&quot;== PowerOff ==&quot;</span>)</span><br><span class="line">c.cpu.Shutdown()</span><br><span class="line">c.mem.Clear()</span><br><span class="line">fmt.Println(<span class="string">&quot;System is down.&quot;</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>客户端</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">pc := NewComputerFacade()</span><br><span class="line">pc.PowerOn()</span><br><span class="line">pc.PowerOff()</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>类图</p><div class="mermaid-wrap"><pre class="mermaid-src" hidden>    classDiagram    class CPU {        +Start()        +Shutdown()    }    class Memory {        +LoadOS()        +Clear()    }    class Disk {        +ReadBootSector()    }    class ComputerFacade {        -cpu: CPU        -mem: Memory        -disk: Disk        +NewComputerFacade()        +PowerOn()        +PowerOff()    }    ComputerFacade *-- CPU : uses    ComputerFacade *-- Memory : uses    ComputerFacade *-- Disk : uses    ComputerFacade ..&gt; CPU : Start()    ComputerFacade ..&gt; Memory : LoadOS()    ComputerFacade ..&gt; Disk : ReadBootSector()    ComputerFacade ..&gt; CPU : Shutdown()    ComputerFacade ..&gt; Memory : Clear()  </pre></div>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;1-什么是外观模式&quot;&gt;&lt;a href=&quot;#1-什么是外观模式&quot; class=&quot;headerlink&quot; title=&quot;1. 什么是外观模式&quot;&gt;&lt;/a&gt;1. 什么是外观模式&lt;/h1&gt;&lt;p&gt;外观模式是一种结构型设计模式， 能为程序库、 框架或其他复杂类提供一个简单的接口</summary>
      
    
    
    
    <category term="Design Pattern" scheme="https://eliano64.github.io/categories/Design-Pattern/"/>
    
    
    <category term="Design Pattern" scheme="https://eliano64.github.io/tags/Design-Pattern/"/>
    
    <category term="go" scheme="https://eliano64.github.io/tags/go/"/>
    
    <category term="Interface Oriented Design" scheme="https://eliano64.github.io/tags/Interface-Oriented-Design/"/>
    
    <category term="Facade Pattern" scheme="https://eliano64.github.io/tags/Facade-Pattern/"/>
    
  </entry>
  
  <entry>
    <title>251018 thinking decorator</title>
    <link href="https://eliano64.github.io/2025/10/25/251018-thinking-decorator/"/>
    <id>https://eliano64.github.io/2025/10/25/251018-thinking-decorator/</id>
    <published>2025-10-25T08:07:58.000Z</published>
    <updated>2025-10-25T09:09:35.926Z</updated>
    
    <content type="html"><![CDATA[<h1 id="1-什么是装饰器模式"><a href="#1-什么是装饰器模式" class="headerlink" title="1. 什么是装饰器模式"></a>1. 什么是装饰器模式</h1><p>装饰器模式（Decorator Pattern）是一种结构型设计模式，它允许你在不改变原有对象结构的情况下，动态地给对象添加新的功能。</p><h1 id="2-为什么需要装饰器模式"><a href="#2-为什么需要装饰器模式" class="headerlink" title="2. 为什么需要装饰器模式"></a>2. 为什么需要装饰器模式</h1><p>想象一下去咖啡店点单的场景：</p><ul><li>一杯基础款咖啡（比如美式）。</li><li>各种调料（比如牛奶、糖、巧克力酱等）。</li></ul><p>你可以选择只喝纯的美式，也可以要求加奶，再加糖。</p><p>如果我们用继承来实现“咖啡+调料”的功能，会发生什么？</p><p>我们可能需要创建<code>CoffeeWithMilk</code> <code>CoffeeWithSugar</code> <code>CoffeeWithMilkAndSugar</code>等大量的子类。</p><p>如果再增加一种调料（比如奶油），类的数量就会爆炸式增长。</p><p>同时，如果想给一个已经创建好的<code>Coffee</code>实例动态添加牛奶，使用继承是做不到的，因为继承是静态的。</p><p>但装饰器模式通过组合解决了这些问题：</p><ol><li>遵循开闭原则：你可以随时创建新的调料（装饰器），而无需修改任何现有的咖啡（组件）代码。系统对扩展开放，对修改关闭。</li><li>灵活性和动态性：可以在运行时根据需要，任意地、动态地为对象添加一层或多层功能，组合顺序和数量都非常灵活。</li><li>避免子类爆炸：无论有多少种调料，我们只需要为每种调料创建一个装饰器类，而不是为每种组合都创建一个子类。</li></ol><h1 id="3-装饰器模式的实现（go）"><a href="#3-装饰器模式的实现（go）" class="headerlink" title="3. 装饰器模式的实现（go）"></a>3. 装饰器模式的实现（go）</h1><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">type</span> Coffee <span class="keyword">interface</span> &#123;</span><br><span class="line">GetCoffee() <span class="type">string</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Latte <span class="keyword">struct</span> &#123;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(l *Latte)</span></span> GetCoffee() <span class="type">string</span> &#123;</span><br><span class="line"><span class="keyword">return</span> <span class="string">&quot;Latte&quot;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> CoffeeDecorator <span class="keyword">struct</span> &#123;</span><br><span class="line">    CoffeeOrdered *Coffee</span><br><span class="line">    AddCondiment <span class="type">string</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(d *CoffeeDecorator)</span></span> GetCoffee() <span class="type">string</span> &#123;</span><br><span class="line"><span class="keyword">return</span> d.CoffeeOrdered.GetCoffee() + <span class="string">&quot; with &quot;</span> + d.AddCondiment</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>客户端</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">latte := &amp;Latte&#123;&#125;</span><br><span class="line">latteWithMilk := &amp;CoffeeDecorator&#123;CoffeeOrdered: latte, AddCondiment: <span class="string">&quot;Milk&quot;</span>&#125;</span><br><span class="line">latteWithMilkAndSugar := &amp;CoffeeDecorator&#123;CoffeeOrdered: latteWithMilk, AddCondiment: <span class="string">&quot;Sugar&quot;</span>&#125;</span><br><span class="line">fmt.Println(latteWithMilkAndSugar.GetCoffee())</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>类图</p><div class="mermaid-wrap"><pre class="mermaid-src" hidden>    classDiagram    class Coffee {        &lt;&lt;interface&gt;&gt;        +GetCoffee() string    }    class Latte {        +GetCoffee() string    }    class CoffeeDecorator {        +GetCoffee() string        +AddCondiment string        +CoffeeOrdered *Coffee    }    Coffee &lt;|.. Latte    CoffeeDecorator &quot;1&quot; *-- &quot;1&quot; Coffee  </pre></div>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;1-什么是装饰器模式&quot;&gt;&lt;a href=&quot;#1-什么是装饰器模式&quot; class=&quot;headerlink&quot; title=&quot;1. 什么是装饰器模式&quot;&gt;&lt;/a&gt;1. 什么是装饰器模式&lt;/h1&gt;&lt;p&gt;装饰器模式（Decorator Pattern）是一种结构型设计模式，它</summary>
      
    
    
    
    <category term="Design Pattern" scheme="https://eliano64.github.io/categories/Design-Pattern/"/>
    
    
    <category term="Design Pattern" scheme="https://eliano64.github.io/tags/Design-Pattern/"/>
    
    <category term="go" scheme="https://eliano64.github.io/tags/go/"/>
    
    <category term="Decorator Pattern" scheme="https://eliano64.github.io/tags/Decorator-Pattern/"/>
    
    <category term="Interface Oriented Design" scheme="https://eliano64.github.io/tags/Interface-Oriented-Design/"/>
    
  </entry>
  
</feed>
