<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>AYAHIRO&#39;S BLOG</title>
  
  <subtitle>Nothing fxck you harder than time.</subtitle>
  <link href="/atom.xml" rel="self"/>
  
  <link href="http://yoursite.com/"/>
  <updated>2019-11-12T15:27:08.478Z</updated>
  <id>http://yoursite.com/</id>
  
  <author>
    <name>ayahiro</name>
    
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title>Redis-Note</title>
    <link href="http://yoursite.com/2019/11/11/redis-note/"/>
    <id>http://yoursite.com/2019/11/11/redis-note/</id>
    <published>2019-11-11T06:42:09.342Z</published>
    <updated>2019-11-12T15:27:08.478Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/2019/11/11/redis-note/redis_logo.png" alt="redis_logo"></p><p>看网课的一些笔记 📝</p><a id="more"></a><h3 id="持久化之RDB-💿"><a href="#持久化之RDB-💿" class="headerlink" title="持久化之RDB 💿"></a>持久化之RDB 💿</h3><p>RDB (Redis DataBase)</p><h4 id="是什么"><a href="#是什么" class="headerlink" title="是什么"></a>是什么</h4><p>redis会单独fork一个子进程来进行持久化, 会先将数据写入到一个临时文件中, 待持久化过程结束了, 再用这个临时文件替换上次持久化好的文件.<br>整个过程, 主进程不进行任何IO操作, 这就确保了极高的性能.<br>如果需要进行大规模数据的恢复, 且对数据回复的完整性不是特别敏感, RDB比AOF方式更加高效, RDB的缺点是最后一次持久化后的数据可能丢失</p><p>fork的作用是复制一个与当前进程一样的进程. 新进程的所有数据(变量, 环境变量, 程序计数器等), 数值都和原进程一样, 但是一个全新的进程, 并作为原进程的子进程</p><p>RDB保存的是dump.rdb文件</p><h4 id="配置位置"><a href="#配置位置" class="headerlink" title="配置位置"></a>配置位置</h4><p><img src="/2019/11/11/redis-note/rdb配置.png" alt="rdb配置"></p><h4 id="如何触发rdb快照"><a href="#如何触发rdb快照" class="headerlink" title="如何触发rdb快照"></a>如何触发rdb快照</h4><ul><li>配置文件中的默认快照配置 =&gt; 冷拷贝后重新使用 cp dump.rdb dump_new.rdb</li><li>save/bgsave命令<ul><li>save 只管保存, 全部阻塞</li><li>bgsave 会在后台异步进行快照操作, 快照同时还可以响应客户端请求. 可以通过lastsave命令获取最后一次成功执行快照的时间</li></ul></li><li>执行flushall命令, 会产生空的dump.rdb, 无意义</li></ul><h4 id="如何恢复"><a href="#如何恢复" class="headerlink" title="如何恢复"></a>如何恢复</h4><ul><li>将dump.rdb移动到redis安装目录并启动服务即可</li><li>config get dir 获取安装目录</li></ul><h4 id="优劣势"><a href="#优劣势" class="headerlink" title="优劣势"></a>优劣势</h4><p>优势: 😊</p><ul><li>适合大规模的数据恢复</li><li>对数据完整性和一致性要求不高</li></ul><p>劣势: 😔</p><ul><li>在一定时间间隔内做一次备份, 可能丢失最后一次快照的修改</li><li>fork的时候, 内存中的数据被复制了一份, 要占用大致两倍空间</li></ul><h3 id="持久化之AOF-📀"><a href="#持久化之AOF-📀" class="headerlink" title="持久化之AOF 📀"></a>持久化之AOF 📀</h3><p>AOF (Append Only File)</p><h4 id="是什么-1"><a href="#是什么-1" class="headerlink" title="是什么"></a>是什么</h4><p><strong>以日志的形式来记录每个写操作</strong>, 将redis执行过的所有写指令记录下来(读操作不记录), 只许追加文件但不可以改写文件, redis启动之初会读取该文件重新构建数据, redis重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作</p><p>AOF保存的是appendonly.aof文件</p><p><strong>rdb和aof共存时, 先加载aof</strong></p><h4 id="配置位置-1"><a href="#配置位置-1" class="headerlink" title="配置位置"></a>配置位置</h4><p><img src="/2019/11/11/redis-note/aof配置.png" alt="aof配置"></p><h4 id="AOF启动-修复-恢复"><a href="#AOF启动-修复-恢复" class="headerlink" title="AOF启动/修复/恢复"></a>AOF启动/修复/恢复</h4><ul><li>正常恢复<ul><li>启动: 修改默认的appendonly no 改为 yes</li><li>将有数据的aof文件复制一份到安装目录</li><li>恢复: 重启redis然后重新加载</li></ul></li><li>异常恢复<ul><li>启动: 修改默认的appendonly no 改为 yes</li><li>备份被写坏的aof文件</li><li>修复: redis-check-aof –fix 进行修复</li><li>恢复: 重启redis然后重新加载</li></ul></li></ul><h4 id="Rewrite"><a href="#Rewrite" class="headerlink" title="Rewrite"></a>Rewrite</h4><p>是什么<br>AOF采用文件追加的方式, 为了避免文件越来越大, 新增了重写机制, 当aof文件的大小超过所设定的阈值时, redis就会启动aof文件的内容压缩, 只保留可以恢复数据的最小指令集, 可以使用bgrewriteaof命令</p><p>重写原理<br>aof文件持续增长而过大时, 会fork出一条新进程来将文件重写(先写临时文件再rename). 遍历新进程内存中的数据, 为每条记录写一次set命令. 重写aof文件时, 并不会读取旧的aof文件, 而是将整个内存中的数据库内容用命令了方式重写了一个新的aof文件</p><p>触发机制<br>redis会记录上次重写时的aof文件大小, 默认配置是当aof文件大小是上次rewrite后大小的一倍且文件大于64MB时触发</p><h4 id="优劣势-1"><a href="#优劣势-1" class="headerlink" title="优劣势"></a>优劣势</h4><p>优势: 😊</p><ul><li>每修改同步: appendfsync always 同步持久化 每次发生数据变更会被立即记录到磁盘, 性能较差但数据完整性比较高</li><li>每秒同步: appendfsync everysec 异步操作, 每秒记录 如果一秒内宕机, 有数据丢失</li><li>不同步: appendfsync no 从不同步</li></ul><p>劣势: 😔</p><ul><li>相同数据集的数据而言aof文件要远大于rdb文件, 恢复速度慢于rdb</li><li>aof运行效率要慢于rdb, 每秒同步策略效率较好, 不同步效率和rdb相同</li></ul><h4 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h4><p><img src="/2019/11/11/redis-note/rdb_vs_aof.png" alt="rdb_vs_aof"></p><h3 id="事务-📋"><a href="#事务-📋" class="headerlink" title="事务 📋"></a>事务 📋</h3><p>情况一: 全体连坐 =&gt; 类比Exception  </p><p><img src="/2019/11/11/redis-note/情况1.png" alt="rdb_vs_aof"></p><p>某条命令语法出错, 则事务不能执行</p><p>情况二: 冤头债主 =&gt; 类比RuntimeException</p><p><img src="/2019/11/11/redis-note/情况2.png" alt="rdb_vs_aof"></p><p>某条命令执行结果出错, 其余的命令仍能执行</p><p>因此<strong>Redis的事务仅支持部分原子性</strong></p><p>watch命令类似于乐观锁, 通过watch命令在执行事务之前监控了多个key, 若在watch之后有任何key发生变化, exec执行的事务都将被放弃, 同时返回nullmulti-bulk应答通知调用者事务执行失败</p><p>三个特性:  </p><ol><li>单独的隔离性: 事务执行过程中, 不会被其他客户端发送来的命令请求打断</li><li>没有隔离级别的概念: 队列中的命令在没有提交之前都不会被执行, 因此也就不存在”事务内的查询要看到事务里的更新, 在事务外的查询不能看到”这种问题</li><li>不保证原子性: redis同一个事务中如果有一条命令执行失败, 其余的命令仍然会被执行, 没有回滚</li></ol><h3 id="主从复制-💾"><a href="#主从复制-💾" class="headerlink" title="主从复制 💾"></a>主从复制 💾</h3><p>配从库不配主库 =&gt; 从库配置: slaveof 主库ip 主库端口</p><h4 id="常用3种模式"><a href="#常用3种模式" class="headerlink" title="常用3种模式"></a>常用3种模式</h4><p>一主N从  </p><ol><li>从库能读取<em>配置主从关系时间点之前</em>的主库上的数据</li><li>从库不能进行写命令</li><li>主库关机, 从库原地待命, 主从关系不变</li><li>主库重连之后的写的数据, 从库能读取到</li><li>从库关机, 主从关系解除, 需要重新配置, 除非在配置文件配置主库信息</li></ol><p>薪火相传  </p><ul><li>上一个slave可以是下一个slave的master, slave同样可以接收其他slaves的连接和同步请求, 可以有效减轻master的写压力</li><li>slave中途变更指向会清除之前的数据, 重新复制最新的数据</li><li>配置方法: slaveof 新主库ip 新主库端口</li></ul><p>反客为主<br>slaveof no one 使当前数据库停止与其他数据库的同步, 转换成主库</p><h4 id="复制原理"><a href="#复制原理" class="headerlink" title="复制原理"></a>复制原理</h4><ul><li>slave启动成功连接到master后会发送一个sync命令</li><li>master接到命令启动后台的存盘进程, 同时收集所有修改数据集的命令, 在后台进程执行完毕后, master将传送整个数据文件到slave, 以完成一次完全同步</li><li>全量复制: slave和master建立连接后, 将master的数据全部复制</li><li>增量复制: master继续将后续修改命令传给slave, 完成同步. 但只要重新连接master就会自动执行一次全量复制</li></ul><h4 id="哨兵模式"><a href="#哨兵模式" class="headerlink" title="哨兵模式"></a>哨兵模式</h4><p>“反客为主”的自动版, 能后台监控主库是否故障, 如果故障了根据投票数自动将从库转换为主库</p><p>如果之前的master回来, 会成为新master的slave</p><p>一组sentinel能同时监控多个master</p><p>复制不可避免有延迟问题</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;img src=&quot;/2019/11/11/redis-note/redis_logo.png&quot; alt=&quot;redis_logo&quot;&gt;&lt;/p&gt;
&lt;p&gt;看网课的一些笔记 📝&lt;/p&gt;
    
    </summary>
    
      <category term="数据库" scheme="http://yoursite.com/categories/%E6%95%B0%E6%8D%AE%E5%BA%93/"/>
    
    
      <category term="Redis" scheme="http://yoursite.com/tags/Redis/"/>
    
  </entry>
  
  <entry>
    <title>前后端交互演变</title>
    <link href="http://yoursite.com/2019/11/08/%E5%89%8D%E5%90%8E%E7%AB%AF%E4%BA%A4%E4%BA%92%E6%BC%94%E5%8F%98/"/>
    <id>http://yoursite.com/2019/11/08/前后端交互演变/</id>
    <published>2019-11-08T07:27:55.677Z</published>
    <updated>2019-11-08T07:26:53.886Z</updated>
    
    <content type="html"><![CDATA[<h3 id="未分离"><a href="#未分离" class="headerlink" title="未分离"></a>未分离</h3><p><img src="/2019/11/08/前后端交互演变/未分离.png" alt="未分离">  </p><a id="more"></a><p>前端职责：<br>在以前传统的网站开发中，前端一般扮演的只是切图的工作，只是简单地将UI设计师提供的原型图实现成静态的HTML页面，而具体的页面交互逻辑，比如与后台的数据交互工作等，可能都是由后台的开发人员来实现的，或者是前端是紧紧的耦合后台。</p><p>后端职责：<br>有可能后台人员直接兼顾前端的工作，一边实现API接口，一边开发页面，两者互相切换着做，而且根据不同的url动态拼接页面，这也导致后台的开发压力大大增加。</p><p>开发模式：</p><p>方式一：</p><p><img src="/2019/11/08/前后端交互演变/流程1.png" alt="流程1">  </p><p>方式二：</p><p><img src="/2019/11/08/前后端交互演变/流程2.png" alt="流程2">  </p><p>使用技术：servlet+jsp/各种模板引擎</p><h4 id="Servlet"><a href="#Servlet" class="headerlink" title="Servlet"></a><a href="https://www.jianshu.com/p/7e430b61ce19" target="_blank" rel="noopener">Servlet</a></h4><p>我们编写java程序想要在网上实现 聊天、发帖、这样一些的交互功能，普通的java技术是很困难的。sun公司就提供了Serlvet这种技术供我们使用。它可以接收浏览器发过来的http请求，取得一些参数经过一些处理后，返回用户想要的信息。<br><img src="/2019/11/08/前后端交互演变/servlet.png" alt="servlet">  </p><h4 id="Jsp"><a href="#Jsp" class="headerlink" title="Jsp"></a><a href="https://www.jianshu.com/p/4bd964c96c11" target="_blank" rel="noopener">Jsp</a></h4><ul><li>JSP是一种基于文本的程序，其特点就是HTML和Java代码共同存在！</li><li>JSP是为了简化Servlet的工作出现的替代品，Servlet输出HTML非常困难，JSP就是替代Servlet输出HTML的。<br><img src="/2019/11/08/前后端交互演变/jsp编译后.png" alt="jsp编译后">  </li></ul><p>缺点：</p><ol><li>前端无法单独调试</li><li>后端代码侵入到前端代码<br> <img src="/2019/11/08/前后端交互演变/java嵌入jsp.png" alt="java嵌入jsp">  </li><li>jsp运行加载响应慢问题</li></ol><p>结果：<br>前端紧紧的依赖后端，前后端工作分配不均。不仅仅开发效率慢，而且代码难以维护。</p><p>开发模式是，前端写好静态demo，后端拿到demo，嵌套进jsp  添加一些jsp脚本 指令 表达式用来获取后端数据并展示</p><h3 id="半分离"><a href="#半分离" class="headerlink" title="半分离"></a>半分离</h3><p><img src="/2019/11/08/前后端交互演变/半分离.png" alt="半分离"></p><p>步骤：</p><ol><li>浏览器请求，cdn返回html页面</li><li>html中的js代码以ajax方式请求后台的接口</li><li>接口返回json数据，页面解析json数据，通过dom操作渲染页面</li></ol><p>前端职责：<br>前端负责开发页面，通过接口（Ajax）获取数据，采用Dom操作对页面进行数据绑定，最终是由前端把页面渲染出来。</p><p>后端职责：<br>专注于实现业务逻辑，即使在这一时期，通常也是一个工程师搞定前后端所有工作。</p><p>使用技术：ajax dom jquery json?</p><h4 id="Ajax"><a href="#Ajax" class="headerlink" title="Ajax"></a>Ajax</h4><p><img src="/2019/11/08/前后端交互演变/ajax代码.png" alt="ajax代码">  </p><p>关于什么是ajax，因为它涉及到很多技术，比较难说清楚，因此主要关注一下<br>ajax能做什么，概况来说就是<strong>客户端与服务器，可以在【不必刷新整个浏览器】的情况下，与服务器进行异步通讯的技术</strong></p><p>在我们之前的开发，每当用户向服务器发送请求，哪怕只是需要更新一点点的局部内容，服务器都会将整个页面进行刷新。</p><ul><li>性能会有所降低(一点内容，刷新整个页面！)</li><li>用户的操作页面会中断(整个页面被刷新了)</li></ul><p>ajax可以做到局部刷新，不用重新加载网页，大大提高网页加载速度和交互体验！</p><p>前端通过ajax拿到数据后就可以自由操作dom节点了。</p><h4 id="Dom"><a href="#Dom" class="headerlink" title="Dom"></a>Dom</h4><p>比如一个注册页面的输入框，提示框，提交按钮等</p><p><img src="/2019/11/08/前后端交互演变/dom树.png" alt="dom树"> </p><h4 id="Jquery"><a href="#Jquery" class="headerlink" title="Jquery"></a>Jquery</h4><p>jquery是一个优秀的JavaScript代码库，用于高效操作dom节点</p><p><img src="/2019/11/08/前后端交互演变/jquery代码.png" alt="jquery代码"> </p><h4 id="Json"><a href="#Json" class="headerlink" title="Json"></a>Json</h4><p>一种轻量级的数据交换格式，是JavaScript的一个子集<br>几乎所有编程语言都有解析JSON的库，很多开发框架都提供了将一般数据转换为json格式的数据的支持，这样就可以降低前后端数据沟通成本  </p><p><img src="/2019/11/08/前后端交互演变/json代码.png" alt="json代码"> </p><p>优点：<br>前端不会嵌入任何后台代码，前端专注于html、css、js的开发，不依赖于后端。自己还能够模拟json数据来渲染页面。发现bug，也能迅速定位出是谁的问题，不会出现互相推脱的现象。</p><p>缺点：</p><ol><li>javascript存在大量冗余，在业务复杂的情况下，页面的渲染部分的代码，非常复杂。<br><img src="/2019/11/08/前后端交互演变/冗余js.png" alt="冗余js">  </li><li>在json返回的数据比较大的情况下，渲染的十分缓慢，会出现页面卡顿的情况</li><li>seo非常不方便，由于搜索引擎的爬虫无法爬下js异步渲染的数据，导致这样的页面，SEO会存在一定的问题。</li><li>资源消耗严重，在业务复杂的情况下，一个页面可能要发起多次http请求才能将页面渲染完毕。</li></ol><p>为什么是半分离？<br>前端和后台同时开发时，就可能遇到前端已经开发好一个页面了，可是却等待后台api接口的情况。比如说A是负责前端，B是负责后台，A可能用了一周做好了基本的结构，并且需要API接口联调后，才能继续开发，而此时B却还没有实现好所需要的接口，这种情况<br>前端自己手上没有测试数据，无法模拟线上开发环境</p><h3 id="分离"><a href="#分离" class="headerlink" title="分离"></a>分离</h3><p><img src="/2019/11/08/前后端交互演变/分离.png" alt="分离"></p><h4 id="引入Node-js"><a href="#引入Node-js" class="headerlink" title="引入Node.js"></a>引入Node.js</h4><ol><li><p>引入nodejs主要是为了分层开发，职责划分。<br>nodejs作为前端服务器，由前端开发人员负责，前端开发人员不需要知道java后台的api接口是如何实现的，前端只需要关心前端的开发工作，并且管理好nodejs前端服务器，而后台开发人员也不需要考虑如何前端是如何部署的，他只需要做好自己擅长的部分，提供好API接口就可以</p></li><li><p>前端可以使用node.js生成假数据，不需要等待后端的api完成就可以模拟线上开发环境</p></li></ol><h4 id="数据和视图绑定"><a href="#数据和视图绑定" class="headerlink" title="数据和视图绑定"></a>数据和视图绑定</h4><p>过多的js代码操作dom会造成大量冗余，代码结构杂乱无章，不好维护，为了解决这个问题，前端三大框架(Vue React Angular)运营而生。</p><p>他们的核心思想是将数据(Model)和视图(View)进行绑定。</p><p><img src="/2019/11/08/前后端交互演变/vue代码.png" alt="vue代码"></p><p>vue还可以使用组件技术组织网页中的各个模块，看起来更加有序，好维护</p><p><img src="/2019/11/08/前后端交互演变/vue组件.png" alt="vue组件"></p><p>由此开始前端正式和后端说再见了</p><h4 id="微服务"><a href="#微服务" class="headerlink" title="微服务"></a>微服务</h4><p>前端和后端划清关系之后，后端更加注重处理自己的业务逻辑<br>随着应用更加复杂，功能划分更细，需要将一个大的系统拆分成小系统，每个小系统都可以对外提供一种类型的服务，微服务逐渐兴起。</p><p>正如前端有vue全家桶  java后台有sring全家桶</p><p><img src="/2019/11/08/前后端交互演变/spring.png" alt="spring"></p>]]></content>
    
    <summary type="html">
    
      &lt;h3 id=&quot;未分离&quot;&gt;&lt;a href=&quot;#未分离&quot; class=&quot;headerlink&quot; title=&quot;未分离&quot;&gt;&lt;/a&gt;未分离&lt;/h3&gt;&lt;p&gt;&lt;img src=&quot;/2019/11/08/前后端交互演变/未分离.png&quot; alt=&quot;未分离&quot;&gt;  &lt;/p&gt;
    
    </summary>
    
    
      <category term="Basic" scheme="http://yoursite.com/tags/Basic/"/>
    
  </entry>
  
  <entry>
    <title>MySQL-Note</title>
    <link href="http://yoursite.com/2019/10/23/mysql-note/"/>
    <id>http://yoursite.com/2019/10/23/mysql-note/</id>
    <published>2019-10-23T13:33:06.265Z</published>
    <updated>2019-11-11T10:47:24.100Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/2019/10/23/mysql-note/mysql_logo.jpg" alt="mysql_logo"></p><p>看网课的一些笔记 📝</p><a id="more"></a><h3 id="影响性能的几个方面-🔰"><a href="#影响性能的几个方面-🔰" class="headerlink" title="影响性能的几个方面 🔰"></a>影响性能的几个方面 🔰</h3><ul><li>服务器硬件  CPU 内存 I/O子系统 网络</li><li>服务器操作系统</li><li>数据库存储引擎的选择</li><li>数据库参数配置 =&gt; 影响巨大</li><li>数据库结构设计和SQL语句 =&gt; 慢查询</li></ul><h3 id="数据库结构优化-🔧"><a href="#数据库结构优化-🔧" class="headerlink" title="数据库结构优化 🔧"></a>数据库结构优化 🔧</h3><p>目的:</p><ul><li>减少数据冗余</li><li>尽量避免数据维护中出现更新, 插入, 删除异常<ul><li>插入异常: 表中某个实体随另一个实体存在</li><li>更改表中某个实体的单独属性时, 需要对多行进行更新</li><li>删除b表中的某一实体会导致其他实体的消失</li></ul></li></ul><h4 id="逻辑设计"><a href="#逻辑设计" class="headerlink" title="逻辑设计"></a>逻辑设计</h4><p>范式化:</p><ul><li>可以尽量减少数据冗余 😊</li><li>更新操作比反范式化快 😊</li><li>表通常比反范式化小 😊</li><li>对于查询需要对多个表进行关联 😔</li><li>更难进行索引优化 😔</li></ul><p>反范式化:</p><ul><li>可以减少表的关联 😊</li><li>更好进行索引优化 😊</li><li>存在数据冗余和数据维护异常 😔</li><li>对数据的修改需要更多成本 😔</li></ul><p><strong>在范式化基础上适当反范式化</strong></p><h4 id="物理设计"><a href="#物理设计" class="headerlink" title="物理设计"></a>物理设计</h4><p>选择合适的存储引擎:<br><img src="/2019/10/23/mysql-note/engine.png" alt="引擎"></p><p>为表中的字段选择合适的数据类型:<br>当一个列可以选择多种数据类型时, 应该优先考虑数字类型, 其次是日期或二进制类型, 最后是字符类型. 对于相同级别的数据类型, 应该优先选择占用空间小的数据类型.</p><ul><li>财务相关数据应选择decimal</li><li>varchar和char的宽度是以字符(而不是字节)为单位, 如UTF-8一个字符占用3个字节</li><li>varchar不定长, 适用场景: <ol><li>字符串列的最大长度比平均长度大很多</li><li>字符串列很少被更新</li><li>使用了多字节字符集(UTF-8)存储字符串</li></ol></li><li>char定长, 适用场景: <ol><li>适合存储长度近似的值(md5, 身份证)</li><li>短字符串(性别)</li><li>经常更新的字符串列</li></ol></li></ul><p>日期类型:</p><ul><li>datatime: YYYY-MM-DD HH:MM:SS[.fraction]<br>  与时区无关, 占用8个字节<br>  范围: 1000-01-01 00:00:00 – 9999-12-31 23:59:59</li><li>timestamp: YYYY-MM-DD HH:MM:SS[.fraction]<br>  时区有关, 时间戳, 占用4个字节 在行的数据修改时可以自动修改timestamp列的值<br>  范围: 1970-01-01 00:00:00 – 2038-01-19 23:59:59</li><li>date: YYYY-MM-DD<br>  可以用date存储日期, 只要3个字节<br>  可以用日期时间函数进行日期间的计算<br>  范围: 1000-01-01 – 9999-12-31</li><li>time: HH:MM:SS[.fraction]</li></ul><p><strong>不要用字符串类型存储日期时间</strong></p><p>如何为InnoDB选择逐渐:</p><ul><li>主键应该尽可能的小</li><li>主键应该是顺序增长的</li><li>InnoDB的主键和业务主键可以不同</li></ul><h3 id="数据库索引优化-📌"><a href="#数据库索引优化-📌" class="headerlink" title="数据库索引优化 📌"></a>数据库索引优化 📌</h3><p>为什么使用索引:</p><ul><li>大大减少了存储引擎需要扫描的数据量</li><li>进行排序时避免使用临时表</li><li>可以把随机I/O变为顺序I/O</li></ul><p>索引不是越多越好:</p><ul><li>会增加写操作的成本</li><li>会增加查询优化器的选择时间</li></ul><h4 id="B-tree索引"><a href="#B-tree索引" class="headerlink" title="B-tree索引"></a>B-tree索引</h4><p>特点:</p><ul><li>以B+树结构存储数据</li><li>加快数据查询速度</li><li>适合进行范围查找</li></ul><p>什么时候用到 😊</p><ul><li>等值查询<br>  user_name=”bxy”</li><li>匹配最左前缀查询<br>  联合索引(user_name, id)  查询条件为user_name列时, 会使用该联合索引</li><li>匹配列前缀查询<br>  user_name like ‘bx%’</li><li>范围查找<br>  id &gt; 10 and id &lt; 100</li><li>精确匹配左前列并范围匹配另外一列<br>  精确匹配user_name 并范围查询id</li><li>只访问索引的查询<br>  只需访问索引, 无需访问数据行(无需回表操作) 即覆盖索引</li></ul><p>使用限制 😔</p><ul><li>如果不是按照索引最左列开始查找, 则无法使用索引<br>  联合索引(user_name, id)  查询条件为id列时, 无法使用该联合索引</li><li>使用索引时不能跳过索引中的列<br>  联合索引(user_name, id, role) 查询条件包含user_name和role 只有user_name会用到该联合索引</li><li>not in 和 &lt;&gt; 操作无法使用索引</li><li>如果查询中有某个列的范围查询, 则其右边的所有列都无法使用索引<br>  联合索引(id, role, …) id使用范围查询, 则其右边的列都无法使用索引</li></ul><h4 id="Hash索引"><a href="#Hash索引" class="headerlink" title="Hash索引"></a>Hash索引</h4><p>特点:</p><ul><li>基于hash表实现</li><li>只能用在等值查询中</li><li>hash索引中的所有列, 存储引擎都会每一行计算一个hash码, hash索引中存储的是hash码</li></ul><p>限制: </p><ul><li>hash索引必须进行二次查找<br>  索引中没有保存字段的值, 先通过hash索引找到对应的行, 再从行中读取字段</li><li>无法用于排序</li><li>不支持部分索引查找也不支持范围查找</li><li>hash码的计算可能存在hash冲突, 不适合用用在选择性差的列上(如性别, 只有男和女)</li></ul><h4 id="索引优化策略"><a href="#索引优化策略" class="headerlink" title="索引优化策略"></a>索引优化策略</h4><ul><li>索引列上不能使用函数或表达式<br>  <img src="/2019/10/23/mysql-note/索引优化.png" alt="索引优化"></li><li>注意控制前缀索引的长度和索引列的选择性</li><li>建立联合索引  <ul><li>经常会被使用到的列优先</li><li>选择性高的列优先</li><li>宽度小的列优先</li></ul></li><li>使用<a href="https://www.jianshu.com/p/8991cbca3854" target="_blank" rel="noopener">索引覆盖</a></li></ul><p>利用索引优化锁: </p><ul><li>索引可以减少锁定的行数</li><li>索引可以加快处理速度, 同时也加快锁的释放</li></ul><p>删除重复和冗余索引: </p><ul><li>primary key(id), <del>unique key(id)</del>, <del>index(id)</del></li><li><del>index(a</del>), index(a, b)</li><li>primary key(id), <del>index(a, id)</del></li></ul><h3 id="SQL查询优化-🔎"><a href="#SQL查询优化-🔎" class="headerlink" title="SQL查询优化 🔎"></a>SQL查询优化 🔎</h3><p>获取有性能问题的SQL途径:</p><ul><li>slow_query_log 启动慢查询日志<br>  常用慢查询日志分析工具: mysqldumpslow  pt-query-digest</li><li>通过information_schema中的processlist表来实时获取有性能问题的SQL</li></ul><p>确定查询处理各个阶段消耗的时间:</p><ul><li>使用profile</li><li>使用performance_schema</li></ul><p>特定的SQL查询优化: </p><ul><li>修改大表的表结构  使用pt-online-schema-change</li><li>如何优化not in和&lt;&gt;查询<br>  <img src="/2019/10/23/mysql-note/优化not_in.png" alt="优化not_in"></li><li>使用汇总表优化查询</li><li>待汇总…</li></ul><h3 id="数据库分库分表-📦"><a href="#数据库分库分表-📦" class="headerlink" title="数据库分库分表 📦"></a>数据库分库分表 📦</h3><p>分库分表可以分担主数据库的写负载, 主从复制可以分担主数据库的读负载</p><p>不到万不得已不用考虑数据库分片</p><p>待汇总…</p><h3 id="MySQL主从复制-💾"><a href="#MySQL主从复制-💾" class="headerlink" title="MySQL主从复制 💾"></a>MySQL主从复制 💾</h3><p>复制解决了什么问题:  </p><ul><li>实现在不同服务器上的数据分布</li><li>实现数据读取的负载均衡</li><li>增强了数据的安全性</li><li>实现数据库高可用和故障切换</li><li>实现数据库在线升级</li></ul><h4 id="二进制日志"><a href="#二进制日志" class="headerlink" title="二进制日志"></a>二进制日志</h4><p>二进制日志binlog是MySQL服务层日志, 记录了所有对MySQL数据库的修改事件, 包括CRUD和对表结构的修改事件</p><p>二进制日志的格式:  </p><ul><li>基于段的格式 binlog_format=STATEMENT 记录的是SQL语句<ul><li>日志记录量相对较小, 节约磁盘及网络I/O 😊<br>  只对一条记录修改或插入, row格式所产生的日志量小于段产生的日志量</li><li>必须要记录上下文信息, 保证语句在服务器上执行结果和在主服务器上相同 😔<br>  特定函数如UUID(), user() 这样非确定性函数还是无法复制<br>  可能造成MySQL复制的主备服务器数据不一致</li></ul></li><li>基于行的日志格式 binlog_format=ROW  (version &gt; 5.7 default) 记录的是修改记录行的信息<ul><li>可以避免MySQL复制中出现的主从不一致问题 😊<br>  对每一行数据的修改比基于段的复制高效</li><li>记录日志量较大 😔<br>  binlog_row_image=[FULL | MINIMAL | NOBLOB]</li></ul></li><li>混合日志格式 binlog_format=MIXED<ul><li>根据SQL语句由系统决定在段or行的日志格式中选择</li><li>数据量的大小由所执行的SQL语句决定</li></ul></li></ul><h4 id="二进制日志格式对复制的影响"><a href="#二进制日志格式对复制的影响" class="headerlink" title="二进制日志格式对复制的影响"></a>二进制日志格式对复制的影响</h4><p>基于SQL语句的复制(SBR)</p><p>优点:  </p><ul><li>生成的日志量少, 节约网络传输I/O</li><li>不强制要求主从数据库的表定义完全相同</li><li>相比于基于行的复制方式更为灵活</li></ul><p>缺点:  </p><ul><li>对于非确定性事件, 无法保证主从复制数据一致性</li><li>对于存储过程, 触发器, 自定义函数进行的修改也可能造成数据不一致</li><li>相比于基于行的复制方式在从库上执行需要更多的行锁</li></ul><p>基于行的复制(RBR) 👍</p><p>优点:  </p><ul><li>可以应用于任何SQL的复制包括非确定性函数, 存储过程等</li><li>可以减少从库锁的使用</li></ul><p>缺点:  </p><ul><li>要求主从数据库的表结构相同, 否则可能会中断复制</li><li>无法在从库上单独执行触发器</li></ul><h4 id="MySQL复制工作方式"><a href="#MySQL复制工作方式" class="headerlink" title="MySQL复制工作方式"></a>MySQL复制工作方式</h4><p><img src="/2019/10/23/mysql-note/复制工作方式.png" alt="复制工作方式"></p><ol><li>主服务器将变更写入二进制日志(需先开启binlog, 否则再开启要重启数据库服务器)</li><li>从服务器读取主服务器的二进制日志变更并写入到relay_log中<br> 在从服务器上启动一个工作线程(I/O线程)与主库建立客户端连接, 在主库上启动二进制转储线程(binlogdump), I/O线程就通过binlogdump读取主库的binlog中的事件<br> 根据从什么位置开始读取binlog, 可以分为<ul><li>基于日志点的复制</li><li>基于GTID的复制</li></ul></li><li>从服务器读取relay_log中的事件, 在从库上重放(由SQL线程完成)</li></ol><h4 id="MySQL复制性能优化"><a href="#MySQL复制性能优化" class="headerlink" title="MySQL复制性能优化"></a>MySQL复制性能优化</h4><p>影响主从延迟的因素</p><ul><li>主库写入二进制日志的时间 =&gt; 控制主库的事务大小, 分割大事务</li><li>二进制日志传输时间 =&gt; 使用MIXED日志格式或设置set binlog_row_image=minimal</li><li>默认情况只有一个SQL线程, 主库上b并发的修改在从库上变成了串行 =&gt; 使用多线程复制</li></ul>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;img src=&quot;/2019/10/23/mysql-note/mysql_logo.jpg&quot; alt=&quot;mysql_logo&quot;&gt;&lt;/p&gt;
&lt;p&gt;看网课的一些笔记 📝&lt;/p&gt;
    
    </summary>
    
      <category term="数据库" scheme="http://yoursite.com/categories/%E6%95%B0%E6%8D%AE%E5%BA%93/"/>
    
    
      <category term="MySQL" scheme="http://yoursite.com/tags/MySQL/"/>
    
  </entry>
  
  <entry>
    <title>MySQL实战45讲：6-8</title>
    <link href="http://yoursite.com/2019/10/12/%5BMySQL%5D6-8/"/>
    <id>http://yoursite.com/2019/10/12/[MySQL]6-8/</id>
    <published>2019-10-12T13:42:21.557Z</published>
    <updated>2019-08-09T11:19:49.279Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/2019/10/12/[MySQL]6-8/封面.jpg" alt="封面"></p><p>1-8讲是基础篇，学完了基础就到实战了，接下来的提炼就看心情吧。</p><a id="more"></a><h2 id="06-全局锁和表锁-：给表加个字段怎么有这么多阻碍？"><a href="#06-全局锁和表锁-：给表加个字段怎么有这么多阻碍？" class="headerlink" title="06 | 全局锁和表锁 ：给表加个字段怎么有这么多阻碍？"></a>06 | 全局锁和表锁 ：给表加个字段怎么有这么多阻碍？</h2><p><strong>根据加锁的范围，MySQL里面的锁大致可以分成全局锁、表级锁和行锁三类。</strong></p><h3 id="全局锁"><a href="#全局锁" class="headerlink" title="全局锁"></a>全局锁</h3><p>全局锁就是对整个数据库实例加锁。MySQL提供了一个加全局读锁的方法，命令是 Flush tables with read lock (FTWRL)。使用这个命令之后其他线程的以下语句会被阻塞：数据更新语句（数据的增删改）、数据定义语句（包括建表、修改表结构等）和更新类事务的提交语句。</p><p><strong>全局锁的典型使用场景是，做全库逻辑备份。也就是把整库每个表都select出来存成文本。</strong></p><h4 id="备份表不加锁的问题："><a href="#备份表不加锁的问题：" class="headerlink" title="备份表不加锁的问题："></a>备份表不加锁的问题：</h4><p>备份系统备份的得到的库不是一个逻辑时间点，这个视图是逻辑不一致的。</p><p><img src="/2019/10/12/[MySQL]6-8/业务备份状态图.png" alt="业务备份状态图"></p><p>解决：</p><p>在可重复读隔离级别下开启一个事务：官方自带的逻辑备份工具是mysqldump。当mysqldump使用参数–single-transaction的时候，导数据之前就会启动一个事务，来确保拿到一致性视图。而由于MVCC的支持，这个过程中数据是可以正常更新的。</p><h4 id="为什么有了mysqldump还需要FTWRL"><a href="#为什么有了mysqldump还需要FTWRL" class="headerlink" title="为什么有了mysqldump还需要FTWRL?"></a>为什么有了mysqldump还需要FTWRL?</h4><p>有些引擎（MyISAM）不支持事务，也就不支持可重复读隔离级别，所以，single-transaction方法只适用于所有的表使用事务引擎的库。如果有的表使用了不支持事务的引擎，那么备份就只能通过FTWRL方法。</p><h4 id="为什么不使用set-global-readonly-true的方式"><a href="#为什么不使用set-global-readonly-true的方式" class="headerlink" title="为什么不使用set global readonly=true的方式?"></a>为什么不使用set global readonly=true的方式?</h4><p>readonly方式也可以让全库进入只读状态，但还是建议用FTWRL方式，主要有两个原因：</p><ul><li><p>在有些系统中，readonly的值会被用来做其他逻辑，比如用来判断一个库是主库还是备库。因此，修改global变量的方式影响面更大，不建议使用。</p></li><li><p>在异常处理机制上有差异。如果执行FTWRL命令之后由于客户端发生异常断开，那么MySQL会自动释放这个全局锁，整个库回到可以正常更新的状态。而将整个库设置为readonly之后，如果客户端发生异常，则数据库就会一直保持readonly状态，这样会导致整个库长时间处于不可写状态，风险较高。</p></li></ul><h3 id="表级锁"><a href="#表级锁" class="headerlink" title="表级锁"></a>表级锁</h3><p>MySQL里面表级别的锁有两种：一种是表锁，一种是元数据锁（meta data lock，MDL)。</p><h4 id="表锁"><a href="#表锁" class="headerlink" title="表锁"></a>表锁</h4><p>表锁的语法是 lock tables … read/write。可以用unlock tables主动释放锁，也可以在客户端断开的时候自动释放。需要注意，lock tables语法除了会限制别的线程的读写外，也限定了本线程接下来的操作对象。</p><p><strong>对于InnoDB这种支持行锁的引擎，一般不使用lock tables命令来控制并发，毕竟锁住整个表的影响面还是太大。</strong></p><h4 id="元数据锁"><a href="#元数据锁" class="headerlink" title="元数据锁"></a>元数据锁</h4><p>MDL不需要显式使用，在访问一个表的时候会被自动加上。MDL的作用是，保证读写的正确性。</p><p>在MySQL 5.5版本中引入了MDL，当对一个表做增删改查操作的时候，加MDL读锁；当要对表做结构变更操作的时候，加MDL写锁。</p><ul><li><p>读锁之间不互斥，因此你可以有多个线程同时对一张表增删改查。</p></li><li><p>读写锁之间、写锁之间是互斥的，用来保证变更表结构操作的安全性。因此，如果有两个线程要同时给一个表加字段，其中一个要等另一个执行完才能开始执行。</p></li></ul><p><strong>事务中的MDL锁，在语句执行开始时申请，但是语句结束后并不会马上释放，而会等到整个事务提交后再释放。</strong></p><h4 id="如何安全地给小表加字段？"><a href="#如何安全地给小表加字段？" class="headerlink" title="如何安全地给小表加字段？"></a>如何安全地给小表加字段？</h4><p>首先要解决长事务，事务不提交，就会一直占着MDL锁。在MySQL的information_schema 库的 innodb_trx 表中，可以查到当前执行中的事务。如果要做DDL变更的表刚好有长事务在执行，要考虑先暂停DDL，或者kill掉这个长事务。</p><p>如果要变更的表是一个热点表，虽然数据量不大，但是上面的请求很频繁，这时候kill可能未必管用，因为新的请求马上就来了。比较理想的机制是，在alter table语句里面设定等待时间，如果在这个指定的等待时间里面能够拿到MDL写锁最好，拿不到也不要阻塞后面的业务语句，先放弃。之后开发人员或者DBA再通过重试命令重复这个过程。MariaDB已经合并了AliSQL的这个功能。</p><h2 id="07-行锁功过：怎么减少行锁对性能的影响？"><a href="#07-行锁功过：怎么减少行锁对性能的影响？" class="headerlink" title="07 | 行锁功过：怎么减少行锁对性能的影响？"></a>07 | 行锁功过：怎么减少行锁对性能的影响？</h2><p>MySQL的行锁是在引擎层由各个引擎自己实现的。但并不是所有的引擎都支持行锁，比如MyISAM引擎就不支持行锁。不支持行锁会影响到业务并发度。</p><h3 id="两阶段锁协议"><a href="#两阶段锁协议" class="headerlink" title="两阶段锁协议"></a>两阶段锁协议</h3><p><strong>在InnoDB事务中，行锁是在需要的时候才加上的，但并不是不需要了就立刻释放，而是要等到事务结束时才释放。这个就是两阶段锁协议。</strong></p><p><img src="/2019/10/12/[MySQL]6-8/两阶段锁.jpg" alt="两阶段锁"></p><p><strong>如果事务中需要锁多个行，要把最可能造成锁冲突、最可能影响并发度的锁尽量往后放。</strong></p><p>如下面这个例子：</p><p>实现一个电影票在线交易业务，顾客A要在影院B购买电影票。简化一点，这个业务需要涉及到以下操作：</p><ol><li>从顾客A账户余额中扣除电影票价；</li><li>给影院B的账户余额增加这张电影票价；</li><li>记录一条交易日志。</li></ol><p>如果同时有另外一个顾客C要在影院B买票，那么这两个事务冲突的部分就是语句2了。因为它们要更新同一个影院账户的余额，需要修改同一行数据。</p><p>根据两阶段锁协议，不论怎样安排语句顺序，所有的操作需要的行锁都是在事务提交的时候才释放的。所以，如果把语句2安排在最后，比如按照3、1、2这样的顺序，那么影院账户余额这一行的锁时间就最少。这就最大程度地减少了事务之间的锁等待，提升了并发度。</p><h3 id="死锁和死锁检测"><a href="#死锁和死锁检测" class="headerlink" title="死锁和死锁检测"></a>死锁和死锁检测</h3><p>当并发系统中不同线程出现循环资源依赖，涉及的线程都在等待别的线程释放资源时，就会导致这几个线程都进入无限等待的状态，称为死锁。</p><p><img src="/2019/10/12/[MySQL]6-8/死锁.jpg" alt="死锁"></p><p>当出现死锁以后，有两种策略：</p><ul><li><p>直接进入等待，直到超时。这个超时时间可以通过参数innodb_lock_wait_timeout来设置。</p></li><li><p>发起死锁检测，发现死锁后，主动回滚死锁链条中的某一个事务，让其他事务得以继续执行。将参数innodb_deadlock_detect设置为on，表示开启这个逻辑。</p></li></ul><p>正常情况下还是要采用第二种策略，即：主动死锁检测，而且innodb_deadlock_detect的默认值本身就是on。主动死锁检测在发生死锁的时候，是能够快速发现并进行处理的，但是它也是有额外负担的。</p><p>死锁检测要耗费大量的CPU资源，怎么解决由这种热点行更新导致的性能问题呢？</p><ul><li><p>如果能确保这个业务一定不会出现死锁，可以临时把死锁检测关掉。而关掉死锁检测意味着可能会出现大量的超时，这是业务有损的。</p></li><li><p>从设计上优化，以影院账户为例，可以考虑放在多条记录上，比如10个记录，影院的账户总额等于这10个记录的值的总和。这样每次要给影院账户加金额的时候，随机选其中一条记录来加。这样每次冲突概率变成原来的1/10，可以减少锁等待个数，也就减少了死锁检测的CPU消耗。</p></li></ul><h2 id="08-事务到底是隔离的还是不隔离的？"><a href="#08-事务到底是隔离的还是不隔离的？" class="headerlink" title="08 | 事务到底是隔离的还是不隔离的？"></a>08 | 事务到底是隔离的还是不隔离的？</h2><p>开篇问题，事务B查到的k的值是3，而事务A查到的k的值是1，为什么？</p><p><img src="/2019/10/12/[MySQL]6-8/可重复读事务执行流程.png" alt="可重复读事务执行流程"></p><p>需要注意事务的启动时机：</p><p>begin/start transaction 命令并不是一个事务的起点，在执行到它们之后的第一个操作InnoDB表的语句，事务才真正启动。如果想要马上启动一个事务，可以使用start transaction with consistent snapshot 这个命令。事务C没有显式地使用begin/commit，表示这个update语句本身就是一个事务，语句完成的时候会自动提交。</p><p>在MySQL里，有两个“视图”的概念：</p><ul><li><p>一个是view。它是一个用查询语句定义的虚拟表，在调用的时候执行查询语句并生成结果。创建视图的语法是create view … ，而它的查询方法与表一样。</p></li><li><p>另一个是InnoDB在实现MVCC时用到的一致性读视图，即consistent read view，用于支持RC（Read Committed，读提交）和RR（Repeatable Read，可重复读）隔离级别的实现。它没有物理结构，作用是事务执行期间用来定义“我能看到什么数据”。</p></li></ul><h3 id="“快照”在MVCC里是怎么工作的？"><a href="#“快照”在MVCC里是怎么工作的？" class="headerlink" title="“快照”在MVCC里是怎么工作的？"></a>“快照”在MVCC里是怎么工作的？</h3><p>InnoDB里面每个事务有一个唯一的事务ID，叫作transaction id。它是在事务开始的时候向InnoDB的事务系统申请的，是按申请顺序严格递增的。</p><p>每行数据也都是有多个版本的。每次事务更新数据的时候，都会生成一个新的数据版本，并且把transaction id赋值给这个数据版本的事务ID，记为row trx_id。</p><p>在实现上， InnoDB为每个事务构造了一个数组，用来保存这个事务启动瞬间，当前正在“活跃”的所有事务ID。“活跃”指的就是，启动了但还没提交。</p><p>数组里面事务ID的最小值记为低水位，当前系统里面已经创建过的事务ID的最大值加1记为高水位。视图数组和高水位，就组成了当前事务的一致性视图（read-view）。</p><p>视图数组把所有的row trx_id 分成了几种不同的情况。</p><p><img src="/2019/10/12/[MySQL]6-8/数据版本可见性规则.png" alt="数据版本可见性规则"></p><p>对于当前事务的启动瞬间来说，一个数据版本的row trx_id，有以下几种可能：</p><ol><li><p>如果落在绿色部分，表示这个版本是已提交的事务或者是当前事务自己生成的，这个数据是可见的；</p></li><li><p>如果落在红色部分，表示这个版本是由将来启动的事务生成的，是肯定不可见的；</p></li><li><p>如果落在黄色部分，那就包括两种情况</p></li></ol><ul><li>若 row trx_id在数组中，表示这个版本是由还没提交的事务生成的，不可见；</li><li>若 row trx_id不在数组中，表示这个版本是已经提交了的事务生成的，可见。</li></ul><p><strong>InnoDB利用了“所有数据都有多个版本”的这个特性，实现了“秒级创建快照”的能力。</strong></p><p>回到开篇问题，利用上面的理论画图理解</p><p><img src="/2019/10/12/[MySQL]6-8/事务A查询数据逻辑.png" alt="事务A查询数据逻辑"></p><p>事务A查询语句的读数据流程是这样的：</p><ul><li>找到(1,3)的时候，判断出row trx_id=101，比高水位大，处于红色区域，不可见；</li><li>接着，找到上一个历史版本，一看row trx_id=102，比高水位大，处于红色区域，不可见；</li><li>再往前找，终于找到了（1,1)，它的row trx_id=90，比低水位小，处于绿色区域，可见。</li></ul><p>翻译一下。一个数据版本，对于一个事务视图来说，除了自己的更新总是可见以外，有三种情况：</p><ol><li><p>版本未提交，不可见；</p></li><li><p>版本已提交，但是是在视图创建后提交的，不可见；</p></li><li><p>版本已提交，而且是在视图创建前提交的，可见。</p></li></ol><p>利用这个规则判断事务A的查询结果，是这样的：</p><ul><li>(1,3)还没提交，属于情况1，不可见；</li><li>(1,2)虽然提交了，但是是在视图数组创建之后提交的，属于情况2，不可见；</li><li>(1,1)是在视图数组创建之前提交的，可见。</li></ul><h3 id="更新逻辑"><a href="#更新逻辑" class="headerlink" title="更新逻辑"></a>更新逻辑</h3><p><strong>更新数据都是先读后写的，而这个读，只能读当前的值，称为“当前读”（current read）。</strong></p><p><strong>事务的可重复读的能力是怎么实现的？</strong></p><p>可重复读的核心就是一致性读（consistent read）；而事务更新数据的时候，只能用当前读。如果当前的记录的行锁被其他事务占用的话，就需要进入锁等待。</p><p>而读提交的逻辑和可重复读的逻辑类似，它们最主要的区别是：</p><ul><li><p>在可重复读隔离级别下，只需要在事务开始的时候创建一致性视图，之后事务里的其他查询都共用这个一致性视图；</p></li><li><p>在读提交隔离级别下，每一个语句执行前都会重新算出一个新的视图。</p></li></ul><p><img src="/2019/10/12/[MySQL]6-8/读提交隔离级别下的事务状态图.png" alt="读提交隔离级别下的事务状态图"></p><p>事务A的查询语句的视图数组是在执行这个语句的时候创建的，时序上(1,2)、(1,3)的生成时间都在创建这个视图数组的时刻之前。但是，在这个时刻：</p><ul><li>(1,3)还没提交，属于情况1，不可见；</li><li>(1,2)提交了，属于情况3，可见。</li></ul><p>所以，这时候事务A查询语句返回的是k=2。显然地，事务B查询结果k=3。</p><p>小结一下</p><p>InnoDB的行数据有多个版本，每个数据版本有自己的row trx_id，每个事务或者语句有自己的一致性视图。普通查询语句是一致性读，一致性读会根据row trx_id和一致性视图确定数据版本的可见性。</p><ul><li><p>对于可重复读，查询只承认在事务启动前就已经提交完成的数据；</p></li><li><p>对于读提交，查询只承认在语句启动前就已经提交完成的数据；</p></li></ul>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;img src=&quot;/2019/10/12/[MySQL]6-8/封面.jpg&quot; alt=&quot;封面&quot;&gt;&lt;/p&gt;
&lt;p&gt;1-8讲是基础篇，学完了基础就到实战了，接下来的提炼就看心情吧。&lt;/p&gt;
    
    </summary>
    
      <category term="数据库" scheme="http://yoursite.com/categories/%E6%95%B0%E6%8D%AE%E5%BA%93/"/>
    
    
      <category term="MySQL" scheme="http://yoursite.com/tags/MySQL/"/>
    
  </entry>
  
  <entry>
    <title>MySQL实战45讲：1-5</title>
    <link href="http://yoursite.com/2019/10/12/%5BMySQL%5D1-5/"/>
    <id>http://yoursite.com/2019/10/12/[MySQL]1-5/</id>
    <published>2019-10-12T13:42:21.537Z</published>
    <updated>2019-08-06T04:25:04.421Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/2019/10/12/[MySQL]1-5/封面.jpg" alt="封面"></p><p>为啥会有这个系列呢，因为我这人比较功利，学到了东西怕忘了，忘了就等于没学，浪费时间，开这个系列是记录一些概念和知识点，方便以后查阅。文字基本是原文，我只是提炼一些关键的段落。</p><blockquote><p>资料来源于极客时间林晓斌的《MySQL实战45讲》。</p></blockquote><a id="more"></a><h2 id="01-基础架构：一条SQL查询语句是如何执行的"><a href="#01-基础架构：一条SQL查询语句是如何执行的" class="headerlink" title="01 | 基础架构：一条SQL查询语句是如何执行的?"></a>01 | 基础架构：一条SQL查询语句是如何执行的?</h2><p><img src="/2019/10/12/[MySQL]1-5/逻辑架构图.png" alt="逻辑架构图"></p><p>大体来说，MySQL可以分为Server层和存储引擎层两部分。</p><ul><li><p>Server层包括连接器、查询缓存、分析器、优化器、执行器等，涵盖MySQL的大多数核心服务功能，以及所有的内置函数（如日期、时间、数学和加密函数等），所有跨存储引擎的功能都在这一层实现，比如存储过程、触发器、视图等。</p></li><li><p>存储引擎层负责数据的存储和提取。其架构模式是插件式的，支持InnoDB、MyISAM、Memory等多个存储引擎。现在最常用的存储引擎是InnoDB，它从MySQL 5.5.5版本开始成为了默认存储引擎。</p></li></ul><h3 id="连接器"><a href="#连接器" class="headerlink" title="连接器"></a>连接器</h3><p>第一步，会先连接到这个数据库上，这时候接待你的就是连接器。连接器负责跟客户端建立连接、获取权限、维持和管理连接。</p><h3 id="查询缓存"><a href="#查询缓存" class="headerlink" title="查询缓存"></a>查询缓存</h3><p>连接建立完成后，就可以执行select语句了。执行逻辑就会来到第二步：查询缓存。MySQL拿到一个查询请求后，会先到查询缓存看看，之前是不是执行过这条语句，如果语句不在查询缓存中，就会继续后面的执行阶段。执行完成后，执行结果会被存入查询缓存中。MySQL 8.0版本直接将查询缓存的整块功能删掉了。</p><h3 id="分析器"><a href="#分析器" class="headerlink" title="分析器"></a>分析器</h3><p>如果没有命中查询缓存，就要开始真正执行语句了。分析器先会做“词法分析”。你输入的是由多个字符串和空格组成的一条SQL语句，MySQL需要识别出里面的字符串分别是什么，代表什么。</p><h3 id="优化器"><a href="#优化器" class="headerlink" title="优化器"></a>优化器</h3><p>经过了分析器，MySQL就知道你要做什么了。在开始执行之前，还要先经过优化器的处理。优化器是在表里面有多个索引的时候，决定使用哪个索引；或者在一个语句有多表关联（join）的时候，决定各个表的连接顺序。</p><h3 id="执行器"><a href="#执行器" class="headerlink" title="执行器"></a>执行器</h3><p>进入了执行器阶段，开始执行语句。开始执行的时候，要先判断一下你对这个表T有没有执行查询的权限，如果没有，就会返回没有权限的错误，如果有权限，就打开表继续执行。打开表的时候，执行器就会根据表的引擎定义，去使用这个引擎提供的接口。</p><p>数据库的慢查询日志中有一个rows_examined的字段，表示这个语句执行过程中扫描了多少行。这个值就是在执行器每次调用引擎获取数据行的时候累加的。<br>在有些场景下，执行器调用一次，在引擎内部则扫描了多行，因此<strong>引擎扫描行数跟rows_examined并不是完全相同的</strong>。</p><h2 id="02-日志系统：一条SQL更新语句是如何执行的？"><a href="#02-日志系统：一条SQL更新语句是如何执行的？" class="headerlink" title="02 | 日志系统：一条SQL更新语句是如何执行的？"></a>02 | 日志系统：一条SQL更新语句是如何执行的？</h2><p>可以确定的说，查询语句的那一套流程，更新语句也是同样会走一遍。</p><p>执行语句前要先连接数据库，这是连接器的工作，接下来，分析器会通过词法和语法解析知道这是一条更新语句。优化器决定要使用ID这个索引。然后，执行器负责具体执行，找到这一行，然后更新。</p><p>与查询流程不一样的是，更新流程还涉及两个重要的日志模块：redo log（重做日志）和 binlog（归档日志）。</p><h3 id="redo-log"><a href="#redo-log" class="headerlink" title="redo log"></a>redo log</h3><p>redo log是InnoDB引擎特有的日志。</p><p>如果每一次的更新操作都需要写进磁盘，然后磁盘也要找到对应的那条记录，然后再更新，整个过程IO成本、查找成本都很高，为了解决这个问题，MySQL里经常说到的WAL技术，WAL的全称是Write-Ahead Logging，它的关键点就是先写日志，再写磁盘。</p><p>具体来说，当有一条记录需要更新的时候，InnoDB引擎就会先把记录写到redo log里面，并更新内存，这个时候更新就算完成了。同时，InnoDB引擎会在适当的时候，将这个操作记录更新到磁盘里面，而这个更新往往是在系统比较空闲的时候做。</p><p>有了redo log，InnoDB就可以保证即使数据库发生异常重启，之前提交的记录都不会丢失，这个能力称为<strong>crash-safe</strong>。</p><h3 id="binlog"><a href="#binlog" class="headerlink" title="binlog"></a>binlog</h3><p>binlog是Server层自己的日志。</p><p>为什么会有两份日志呢？</p><p>最开始MySQL里并没有InnoDB引擎。MySQL自带的引擎是MyISAM，但是MyISAM没有crash-safe的能力，binlog日志只能用于归档。而InnoDB是另一个公司以插件形式引入MySQL的，既然只依靠binlog是没有crash-safe能力的，所以InnoDB使用另外一套日志系统——也就是redo log来实现crash-safe能力。</p><p>这两种日志有以下三点不同</p><ol><li><p>redo log是InnoDB引擎特有的；binlog是MySQL的Server层实现的，所有引擎都可以使用。</p></li><li><p>redo log是物理日志，记录的是“在某个数据页上做了什么修改”；binlog是逻辑日志，记录的是这个语句的原始逻辑，比如“给ID=2这一行的c字段加1 ”。</p></li><li><p>redo log是循环写的，空间固定会用完；binlog是可以追加写入的。“追加写”是指binlog文件写到一定大小后会切换到下一个，并不会覆盖以前的日志。</p></li></ol><p>update语句的执行流程图，图中浅色框表示是在InnoDB内部执行的，深色框表示是在执行器中执行的。</p><p><img src="/2019/10/12/[MySQL]1-5/update语句执行流程.png" alt="update语句执行流程"></p><h3 id="两阶段提交"><a href="#两阶段提交" class="headerlink" title="两阶段提交"></a>两阶段提交</h3><p>将redo log的写入拆成了两个步骤：prepare和commit，这就是”两阶段提交”。</p><p>如果不使用“两阶段提交”，那么数据库的状态就有可能和用它的日志恢复出来的库的状态不一致。</p><p>简单说，redo log和binlog都可以用于表示事务的提交状态，而两阶段提交就是让这两个状态保持逻辑上的一致。</p><h2 id="03-事务隔离：为什么你改了我还看不见？"><a href="#03-事务隔离：为什么你改了我还看不见？" class="headerlink" title="03 | 事务隔离：为什么你改了我还看不见？"></a>03 | 事务隔离：为什么你改了我还看不见？</h2><p>提到事务，会想到ACID（Atomicity、Consistency、Isolation、Durability，即原子性、一致性、隔离性、持久性）。</p><p>当数据库上有多个事务同时执行的时候，就可能出现脏读（dirty read）、不可重复读（non-repeatable read）、幻读（phantom read）的问题，为了解决这些问题，就有了“隔离级别”的概念。</p><ul><li>读未提交是指，一个事务还没提交时，它做的变更就能被别的事务看到。</li><li>读提交是指，一个事务提交之后，它做的变更才会被其他事务看到。</li><li>可重复读是指，一个事务执行过程中看到的数据，总是跟这个事务在启动时看到的数据是一致的。当然在可重复读隔离级别下，未提交变更对其他事务也是不可见的。</li><li>串行化，顾名思义是对于同一行记录，“写”会加“写锁”，“读”会加“读锁”。当出现读写锁冲突的时候，后访问的事务必须等前一个事务执行完成，才能继续执行。</li></ul><p>在实现上，数据库里面会创建一个视图，访问的时候以视图的逻辑结果为准。在“可重复读”隔离级别下，这个视图是在事务启动时创建的，整个事务存在期间都用这个视图。在“读提交”隔离级别下，这个视图是在每个SQL语句开始执行的时候创建的。这里需要注意的是，“读未提交”隔离级别下直接返回记录上的最新值，没有视图概念；而“串行化”隔离级别下直接用加锁的方式来避免并行访问。</p><h3 id="事务隔离的实现"><a href="#事务隔离的实现" class="headerlink" title="事务隔离的实现"></a>事务隔离的实现</h3><p>在MySQL中，实际上每条记录在更新的时候都会同时记录一条回滚操作。记录上的最新值，通过回滚操作，都可以得到前一个状态的值。</p><p>假设一个值从1被按顺序改成了2、3、4，在回滚日志里面就会有类似下面的记录。<br><img src="/2019/10/12/[MySQL]1-5/回滚日志记录.png" alt="回滚日志记录"></p><p>假设一个值当前是4，但是在查询这条记录的时候，不同时刻启动的事务会有不同的read-view（视图）。假设在视图A、B、C里面，这一个记录的值分别是1、2、4，同一条记录在系统中可以存在多个版本，就是数据库的多版本并发控制（MVCC）。</p><p>回滚日志不能一直保留，什么时候删除呢？答案是，在不需要的时候才删除。也就是说，系统会判断，当没有事务再需要用到这些回滚日志时，回滚日志会被删除。</p><p>什么时候才不需要了呢？就是当系统里没有比这个回滚日志更早的read-view的时候。</p><h3 id="为什么尽量不使用长事务"><a href="#为什么尽量不使用长事务" class="headerlink" title="为什么尽量不使用长事务"></a>为什么尽量不使用长事务</h3><p>长事务意味着系统里面会存在很老的事务视图。由于这些事务随时可能访问数据库里面的任何数据，所以这个事务提交之前，数据库里面它可能用到的回滚记录都必须保留，这就会导致大量占用存储空间。</p><h3 id="事务的启动方式"><a href="#事务的启动方式" class="headerlink" title="事务的启动方式"></a>事务的启动方式</h3><p>MySQL的事务启动方式有以下几种：</p><ol><li><p>显式启动事务语句， begin 或 start transaction。配套的提交语句是commit，回滚语句是rollback。</p></li><li><p>set autocommit=0，这个命令会将这个线程的自动提交关掉。意味着如果你只执行一个select语句，这个事务就启动了，而且并不会自动提交。这个事务持续存在直到你主动执行commit 或 rollback 语句，或者断开连接。</p></li></ol><p>有些客户端连接框架会默认连接成功后先执行一个set autocommit=0的命令。这就导致接下来的查询都在事务中，如果是长连接，就导致了意外的长事务。</p><p>因此，建议总是使用set autocommit=1, 通过显式语句的方式来启动事务。</p><h2 id="04-深入浅出索引（上）"><a href="#04-深入浅出索引（上）" class="headerlink" title="04 | 深入浅出索引（上）"></a>04 | 深入浅出索引（上）</h2><h3 id="索引的常见模型"><a href="#索引的常见模型" class="headerlink" title="索引的常见模型"></a>索引的常见模型</h3><h4 id="哈希表"><a href="#哈希表" class="headerlink" title="哈希表"></a>哈希表</h4><p><img src="/2019/10/12/[MySQL]1-5/哈希表.png" alt="哈希表"></p><ul><li>添加数据快</li><li>无序，做区间查询速度慢</li><li>适用于只有等值查询的场景</li></ul><h4 id="有序数组"><a href="#有序数组" class="headerlink" title="有序数组"></a>有序数组</h4><p><img src="/2019/10/12/[MySQL]1-5/有序数组.png" alt="有序数组"></p><ul><li>添加数据麻烦，成本高</li><li>在等值查询和范围查询场景中的性能就都非常优秀</li><li>只适用于静态存储引擎</li></ul><h4 id="二叉搜索树"><a href="#二叉搜索树" class="headerlink" title="二叉搜索树"></a>二叉搜索树</h4><p><img src="/2019/10/12/[MySQL]1-5/二叉搜索树.png" alt="二叉搜索树"></p><p>二叉树是搜索效率最高的，但是实际上大多数的数据库存储却并不使用二叉树。其原因是，索引不止存在内存中，还要写到磁盘上。</p><p>为了让一个查询尽量少地读磁盘，就必须让查询过程访问尽量少的数据块。那么，我们就不应该使用二叉树，而是要使用“N叉”树。这里，“N叉”树中的“N”取决于数据块的大小。</p><p>N叉树由于在读写上的性能优点，以及适配磁盘的访问模式，已经被广泛应用在数据库引擎中了。</p><h3 id="InnoDB的索引模型"><a href="#InnoDB的索引模型" class="headerlink" title="InnoDB的索引模型"></a>InnoDB的索引模型</h3><p>在InnoDB中，表都是根据主键顺序以索引的形式存放的，这种存储方式的表称为索引组织表。</p><p>InnoDB使用了B+树索引模型，所以数据都是存储在B+树中的。</p><p>每一个索引在InnoDB里面对应一棵B+树。</p><p>假设，有一个主键列为ID的表，表中有字段k，并且在k上有索引。</p><p><img src="/2019/10/12/[MySQL]1-5/索引组织结构.png" alt="索引组织结构"></p><p>根据叶子节点的内容，索引类型分为<strong>主键索引</strong>和<strong>非主键索引</strong>。</p><ul><li><p>主键索引的叶子节点存的是整行数据。在InnoDB里，主键索引也被称为聚簇索引（clustered index）。</p></li><li><p>非主键索引的叶子节点内容是主键的值。在InnoDB里，非主键索引也被称为二级索引（secondary index）。</p></li></ul><p>基于主键索引和普通索引的查询有什么区别？</p><ul><li><p>如果语句是select * from T where ID=500，即主键查询方式，则只需要搜索ID这棵B+树；</p></li><li><p>如果语句是select * from T where k=5，即普通索引查询方式，则需要先搜索k索引树，得到ID的值为500，再到ID索引树搜索一次。这个过程称为回表。</p></li></ul><p>也就是说，基于非主键索引的查询需要多扫描一棵索引树。因此，我们在应用中应该尽量使用主键查询。</p><h3 id="索引维护"><a href="#索引维护" class="headerlink" title="索引维护"></a>索引维护</h3><p>自增主键的插入数据模式，符合了递增插入的场景。每次插入一条新记录，都是追加操作，都不涉及到挪动其他记录，也不会触发叶子节点的分裂。</p><p>而有业务逻辑的字段做主键，则往往不容易保证有序插入，这样写数据成本相对较高。</p><p>假设表中确实有一个唯一字段，比如字符串类型的身份证号，那应该用身份证号做主键，还是用自增字段做主键呢？</p><p>由于每个非主键索引的叶子节点上都是主键的值。如果用身份证号做主键，那么每个二级索引的叶子节点占用约20个字节，而如果用整型做主键，则只要4个字节，如果是长整型（bigint）则是8个字节。</p><p>显然，<strong>主键长度越小，普通索引的叶子节点就越小，普通索引占用的空间也就越小</strong>。</p><p>所以，从性能和存储空间方面考量，自增主键往往是更合理的选择。</p><h2 id="05-深入浅出索引（下）"><a href="#05-深入浅出索引（下）" class="headerlink" title="05 | 深入浅出索引（下）"></a>05 | 深入浅出索引（下）</h2><h3 id="索引覆盖"><a href="#索引覆盖" class="headerlink" title="索引覆盖"></a>索引覆盖</h3><p>使用</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">select</span> <span class="keyword">ID</span> <span class="keyword">from</span> T <span class="keyword">where</span> k <span class="keyword">between</span> <span class="number">3</span> <span class="keyword">and</span> <span class="number">5</span></span><br></pre></td></tr></table></figure><p>要比</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">select</span> * <span class="keyword">from</span> T <span class="keyword">where</span> k <span class="keyword">between</span> <span class="number">3</span> <span class="keyword">and</span> <span class="number">5</span></span><br></pre></td></tr></table></figure><p>好，因为前者只需要查ID的值，而ID的值已经在k索引树上了，因此可以直接提供查询结果，不需要回表。也就是说，在这个查询里面，索引k已经“覆盖了”我们的查询需求，我们称为覆盖索引。</p><p><strong>由于覆盖索引可以减少树的搜索次数，显著提升查询性能，所以使用覆盖索引是一个常用的性能优化手段。</strong></p><h3 id="最左前缀原则"><a href="#最左前缀原则" class="headerlink" title="最左前缀原则"></a>最左前缀原则</h3><p>现有（name，age）这个联合索引，索引项是按照索引定义里面出现的字段顺序排序的。</p><p><img src="/2019/10/12/[MySQL]1-5/最左前缀.jpg" alt="最左前缀"></p><p>需求是查到所有名字是“张三”的人时，可以快速定位到ID4，然后向后遍历得到所有需要的结果。</p><p>如果要查的是所有名字第一个字是“张”的人，SQL语句的条件是”where name like ‘张%’”。这时，也能够用上这个索引，查找到第一个符合条件的记录是ID3，然后向后遍历，直到不满足条件为止。</p><p>可以看到，不只是索引的全部定义，只要满足最左前缀，就可以利用索引来加速检索。这个最左前缀可以是联合索引的最左N个字段，也可以是字符串索引的最左M个字符。</p><p>在建立联合索引的时候，如何安排索引内的字段顺序？</p><p><strong>第一原则是，如果通过调整顺序，可以少维护一个索引，那么这个顺序往往就是需要优先考虑采用的。</strong></p><p>注意：查询条件里面只有b的语句，是无法使用(a,b)这个联合索引的，这时候你不得不维护另外一个索引，也就是说你需要同时维护(a,b)、(b) 这两个索引。</p><h3 id="索引下推"><a href="#索引下推" class="headerlink" title="索引下推"></a>索引下推</h3><p>现在有一个需求：检索出表中“名字第一个字是张，而且年龄是10岁的所有男孩”</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">select</span> * <span class="keyword">from</span> tuser <span class="keyword">where</span> <span class="keyword">name</span> <span class="keyword">like</span> <span class="string">'张%'</span> <span class="keyword">and</span> age=<span class="number">10</span> <span class="keyword">and</span> ismale=<span class="number">1</span>;</span><br></pre></td></tr></table></figure><p>MySQL 5.6之前，只能从ID3开始一个个回表。到主键索引上找出数据行，再对比字段值。</p><p>MySQL 5.6 引入的索引下推优化（index condition pushdown)， 可以在索引遍历过程中，对索引中包含的字段先做判断，直接过滤掉不满足条件的记录，减少回表次数。</p><p><img src="/2019/10/12/[MySQL]1-5/索引下推.jpg" alt="索引下推"></p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;img src=&quot;/2019/10/12/[MySQL]1-5/封面.jpg&quot; alt=&quot;封面&quot;&gt;&lt;/p&gt;
&lt;p&gt;为啥会有这个系列呢，因为我这人比较功利，学到了东西怕忘了，忘了就等于没学，浪费时间，开这个系列是记录一些概念和知识点，方便以后查阅。文字基本是原文，我只是提炼一些关键的段落。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;资料来源于极客时间林晓斌的《MySQL实战45讲》。&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
      <category term="数据库" scheme="http://yoursite.com/categories/%E6%95%B0%E6%8D%AE%E5%BA%93/"/>
    
    
      <category term="MySQL" scheme="http://yoursite.com/tags/MySQL/"/>
    
  </entry>
  
  <entry>
    <title>设计模式之策略模式和模版方法模式</title>
    <link href="http://yoursite.com/2019/10/12/strategy-template/"/>
    <id>http://yoursite.com/2019/10/12/strategy-template/</id>
    <published>2019-10-12T13:42:21.517Z</published>
    <updated>2019-09-03T12:04:57.859Z</updated>
    
    <content type="html"><![CDATA[<p>今天学两个简单，容易理解又实用的设计模式</p><h3 id="策略模式"><a href="#策略模式" class="headerlink" title="策略模式"></a>策略模式</h3><p><strong>描述：定义一个算法的系列，将其各个分装，并且使他们有交互性。策略模式使得算法在用户使用的时候能独立的改变。</strong></p><p><img src="/2019/10/12/strategy-template/策略模式.png" alt="策略模式"><br><a id="more"></a></p><p>什么时候用策略模式？<br>策略模式是用来封装算法的，从概念上来看，所有这些算法完成的都是相同的工作，只是实现不同，它可以以相同的方式调用所有的算法，减少了算法类和使用算法类之间的耦合。在实践中，我们发现可以用它封装几乎任何类型的规则，只要在分析过程中听到需要在不同时机应用不同的业务规则，就可以用策略模式处理这种变化的可能性。</p><p>策略模式的3个角色</p><ul><li><p>Context（环境类）：环境类是使用算法的角色，它在解决某个问题（即实现某个方法）时可以采用多种策略。在环境类中维持一个对抽象策略类的引用实例，用于定义所采用的策略。</p></li><li><p>Strategy（抽象策略类）：它为所支持的算法声明了抽象方法，是所有策略类的父类，它可以是抽象类或具体类，也可以是接口。环境类通过抽象策略类中声明的方法在运行时调用具体策略类中实现的算法。</p></li><li><p>ConcreteStrategy（具体策略类）：它实现了在抽象策略类中声明的算法，在运行时，具体策略类将覆盖在环境类中定义的抽象策略类对象，使用一种具体的算法实现某个业务处理。</p></li></ul><p>策略模式实现起来也十分简单，随便写个代码示范就好</p><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">//Strategy</span></span><br><span class="line"><span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">FastFood</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title">getFood</span><span class="params">()</span></span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//ConcreteStrategy</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">KFC</span> <span class="keyword">extends</span> <span class="title">FastFood</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">getFood</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"点开封菜！"</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="class"><span class="keyword">class</span> <span class="title">McDonalds</span> <span class="keyword">extends</span> <span class="title">FastFood</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">getFood</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"点金拱门！"</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">//Context</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">TakeawayPlatform</span> </span>&#123;</span><br><span class="line">    FastFood fastFood;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">TakeawayPlatform</span><span class="params">(FastFood fastFood)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.fastFood = fastFood;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">getFood</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        fastFood.getFood();</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="class"><span class="keyword">class</span> <span class="title">TakeawayFactory</span> </span>&#123;</span><br><span class="line">    FastFood fastFood = <span class="keyword">null</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> String KFC = <span class="string">"KFC"</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> String McDonalds = <span class="string">"McDonalds"</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">TakeawayFactory</span><span class="params">(String type)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">switch</span> (type) &#123;</span><br><span class="line">            <span class="keyword">case</span> KFC:</span><br><span class="line">                fastFood = <span class="keyword">new</span> KFC();</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            <span class="keyword">case</span> McDonalds:</span><br><span class="line">                fastFood = <span class="keyword">new</span> McDonalds();</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><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">getFood</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        fastFood.getFood();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>主程序</p><figure class="highlight java"><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="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Strategy</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        TakeawayPlatform takeawayKFC = <span class="keyword">new</span> TakeawayPlatform(<span class="keyword">new</span> KFC());</span><br><span class="line">        takeawayKFC.getFood();</span><br><span class="line"></span><br><span class="line">        TakeawayPlatform takeawayMCD = <span class="keyword">new</span> TakeawayPlatform(<span class="keyword">new</span> McDonalds());</span><br><span class="line">        takeawayMCD.getFood();</span><br><span class="line"></span><br><span class="line">        TakeawayFactory takeawayFactory = <span class="keyword">new</span> TakeawayFactory(<span class="string">"KFC"</span>);</span><br><span class="line">        takeawayFactory.getFood();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在基本策略模式中，选择ConcreteStrategy的职责由客户端承担，并转给Context对象。这并没有解除客户端需要选择判断的压力，而策略模式和简单工厂模式结合后，选择的职责也可以由Context承担，这最大化减轻了客户端的职责。</p><h3 id="模版方法模式"><a href="#模版方法模式" class="headerlink" title="模版方法模式"></a>模版方法模式</h3><p><strong>描述：模板方法模式准备一个抽象类，将部分逻辑以具体方法及具体构造子类的形式实现，然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法，从而对剩余的逻辑有不同的实现。先构建一个顶级逻辑框架，而将逻辑的细节留给具体的子类去实现。</strong></p><p><img src="/2019/10/12/strategy-template/模版方法模式.png" alt="模版方法模式"></p><p>什么时候用模版方法模式？<br>模版方法模式提供了很好的代码复用平台，当遇到一些列步骤构成的过程需要执行，这些过程从高层次上看是相似的，只是有些具体步骤实现可能不同，这时可以考虑使用模版方法模式。</p><p>模版方法模式的2个角色</p><ul><li><p>AbstractClass（抽象类）：在抽象类中定义了一系列基本操作(PrimitiveOperations)，这些基本操作可以是具体的，也可以是抽象的，每一个基本操作对应算法的一个步骤，在其子类中可以重定义或实现这些步骤。同时，在抽象类中实现了一个模板方法(Template Method)，用于定义一个算法的框架，模板方法不仅可以调用在抽象类中实现的基本方法，也可以调用在抽象类的子类中实现的基本方法，还可以调用其他对象中的方法。</p></li><li><p>ConcreteClass（具体子类）：它是抽象类的子类，用于实现在父类中声明的抽象基本操作以完成子类特定算法的步骤，也可以覆盖在父类中已经实现的具体基本操作。</p></li></ul><p>一个模板方法是定义在抽象类中的、把基本操作方法组合在一起形成一个总算法或一个总行为的方法。这个模板方法定义在抽象类中，并由子类不加以修改地完全继承下来。模板方法是一个具体方法，它给出了一个顶层逻辑框架，而逻辑的组成步骤在抽象类中可以是具体方法，也可以是抽象方法。</p><p>基本方法是实现算法各个步骤的方法，是模板方法的组成部分。基本方法又可以分为三种：抽象方法(Abstract Method)、具体方法(Concrete Method)和钩子方法(Hook Method)。</p><ul><li>抽象方法：一个抽象方法由抽象类声明、由其具体子类实现。</li><li>具体方法：一个具体方法由一个抽象类或具体类声明并实现，其子类可以进行覆盖也可以直接继承。</li><li>钩子方法：可以与一些具体步骤 “挂钩” ，以实现在不同条件下执行模板方法中的不同步骤</li></ul><p>做奶茶的基本工序大致是相同的，加奶加茶，顾客选择加料加冰甜度等等，最后塑封。用代码演示如下</p><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">//AbstractClass</span></span><br><span class="line"><span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">MilkTea</span> </span>&#123;</span><br><span class="line">    <span class="comment">//模版方法，注意要加final让子类不能改写</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title">makeMilkTea</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        addMilk();</span><br><span class="line">        addTea();</span><br><span class="line">        <span class="keyword">if</span> (needIce()) &#123;</span><br><span class="line">            addIce();</span><br><span class="line">        &#125;</span><br><span class="line">        addStuff();</span><br><span class="line">        packUp();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">final</span> <span class="keyword">void</span> <span class="title">addMilk</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"添加牛奶"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">final</span> <span class="keyword">void</span> <span class="title">addTea</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"添加茶汁"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">final</span> <span class="keyword">void</span> <span class="title">addIce</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"添加冰块"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">final</span> <span class="keyword">void</span> <span class="title">packUp</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"塑封，完成！"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">boolean</span> <span class="title">needIce</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title">addStuff</span><span class="params">()</span></span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//ConcreteClass</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">PearlMilkTea</span> <span class="keyword">extends</span> <span class="title">MilkTea</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">addStuff</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"加珍珠"</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="class"><span class="keyword">class</span> <span class="title">RedBeanMilkTeaWithIce</span> <span class="keyword">extends</span> <span class="title">MilkTea</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">boolean</span> <span class="title">needIce</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">addStuff</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"加红豆"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>主程序</p><figure class="highlight java"><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="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TemplateMethod</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"-----开始制作珍珠奶茶-----"</span>);</span><br><span class="line">        MilkTea pearl = <span class="keyword">new</span> PearlMilkTea();</span><br><span class="line">        pearl.makeMilkTea();</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">"-----开始制作红豆奶茶加冰-----"</span>);</span><br><span class="line">        MilkTea redBean = <span class="keyword">new</span> RedBeanMilkTeaWithIce();</span><br><span class="line">        redBean.makeMilkTea();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>当不变的和可变的行为在方法的子类实现混合在一起时，不变的行为就会在子类中重复出现。通过模版方法模式把不变的行为搬到单一的地方，这样帮助子类摆脱重复不变行为的纠缠</p><p>参考资料：</p><ul><li><a href="https://blog.csdn.net/wwwdc1012/article/details/83152856" target="_blank" rel="noopener">https://blog.csdn.net/wwwdc1012/article/details/83152856</a></li><li><a href="https://blog.csdn.net/wwwdc1012/article/details/83005717" target="_blank" rel="noopener">https://blog.csdn.net/wwwdc1012/article/details/83005717</a></li><li>《大话设计模式》</li></ul>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;今天学两个简单，容易理解又实用的设计模式&lt;/p&gt;
&lt;h3 id=&quot;策略模式&quot;&gt;&lt;a href=&quot;#策略模式&quot; class=&quot;headerlink&quot; title=&quot;策略模式&quot;&gt;&lt;/a&gt;策略模式&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;描述：定义一个算法的系列，将其各个分装，并且使他们有交互性。策略模式使得算法在用户使用的时候能独立的改变。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/2019/10/12/strategy-template/策略模式.png&quot; alt=&quot;策略模式&quot;&gt;&lt;br&gt;&lt;/p&gt;
    
    </summary>
    
      <category term="设计模式" scheme="http://yoursite.com/categories/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
    
    
      <category term="DesignPatterns" scheme="http://yoursite.com/tags/DesignPatterns/"/>
    
  </entry>
  
  <entry>
    <title>设计模式之状态模式、备忘录模式和观察者模式</title>
    <link href="http://yoursite.com/2019/10/12/state-memento-observer/"/>
    <id>http://yoursite.com/2019/10/12/state-memento-observer/</id>
    <published>2019-10-12T13:42:21.487Z</published>
    <updated>2019-07-28T12:07:31.879Z</updated>
    
    <content type="html"><![CDATA[<p>还剩下最后3个模式了，学完了之后设计模式的笔记就告一段落了。与其说是“写”博客，不如说是记录笔记，学完一遍设计模式，<br>知道各个模式大概什么样子，以后碰到了再回来翻翻笔记，就很舒服。备战春招的时候再来二刷吧。</p><h3 id="状态模式"><a href="#状态模式" class="headerlink" title="状态模式"></a>状态模式</h3><p><strong>描述：让一个对象在其内部状态改变的时候，其行为也随之改变。状态模式需要对每一个系统可能获取的状态创立一个状态类的子类。当系统的状态变化时，系统便改变所选的子类。</strong></p><p><img src="/2019/10/12/state-memento-observer/状态模式.png" alt="状态模式"><br><a id="more"></a></p><p>什么时候用状态模式？<br>当一个对象的行为取决于它的状态，且它必须在运行时刻根据状态改变它的行为时，就考虑用状态模式。状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂的情况。把状态的判断逻辑转移到表示不同状态的一系列类中，可以把复杂的判断逻辑简化。</p><p>状态模式的3个角色</p><ul><li><p>Context（环境角色）: 它定义了客户程序需要的接口并维护一个具体状态角色的实例，将与状态相关的操作委托给当前的具体状态对象来处理。</p></li><li><p>State（抽象状态）: 定义一个接口以封装使用上下文环境的的一个特定状态相关的行为。</p></li><li><p>ConcreteState（具体状态）:实现抽象状态定义的接口。</p></li></ul><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">//State</span></span><br><span class="line"><span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">Period</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title">eat</span><span class="params">(MealTime mealTime)</span></span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//ConcreteState</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Morning</span> <span class="keyword">extends</span> <span class="title">Period</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">eat</span><span class="params">(MealTime mealTime)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (mealTime.hour &gt; <span class="number">6</span> &amp;&amp; mealTime.hour &lt;= <span class="number">9</span>) &#123;</span><br><span class="line">            System.out.println(<span class="string">"现在是早上，可以吃包子喝豆浆"</span>);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            mealTime.setCurrent(<span class="keyword">new</span> Noon());</span><br><span class="line">            mealTime.eat();</span><br><span class="line">        &#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="class"><span class="keyword">class</span> <span class="title">Noon</span> <span class="keyword">extends</span> <span class="title">Period</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">eat</span><span class="params">(MealTime mealTime)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (mealTime.hour &gt; <span class="number">9</span> &amp;&amp; mealTime.hour &lt;= <span class="number">13</span>) &#123;</span><br><span class="line">            System.out.println(<span class="string">"快到中午了，可以吃大碗宽面"</span>);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            mealTime.setCurrent(<span class="keyword">new</span> Afternoon());</span><br><span class="line">            mealTime.eat();</span><br><span class="line">        &#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="class"><span class="keyword">class</span> <span class="title">Afternoon</span> <span class="keyword">extends</span> <span class="title">Period</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">eat</span><span class="params">(MealTime mealTime)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (mealTime.hour &gt; <span class="number">13</span> &amp;&amp; mealTime.hour &lt;= <span class="number">17</span>) &#123;</span><br><span class="line">            System.out.println(<span class="string">"现在是下午，给阿姨倒杯卡布奇诺"</span>);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            mealTime.setCurrent(<span class="keyword">new</span> Evening());</span><br><span class="line">            mealTime.eat();</span><br><span class="line">        &#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="class"><span class="keyword">class</span> <span class="title">Evening</span> <span class="keyword">extends</span> <span class="title">Period</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">eat</span><span class="params">(MealTime mealTime)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (mealTime.hour &gt; <span class="number">17</span> &amp;&amp; mealTime.hour &lt;= <span class="number">22</span>) &#123;</span><br><span class="line">            System.out.println(<span class="string">"现在是晚上，可以吃葱油拌面"</span>);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            System.out.println(<span class="string">"想吃夜宵？不怕长膘？"</span>);</span><br><span class="line">        &#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">//Context</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MealTime</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> Period current;</span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">double</span> hour;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">MealTime</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        current = <span class="keyword">new</span> Morning();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setCurrent</span><span class="params">(Period current)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.current = current;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">double</span> <span class="title">getHour</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> hour;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setHour</span><span class="params">(<span class="keyword">double</span> hour)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.hour = hour;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">eat</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        current.eat(<span class="keyword">this</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>主程序</p><figure class="highlight java"><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="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">State</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        MealTime mealTime = <span class="keyword">new</span> MealTime();</span><br><span class="line">        mealTime.setHour(<span class="number">7</span>);</span><br><span class="line">        mealTime.eat();</span><br><span class="line">        mealTime.setHour(<span class="number">10</span>);</span><br><span class="line">        mealTime.eat();</span><br><span class="line">        mealTime.setHour(<span class="number">15</span>);</span><br><span class="line">        mealTime.eat();</span><br><span class="line">        mealTime.setHour(<span class="number">18</span>);</span><br><span class="line">        mealTime.eat();</span><br><span class="line">        mealTime.setHour(<span class="number">23</span>);</span><br><span class="line">        mealTime.eat();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>可以看到，客户端只更改了状态的某一参数，就有不同的行为。调用具体方法eat()的时候，是看不到有选择的过程的，避免了大量的if else判断。</p><p>状态模式的好处<br>将与特定状态相关的行为局部化，并且将不同状态的行为分割开。可以消除庞大的分支语句，通过定义新的子类可以很容易地增加新的状态和转换。</p><h3 id="备忘录模式"><a href="#备忘录模式" class="headerlink" title="备忘录模式"></a>备忘录模式</h3><p><strong>描述：备忘录对象是一个用来存储另外一个对象内部状态的快照的对象。备忘录模式的用意是在不破坏封装的条件下，将一个对象的状态捉住，并外部化，存储起来，从而可以在将来合适的时候把这个对象还原到存储起来的状态。</strong></p><p><img src="/2019/10/12/state-memento-observer/备忘录模式.png" alt="备忘录模式"></p><p>什么时候用备忘录模式？<br>备忘录模式适用于功能比较复杂的，但需要维护或记录属性历史的类，或者需要保存的属性只是众多属性的小部分时。</p><p>tips: 在某个系统使用命令模式时，需要实现命令的撤销功能，可以使用备忘录模式来存储可撤销操作的状态</p><p>备忘录模式的3个角色</p><ul><li><p>Originator（原发器）：它是一个普通类，可以创建一个备忘录，并存储它的当前内部状态，也可以使用备忘录来恢复其内部状态，一般将需要保存内部状态的类设计为原发器。</p></li><li><p>Memento（备忘录)：存储原发器的内部状态，根据原发器来决定保存哪些内部状态。备忘录的设计一般可以参考原发器的设计，根据实际需要确定备忘录类中的属性。需要注意的是，<strong>除了原发器本身与负责人类之外，备忘录对象不能直接供其他类使用</strong>，原发器的设计在不同的编程语言中实现机制会有所不同。</p></li><li><p>Caretaker（负责人）：负责人又称为管理者，它负责保存备忘录，但是不能对备忘录的内容进行操作或检查。在负责人类中可以存储一个或多个备忘录对象，它只负责存储对象，而不能修改对象，也无须知道对象的实现细节。</p></li></ul><p>备忘录模式的核心是备忘录类以及用于管理备忘录的负责人类的设计。</p><p>用备忘录模式模拟下棋时的悔棋操作</p><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">//Originator</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Chessman</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> String label;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> x;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> y;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Chessman</span><span class="params">(String label, <span class="keyword">int</span> x, <span class="keyword">int</span> y)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.label = label;</span><br><span class="line">        <span class="keyword">this</span>.x = x;</span><br><span class="line">        <span class="keyword">this</span>.y = y;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setPosition</span><span class="params">(<span class="keyword">int</span> x, <span class="keyword">int</span> y)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.x = x;</span><br><span class="line">        <span class="keyword">this</span>.y = y;</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">public</span> ChessmanMemento <span class="title">save</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> ChessmanMemento(label, x, y);</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">public</span> <span class="keyword">void</span> <span class="title">restore</span><span class="params">(ChessmanMemento memento)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.label = memento.getLabel();</span><br><span class="line">        <span class="keyword">this</span>.x = memento.getX();</span><br><span class="line">        <span class="keyword">this</span>.y = memento.getY();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">show</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(String.format(<span class="string">"棋子&lt;%s&gt;：当前位置为：&lt;%d, %d&gt;"</span>, label, x, y));</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">//Memento</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">ChessmanMemento</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> String label;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> x;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> y;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">ChessmanMemento</span><span class="params">(String label, <span class="keyword">int</span> x, <span class="keyword">int</span> y)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.label = label;</span><br><span class="line">        <span class="keyword">this</span>.x = x;</span><br><span class="line">        <span class="keyword">this</span>.y = y;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">getLabel</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> label;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getX</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> x;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getY</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> y;</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">//Caretaker</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MementoCaretaker</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> ChessmanMemento memento;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> ChessmanMemento <span class="title">getMemento</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> memento;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setMemento</span><span class="params">(ChessmanMemento memento)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.memento = memento;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>主程序</p><figure class="highlight java"><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="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Memento</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        Chessman chessman = <span class="keyword">new</span> Chessman(<span class="string">"黑子"</span>, <span class="number">1</span>, <span class="number">1</span>);</span><br><span class="line">        chessman.show();</span><br><span class="line"></span><br><span class="line">        MementoCaretaker caretaker = <span class="keyword">new</span> MementoCaretaker();</span><br><span class="line">        caretaker.setMemento(chessman.save());</span><br><span class="line"></span><br><span class="line">        chessman.setPosition(<span class="number">2</span>, <span class="number">3</span>);</span><br><span class="line">        chessman.show();</span><br><span class="line"></span><br><span class="line">        chessman.restore(caretaker.getMemento());</span><br><span class="line">        chessman.show();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="观察者模式"><a href="#观察者模式" class="headerlink" title="观察者模式"></a>观察者模式</h3><p><strong>描述：在对象间定义一个一对多的联系性，由此当一个对象改变了状态，所有其他相关的对象会被通知并且自动刷新。</strong></p><p><img src="/2019/10/12/state-memento-observer/观察者模式.png" alt="观察者模式"></p><p>观察者模式的动机<br>将一个系统分割成一系列相互协作的类有很不好的副作用，即需要维护相关对象间的一致性。我们不希望为了维持一致性而使各类紧密耦合，这样会给维护，扩展和重用都带来不便。而观察者模式的关键对象是Subject和Observer，一个Subject可以有任意数目依赖它的Observer，一旦Subject的状态发生变化，所有的Observer都可以得到通知。Subject发出通知时不需要知道它的观察者是谁，任何一个具体观察者也不需要知道其他观察者存在。</p><p>什么时候用观察者模式？<br>当一个对象的改变需要同时改变其他对象，且不知道具体有多少对象有待改变时；一个抽象模型有两个方面，其中一方面依赖于另外一方面，这时用观察者模式可以将这两者封装在独立的对象中使它们各自独立改变和复用。</p><p>观察者模式的4个角色  </p><ul><li><p>Subject（目标）：目标又称为主题，它是指被观察的对象。在目标中定义了一个观察者集合，一个观察目标可以接受任意数量的观察者来观察，它提供一系列方法来增加和删除观察者对象，同时它定义了通知方法notify()。目标类可以是接口，也可以是抽象类或具体类。</p></li><li><p>ConcreteSubject（具体目标）：具体目标是目标类的子类，通常它包含有经常发生改变的数据，当它的状态发生改变时，向它的各个观察者发出通知；同时它还实现了在目标类中定义的抽象业务逻辑方法（如果有的话）。如果无须扩展目标类，则具体目标类可以省略。</p></li><li><p>Observer（观察者）：观察者将对观察目标的改变做出反应，观察者一般定义为接口，该接口声明了更新数据的方法update()，因此又称为抽象观察者。</p></li><li><p>ConcreteObserver（具体观察者）：在具体观察者中维护一个指向具体目标对象的引用，它存储具体观察者的有关状态，这些状态需要和具体目标的状态保持一致；它实现了在抽象观察者Observer中定义的update()方法。通常在实现时，可以调用具体目标类的attach()方法将自己添加到目标类的集合中或通过detach()方法将自己从目标类的集合中删除。</p></li></ul><p>举个微信公众号推送的例子，非常契合</p><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">//Observer</span></span><br><span class="line"><span class="class"><span class="keyword">interface</span> <span class="title">Subscriber</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">receive</span><span class="params">(String publisher, String articleName)</span></span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//ConcreteObserver</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">WeChatClient</span> <span class="keyword">implements</span> <span class="title">Subscriber</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> String username;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">WeChatClient</span><span class="params">(String username)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.username = username;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">receive</span><span class="params">(String publisher, String articleName)</span> </span>&#123;</span><br><span class="line">        System.out.println(String.format(<span class="string">"用户&lt;%s&gt; 接收到 &lt;%s&gt;微信公众号 的推送，文章标题为 &lt;%s&gt;"</span>, username, publisher, articleName));</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">//Subject</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Publisher</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> List&lt;Subscriber&gt; subscribers;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">boolean</span> pubStatus = <span class="keyword">false</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Publisher</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        subscribers = <span class="keyword">new</span> ArrayList&lt;&gt;();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">subscribe</span><span class="params">(Subscriber subscriber)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.subscribers.add(subscriber);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">unsubscribe</span><span class="params">(Subscriber subscriber)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (<span class="keyword">this</span>.subscribers.contains(subscriber)) &#123;</span><br><span class="line">            <span class="keyword">this</span>.subscribers.remove(subscriber);</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">public</span> <span class="keyword">void</span> <span class="title">notifySubscribers</span><span class="params">(String publisher, String articleName)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (!<span class="keyword">this</span>.pubStatus) &#123;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">for</span> (Subscriber subscriber : <span class="keyword">this</span>.subscribers) &#123;</span><br><span class="line">            subscriber.receive(publisher, articleName);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">this</span>.clearPubStatus();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">setPubStatus</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.pubStatus = <span class="keyword">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">clearPubStatus</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.pubStatus = <span class="keyword">false</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">//ConcreteSubject</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">WeChatAccounts</span> <span class="keyword">extends</span> <span class="title">Publisher</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">WeChatAccounts</span><span class="params">(String name)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.name = name;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">publishArticles</span><span class="params">(String articleName, String content)</span> </span>&#123;</span><br><span class="line">        System.out.println(String.format(<span class="string">"\n&lt;%s&gt;微信公众号 发布了一篇推送，文章名称为 &lt;%s&gt;，内容为 &lt;%s&gt; "</span>, <span class="keyword">this</span>.name, articleName, content));</span><br><span class="line">        setPubStatus();</span><br><span class="line">        notifySubscribers(<span class="keyword">this</span>.name, articleName);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>主程序</p><figure class="highlight java"><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">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Observer</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        WeChatAccounts accounts = <span class="keyword">new</span> WeChatAccounts(<span class="string">"X博士"</span>);</span><br><span class="line"></span><br><span class="line">        WeChatClient user1 = <span class="keyword">new</span> WeChatClient(<span class="string">"ayahiro"</span>);</span><br><span class="line">        WeChatClient user2 = <span class="keyword">new</span> WeChatClient(<span class="string">"bxy0516"</span>);</span><br><span class="line">        WeChatClient user3 = <span class="keyword">new</span> WeChatClient(<span class="string">"tom"</span>);</span><br><span class="line"></span><br><span class="line">        accounts.subscribe(user1);</span><br><span class="line">        accounts.subscribe(user2);</span><br><span class="line">        accounts.subscribe(user3);</span><br><span class="line"></span><br><span class="line">        accounts.publishArticles(<span class="string">"article1"</span>, <span class="string">"something1"</span>);</span><br><span class="line"></span><br><span class="line">        accounts.unsubscribe(user1);</span><br><span class="line">        accounts.publishArticles(<span class="string">"article2"</span>, <span class="string">"something2"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>总的来说，观察者模式所做的工作是在解除耦合，让耦合的双方都依赖于抽象，而不是依赖于具体，是依赖倒转原则的最佳体现。</p><p>参考资料：</p><ul><li><a href="https://blog.csdn.net/wwwdc1012/article/details/83353580" target="_blank" rel="noopener">https://blog.csdn.net/wwwdc1012/article/details/83353580</a></li><li><a href="https://blog.csdn.net/wwwdc1012/article/details/83317973" target="_blank" rel="noopener">https://blog.csdn.net/wwwdc1012/article/details/83317973</a></li><li><a href="https://xuwujing.blog.csdn.net/article/details/83589031" target="_blank" rel="noopener">https://xuwujing.blog.csdn.net/article/details/83589031</a></li><li>《大话设计模式》</li></ul>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;还剩下最后3个模式了，学完了之后设计模式的笔记就告一段落了。与其说是“写”博客，不如说是记录笔记，学完一遍设计模式，&lt;br&gt;知道各个模式大概什么样子，以后碰到了再回来翻翻笔记，就很舒服。备战春招的时候再来二刷吧。&lt;/p&gt;
&lt;h3 id=&quot;状态模式&quot;&gt;&lt;a href=&quot;#状态模式&quot; class=&quot;headerlink&quot; title=&quot;状态模式&quot;&gt;&lt;/a&gt;状态模式&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;描述：让一个对象在其内部状态改变的时候，其行为也随之改变。状态模式需要对每一个系统可能获取的状态创立一个状态类的子类。当系统的状态变化时，系统便改变所选的子类。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/2019/10/12/state-memento-observer/状态模式.png&quot; alt=&quot;状态模式&quot;&gt;&lt;br&gt;&lt;/p&gt;
    
    </summary>
    
      <category term="设计模式" scheme="http://yoursite.com/categories/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
    
    
      <category term="DesignPatterns" scheme="http://yoursite.com/tags/DesignPatterns/"/>
    
  </entry>
  
  <entry>
    <title>SpringBoot使用slf4j+logback配合AOP做日志记录</title>
    <link href="http://yoursite.com/2019/10/12/SpringBoot_logger_AOP/"/>
    <id>http://yoursite.com/2019/10/12/SpringBoot_logger_AOP/</id>
    <published>2019-10-12T13:42:21.467Z</published>
    <updated>2019-09-03T07:03:08.853Z</updated>
    
    <content type="html"><![CDATA[<p>需要大致了解：java日志基础，如核心组件Loggers,Appenders,Layouts的用处、SpringAOP概念</p><h3 id="为什么需要日志"><a href="#为什么需要日志" class="headerlink" title="为什么需要日志"></a>为什么需要日志</h3><p>当应用程序部署到服务器上运行时，用户在使用过程中可能会出现各种错误。这时应用程序将错误信息生成日志，就方便了开发人员快速定位错误和根源，从而进行有针对的维护。所以，在大型应用程序中，日志记录是必不可少的。<br><a id="more"></a></p><h3 id="选择日志框架"><a href="#选择日志框架" class="headerlink" title="选择日志框架"></a>选择日志框架</h3><p>目前市面上可供选择的日志框架非常多，如JCL、SLF4J、Jboss-logging、jUL、log4j、log4j2、logback等，首先要分清楚 [日志抽象层] 和 [日志实现]。   这两者的关系可以参考设计模式中的“门面模式”。  我们在开发中调用日志记录方法时，不应直接调用日志实现类的方法，而是调用日志抽象层的方法。这样方便解耦，以后想更换别的日志实现时，可以直接改动配置文件的信息，<strong>而不用修改一行代码</strong>。 那么如何选择日志框架呢？</p><ul><li>日志抽象层：JCL（Jakarta Commons Logging), SLF4j（Simple Logging Facade for Java）, jboss-logging</li><li>日志实现：Log4j, JUL（java.util.logging）, Log4j2,  Logback</li></ul><p>关于如何选择网络上有很多文章分析，在此不赘述。结论就是SLF4J更受开发者青睐，事实上《阿里java开发手册》上也规定：<em>应用中不可直接使用日志系统（Log4j、Logback）中的API，而应依赖使用日志框架<br>SLF4J中的API，使用门面模式的日志框架，有利于维护和各个类的日志处理方式统一。</em><br>至于选择日志实现，log4j是很常用的，但其作者又写了log4j的升级版logback，相比log4j有更好的性能。有诸多理由让我们选择logback，使用好logback关键的一点就是配置好logback.xml文件，可参阅<a href="https://www.cnblogs.com/warking/p/5710303.html" title="logback使用和配置详解" target="_blank" rel="noopener">logback使用和配置详解</a></p><h4 id="maven引入"><a href="#maven引入" class="headerlink" title="maven引入"></a>maven引入</h4><figure class="highlight xml"><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="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">groupId</span>&gt;</span>ch.qos.logback<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>logback-classic<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.slf4j<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>jcl-over-slf4j<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure><p><strong>SpringBoot已默认使用slf4j和logback  无需引入对应依赖。</strong></p><h3 id="如何插入日志记录"><a href="#如何插入日志记录" class="headerlink" title="如何插入日志记录"></a>如何插入日志记录</h3><p>使用SpringAOP，目的是让开发者专注于业务逻辑而无需关心在哪里插入日志，并且可以降低日志记录操作对业务代码的侵入性。<br>这里我们使用 AspectJ 的几个注解来写一个切面类TestAspect.java</p><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> com.example.demo.annotation.RequestColor;</span><br><span class="line"><span class="keyword">import</span> org.aspectj.lang.JoinPoint;</span><br><span class="line"><span class="keyword">import</span> org.aspectj.lang.ProceedingJoinPoint;</span><br><span class="line"><span class="keyword">import</span> org.aspectj.lang.annotation.*;</span><br><span class="line"><span class="keyword">import</span> org.slf4j.Logger;</span><br><span class="line"><span class="keyword">import</span> org.slf4j.LoggerFactory;</span><br><span class="line"><span class="keyword">import</span> org.springframework.stereotype.Component;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Aspect</span></span><br><span class="line"><span class="meta">@Component</span>(<span class="string">"testAspect"</span>)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TestAspect</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> Logger logger = LoggerFactory.getLogger(TestAspect.class); </span><br><span class="line">    <span class="comment">//controller包切点</span></span><br><span class="line">    <span class="meta">@Pointcut</span>(<span class="string">"execution(* com.example.demo.controller.*.*(..))"</span>)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">controllerPointCut</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//TestController切点</span></span><br><span class="line">    <span class="meta">@Pointcut</span>(<span class="string">"execution(* com.example.demo.controller.TestController.*(..))"</span>)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">testControllerPointCut</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//具体方法 ayahiro 切点</span></span><br><span class="line">    <span class="meta">@Pointcut</span>(<span class="string">"execution(* com.example.demo.controller.TestController.ayahiro()))"</span>)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">ayahiroPointCut</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Before</span>(value = <span class="string">"testControllerPointCut()"</span>)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">doBefore</span><span class="params">(JoinPoint joinPoint)</span></span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"form: TestAspect----&gt;&gt;"</span>);</span><br><span class="line">        String className=joinPoint.getSignature().getDeclaringTypeName();</span><br><span class="line">        String methodName=joinPoint.getSignature().getName();</span><br><span class="line">        System.out.println(<span class="string">"doBefore拦截了"</span>+className+<span class="string">"."</span>+methodName);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@After</span>(value = <span class="string">"@annotation(requestColor)"</span>)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">doAfter</span><span class="params">(JoinPoint joinPoint, <span class="keyword">final</span> RequestColor requestColor)</span></span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"form: TestAspect----&gt;&gt;"</span>);</span><br><span class="line">        String className=joinPoint.getSignature().getDeclaringTypeName();</span><br><span class="line">        String methodName=joinPoint.getSignature().getName();</span><br><span class="line">        System.out.println(<span class="string">"doAfter拦截了"</span>+className+<span class="string">"."</span>+methodName);</span><br><span class="line">        System.out.println(<span class="string">"requestType: "</span>+ requestColor.type());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Around</span>(value = <span class="string">"ayahiroPointCut()"</span>)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> Object <span class="title">doAround</span><span class="params">(ProceedingJoinPoint joinPoint)</span> <span class="keyword">throws</span> Throwable</span>&#123;</span><br><span class="line">        Object re=joinPoint.proceed();  <span class="comment">//执行了ayahiro方法  返回了String</span></span><br><span class="line">        System.out.println(re);</span><br><span class="line">        System.out.println(<span class="string">"form: TestAspect----&gt;&gt;"</span>);</span><br><span class="line">        String className=joinPoint.getSignature().getDeclaringTypeName();</span><br><span class="line">        String methodName=joinPoint.getSignature().getName();</span><br><span class="line">        System.out.println(<span class="string">"doAround拦截了"</span>+className+<span class="string">"."</span>+methodName);</span><br><span class="line">        <span class="keyword">return</span> re;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@AfterReturning</span>(value = <span class="string">"testControllerPointCut()"</span>)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">doAfterReturning</span><span class="params">(JoinPoint joinPoint)</span></span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"form: TestAspect----&gt;&gt;"</span>);</span><br><span class="line">        String className=joinPoint.getSignature().getDeclaringTypeName();</span><br><span class="line">        String methodName=joinPoint.getSignature().getName();</span><br><span class="line">        System.out.println(<span class="string">"doAfterReturning拦截了"</span>+className+<span class="string">"."</span>+methodName);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@AfterThrowing</span>(value = <span class="string">"testControllerPointCut()"</span>)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">doAfterThrowing</span><span class="params">(JoinPoint joinPoint)</span></span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"form: TestAspect----&gt;&gt;"</span>);</span><br><span class="line">        String className=joinPoint.getSignature().getDeclaringTypeName();</span><br><span class="line">        String methodName=joinPoint.getSignature().getName();</span><br><span class="line">        System.out.println(<span class="string">"doAfterThrowing拦截了"</span>+className+<span class="string">"."</span>+methodName);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>以及Controller，有两个返回字符串的测试方法</p><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> com.example.demo.annotation.RequestColor;</span><br><span class="line"><span class="keyword">import</span> org.springframework.web.bind.annotation.RequestMapping;</span><br><span class="line"><span class="keyword">import</span> org.springframework.web.bind.annotation.RequestMethod;</span><br><span class="line"><span class="keyword">import</span> org.springframework.web.bind.annotation.RestController;</span><br><span class="line"></span><br><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TestController</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@RequestColor</span>(type = RequestColor.Type.YELLOW)</span><br><span class="line">    <span class="meta">@RequestMapping</span>(path = &#123;<span class="string">"/ayahiro"</span>&#125;,method = &#123;RequestMethod.GET&#125;)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">ayahiro</span><span class="params">()</span> <span class="keyword">throws</span> Exception</span>&#123;</span><br><span class="line">        <span class="comment">//int num=2/0;</span></span><br><span class="line">        <span class="keyword">return</span> <span class="string">"this ayahiro"</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@RequestMapping</span>(path = &#123;<span class="string">"/moonKa"</span>&#125;,method = &#123;RequestMethod.GET&#125;)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">moonKa</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">"this moonKa"</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="介绍几个常用的注解"><a href="#介绍几个常用的注解" class="headerlink" title="介绍几个常用的注解"></a>介绍几个常用的注解</h3><ul><li>@Aspect 表明这个类是“切面类”，切面类就是用来定义切点和切点处要增强功能的方法</li><li>@Pointcut 这个注解包含两部分，PointCut表达式和PointCut签名。表达式是用来确定切入点的位置的，说白了就是通过一些规则来确定，哪些方法是要增强的，也就是要拦截哪些方法。注解括号里的部分就是描述切点的位置，有很多种方法来确定，代码中使用的execution表达式是其中的一种，其语法和其他描述方法可自行百度。 签名就是被注解的方法名，签名没有实际用处，只是用来标记一个Pointcut，可以理解成这个切入点的一个记号。</li><li>@Before 顾名思义，即在切入点处方法执行前，执行此方法。同下面的@After，@Around，@AfterReturning， @AfterThrowing注解类似，都是规定了在何时(相对于待增强方法)执行被注解的方法。只不过注解属性有所区别</li><li>JoinPoint 代表着织入增强处理的连接点。注意一点：除了注解@Around的方法外，其他都可以加这个JoinPoint作参数，@Around注解的方法的参数一定要是ProceedingJoinPoint。 JoinPoint包含了几个很有用的参数：<ul><li>Object[] getArgs：返回目标方法的参数</li><li>Signature getSignature：返回目标方法的签名</li><li>Object getTarget：返回被织入增强处理的目标对象</li><li>Object getThis：返回AOP框架为目标对象生成的代理对象</li></ul></li></ul><h3 id="运行效果"><a href="#运行效果" class="headerlink" title="运行效果"></a>运行效果</h3><p>理解了几个注解的作用后，通过运行结果，来看看测试方法都被哪些增强方法拦截了<br>启动后，在浏览器输入<a href="http://localhost:8080/ayahiro" target="_blank" rel="noopener">http://localhost:8080/ayahiro</a><br><img src="http://blog.duohuo.org/wp-content/uploads/2019/03/捕获-300x107.png" alt><br>可以看到，ayahiro()被所有增强方法拦截了。testControllerPointCut()和ayahiroPointCut()拦截不难理解，都前者是划定了一个范围，后者是直接具体定位到该方法。其中@After(value = “@annotation(requestColor)”) 的拦截方式比较特别，是通过自定义注解拦截的，因为ayahiro()被@RequestColor修饰，而@After拦截所有被@RequestColor修饰的方法。<br>输入<a href="http://localhost:8080/moonKa" target="_blank" rel="noopener">http://localhost:8080/moonKa</a><br><img src="http://blog.duohuo.org/wp-content/uploads/2019/03/捕获2-300x49.png" alt><br>可以看到@After就没有拦截moonKa方法，因为该方法没有被@RequestColor修饰。</p><h3 id="使用日志"><a href="#使用日志" class="headerlink" title="使用日志"></a>使用日志</h3><p>理解了AOP的思想之后，再结合slf4j记录日志就显得非常简单，调用日志方法只需要声明一个 private static final Logger logger = LoggerFactory.getLogger(当前类.class);  再用loger去调用具体的方法：.info()  .warn()  .debug() .error()即可~</p><p>参考资料：</p><ul><li><a href="https://www.cnblogs.com/wangshen31/p/9379197.html" target="_blank" rel="noopener">https://www.cnblogs.com/wangshen31/p/9379197.html</a></li><li><a href="https://blog.csdn.net/caychen/article/details/80112915" target="_blank" rel="noopener">https://blog.csdn.net/caychen/article/details/80112915</a></li></ul>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;需要大致了解：java日志基础，如核心组件Loggers,Appenders,Layouts的用处、SpringAOP概念&lt;/p&gt;
&lt;h3 id=&quot;为什么需要日志&quot;&gt;&lt;a href=&quot;#为什么需要日志&quot; class=&quot;headerlink&quot; title=&quot;为什么需要日志&quot;&gt;&lt;/a&gt;为什么需要日志&lt;/h3&gt;&lt;p&gt;当应用程序部署到服务器上运行时，用户在使用过程中可能会出现各种错误。这时应用程序将错误信息生成日志，就方便了开发人员快速定位错误和根源，从而进行有针对的维护。所以，在大型应用程序中，日志记录是必不可少的。&lt;br&gt;&lt;/p&gt;
    
    </summary>
    
      <category term="SpringBoot" scheme="http://yoursite.com/categories/SpringBoot/"/>
    
    
      <category term="SpringBoot" scheme="http://yoursite.com/tags/SpringBoot/"/>
    
  </entry>
  
  <entry>
    <title>设计模式之访问者模式和中介者模式</title>
    <link href="http://yoursite.com/2019/10/12/mediator-visitor/"/>
    <id>http://yoursite.com/2019/10/12/mediator-visitor/</id>
    <published>2019-10-12T13:42:21.447Z</published>
    <updated>2019-07-25T17:39:37.371Z</updated>
    
    <content type="html"><![CDATA[<p>上次学了解释器模式和迭代器模式，都不是用得很多，而且都比较好理解，就不水博客了</p><h3 id="访问者模式"><a href="#访问者模式" class="headerlink" title="访问者模式"></a>访问者模式</h3><p><strong>描述：封装一些施加于某种数据结构元素之上的操作。一旦这些操作需要修改，接受这个操作的数据结构可以保持不变。访问者模式适用于数据结构相对未定的系统，它把数据结构和作用于结构上的操作之间的耦合解脱开，使得操作集合可以相对自由的演化。</strong></p><p><img src="/2019/10/12/mediator-visitor/访问者模式.png" alt="访问者模式"><br><a id="more"></a></p><p>访问者模式的目的是要把处理从数据结构分离出来，有比较稳定的数据结构，又有易于变化的算法的话，使用访问者模式是合适的，因为它使得算法操作的增加变得容易。其缺点是增加新的数据结构变得困难。访问者模式比较复杂，当真正需要它的时候才考虑使用它。</p><p>访问者模式的5和角色</p><ul><li><p>Visitor（抽象访问者）角色：声明了一个或者多个方法操作，形成所有的具体访问者角色必须实现的接口。</p></li><li><p>ConcreteVisitor（具体访问者）角色：实现抽象访问者所声明的接口，也就是抽象访问者所声明的各个访问操作。</p></li><li><p>Element（抽象元素）：声明一个接受操作，接受一个访问者对象作为一个参数。</p></li><li><p>ConcreteElement（具体元素）：实现了抽象节点所规定的接受操作。</p></li><li><p>ObjectStructure（结构对象）：有如下的责任，可以遍历结构中的所有元素。</p></li></ul><p>在本例中，把厕所当做抽象元素，具体元素有男厕所和女厕所，由此可见数据结构时稳定的，因为生理性别只有男女之分。进了厕所可以解大便解小便，洗手，抽烟，想干啥干啥。因此操作是多变的，这些操作都相当于是“放松”，把“放松”抽象成Visitor, 用代码模拟如下</p><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">//Visitor</span></span><br><span class="line"><span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">Rest</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title">getManRest</span><span class="params">(ManRoom manRoom)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title">getWomanRest</span><span class="params">(WomanRoom womanRoom)</span></span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//ConcreteVisitor</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Stool</span> <span class="keyword">extends</span> <span class="title">Rest</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">getManRest</span><span class="params">(ManRoom manRoom)</span> </span>&#123;</span><br><span class="line">        System.out.println(manRoom.getClass().getSimpleName() + <span class="string">"上大便"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">getWomanRest</span><span class="params">(WomanRoom womanRoom)</span> </span>&#123;</span><br><span class="line">        System.out.println(womanRoom.getClass().getSimpleName() + <span class="string">"上大便"</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="class"><span class="keyword">class</span> <span class="title">Pee</span> <span class="keyword">extends</span> <span class="title">Rest</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">getManRest</span><span class="params">(ManRoom manRoom)</span> </span>&#123;</span><br><span class="line">        System.out.println(manRoom.getClass().getSimpleName() + <span class="string">"上小便"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">getWomanRest</span><span class="params">(WomanRoom womanRoom)</span> </span>&#123;</span><br><span class="line">        System.out.println(womanRoom.getClass().getSimpleName() + <span class="string">"上小便"</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">//Element</span></span><br><span class="line"><span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">Toilet</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title">accept</span><span class="params">(Rest rest)</span></span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//ConcreteElementA</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">ManRoom</span> <span class="keyword">extends</span> <span class="title">Toilet</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">accept</span><span class="params">(Rest rest)</span> </span>&#123;</span><br><span class="line">        rest.getManRest(<span class="keyword">this</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">//ConcreteElementB</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">WomanRoom</span> <span class="keyword">extends</span> <span class="title">Toilet</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">accept</span><span class="params">(Rest rest)</span> </span>&#123;</span><br><span class="line">        rest.getWomanRest(<span class="keyword">this</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">//ObjectStructure</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">ToiletManager</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> List&lt;Toilet&gt; elements = <span class="keyword">new</span> ArrayList&lt;&gt;();</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">attach</span><span class="params">(Toilet toilet)</span> </span>&#123;</span><br><span class="line">        elements.add(toilet);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">detach</span><span class="params">(Toilet toilet)</span> </span>&#123;</span><br><span class="line">        elements.remove(toilet);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">display</span><span class="params">(Rest rest)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">for</span> (Toilet toilet : elements) &#123;</span><br><span class="line">            toilet.accept(rest);</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 java"><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="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Visitor</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        ToiletManager toiletManager = <span class="keyword">new</span> ToiletManager();</span><br><span class="line">        toiletManager.attach(<span class="keyword">new</span> ManRoom());</span><br><span class="line">        toiletManager.attach(<span class="keyword">new</span> WomanRoom());</span><br><span class="line"></span><br><span class="line">        Stool stool = <span class="keyword">new</span> Stool();</span><br><span class="line">        toiletManager.display(stool);</span><br><span class="line"></span><br><span class="line">        Pee pee = <span class="keyword">new</span> Pee();</span><br><span class="line">        toiletManager.display(pee);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="中介者模式"><a href="#中介者模式" class="headerlink" title="中介者模式"></a>中介者模式</h3><p><strong>描述：包装了一系列对象相互作用的方式，使得这些对象不必相互明显作用，从而使它们可以松散偶合。当某些对象之间的作用发生改变时，不会立即影响其他的一些对象之间的作用，保证这些作用可以彼此独立的变化。</strong></p><p><img src="/2019/10/12/mediator-visitor/中介者模式.png" alt="中介者模式"></p><p>在开发中会遇到这样的场景：尽管将系统分割成许多对象通常可增加其可复用性，但是对象间相互连接的激增又会降低其可复用性了，大量的连接使得一个对象不可能在没有其他对象的支持下工作，系统表现为一个不可分割的整体，所以，对系统的行为进行任何较大的改动就十分困难了，这就是中介者模式解决问题的背景。</p><p>中介者模式的4个角色</p><ul><li><p>Mediator（抽象中介者）：它定义一个接口，该接口用于与各同事对象之间进行通信。</p></li><li><p>ConcreteMediator（具体中介者）：它是抽象中介者的子类，通过协调各个同事对象来实现协作行为，它维持了对各个同事对象的引用。</p></li><li><p>Colleague（抽象同事类）：它定义各个同事类公有的方法，并声明了一些抽象方法来供子类实现，同时它维持了一个对抽象中介者类的引用，其子类可以通过该引用来与中介者通信。</p></li><li><p>ConcreteColleague（具体同事类）：它是抽象同事类的子类；每一个同事对象在需要和其他同事对象通信时，先与中介者通信，通过中介者来间接完成与其他同事类的通信；在具体同事类中实现了在抽象同事类中声明的抽象方法。</p></li></ul><p>中介者模式的核心在于中介者类的引入，在中介者模式中，中介者类承担了两方面的职责：</p><ul><li><p>中转作用（结构性）：通过中介者提供的中转作用，各个同事对象就不再需要显式引用其他同事，当需要和其他同事进行通信时，可通过中介者来实现间接调用。该中转作用属于中介者在结构上的支持。</p></li><li><p>协调作用（行为性）：中介者可以更进一步的对同事之间的关系进行封装，同事可以一致的和中介者进行交互，而不需要指明中介者需要具体怎么做，中介者根据封装在自身内部的协调逻辑，对同事的请求进行进一步处理，将同事成员之间的关系行为进行分离和封装。</p></li></ul><p>在本例中，买家和买家交易时使用移动支付，以前是使用纸币进行面对面交易。现在可以通过支付宝、微信转账交易，这些移动支付平台就相当于中介者。而支付宝、微信就是具体中介者了。</p><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">//Mediator</span></span><br><span class="line"><span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">MobilePayment</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title">transfer</span><span class="params">(Account account, <span class="keyword">double</span> amount)</span></span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//Colleague</span></span><br><span class="line"><span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">Account</span> </span>&#123;</span><br><span class="line">    <span class="keyword">protected</span> MobilePayment mobilePayment;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Account</span><span class="params">(MobilePayment mobilePayment)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.mobilePayment = mobilePayment;</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">//ConcreteColleague</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Seller</span> <span class="keyword">extends</span> <span class="title">Account</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Seller</span><span class="params">(MobilePayment mobilePayment)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(mobilePayment);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">transfer</span><span class="params">(<span class="keyword">double</span> amount)</span> </span>&#123;</span><br><span class="line">        mobilePayment.transfer(<span class="keyword">this</span>, amount);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">notify</span><span class="params">(<span class="keyword">double</span> amount)</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"卖家收到 "</span> + amount + <span class="string">" 元"</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="class"><span class="keyword">class</span> <span class="title">Buyer</span> <span class="keyword">extends</span> <span class="title">Account</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Buyer</span><span class="params">(MobilePayment mobilePayment)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(mobilePayment);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">transfer</span><span class="params">(<span class="keyword">double</span> amount)</span> </span>&#123;</span><br><span class="line">        mobilePayment.transfer(<span class="keyword">this</span>, amount);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">notify</span><span class="params">(<span class="keyword">double</span> amount)</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"买家收到 "</span> + amount + <span class="string">" 元"</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">//ConcreteMediator</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Alipay</span> <span class="keyword">extends</span> <span class="title">MobilePayment</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> Buyer buyer;</span><br><span class="line">    <span class="keyword">private</span> Seller seller;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setBuyer</span><span class="params">(Buyer buyer)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.buyer = buyer;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setSeller</span><span class="params">(Seller seller)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.seller = seller;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">transfer</span><span class="params">(Account account, <span class="keyword">double</span> amount)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (account <span class="keyword">instanceof</span> Buyer) &#123;</span><br><span class="line">            seller.notify(amount);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            buyer.notify(amount);</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 java"><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="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Mediator</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        Alipay alipay = <span class="keyword">new</span> Alipay();</span><br><span class="line"></span><br><span class="line">        Buyer buyer = <span class="keyword">new</span> Buyer(alipay);</span><br><span class="line">        Seller seller = <span class="keyword">new</span> Seller(alipay);</span><br><span class="line"></span><br><span class="line">        alipay.setBuyer(buyer);</span><br><span class="line">        alipay.setSeller(seller);</span><br><span class="line"></span><br><span class="line">        buyer.transfer(<span class="number">999</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>中介者模式很容易在系统中应用，也很容易误用。当系统中出现了“多对多”交互复杂的对象群时，不要急于使用中介者模式，而要先反思你的系统在设计上是不是合理。</p><p>优点：Mediator的出现减少了各个Colleague的耦合，使得可以独立改变和复用各个Colleague,由于把对象如何协作进行了抽象，将中介者作为一个独立的概念并将其封装在一个对象中，这样关注的对象就从对象各自本身转移到它们之间的交互上来，也就是以更宏观的角度去看待系统。</p><p>缺点：由于ConcreteMediator控制了集中化，于是就把交互复杂性变为中介者复杂性，这就使得中介者会变得比任何一个ConcreteColleague都复杂。</p><p>参考资料：</p><ul><li><a href="https://blog.csdn.net/wwwdc1012/article/details/83389158" target="_blank" rel="noopener">https://blog.csdn.net/wwwdc1012/article/details/83389158</a></li><li><a href="https://xuwujing.blog.csdn.net/article/details/83588888" target="_blank" rel="noopener">https://xuwujing.blog.csdn.net/article/details/83588888</a></li><li>《大话设计模式》</li></ul>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;上次学了解释器模式和迭代器模式，都不是用得很多，而且都比较好理解，就不水博客了&lt;/p&gt;
&lt;h3 id=&quot;访问者模式&quot;&gt;&lt;a href=&quot;#访问者模式&quot; class=&quot;headerlink&quot; title=&quot;访问者模式&quot;&gt;&lt;/a&gt;访问者模式&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;描述：封装一些施加于某种数据结构元素之上的操作。一旦这些操作需要修改，接受这个操作的数据结构可以保持不变。访问者模式适用于数据结构相对未定的系统，它把数据结构和作用于结构上的操作之间的耦合解脱开，使得操作集合可以相对自由的演化。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/2019/10/12/mediator-visitor/访问者模式.png&quot; alt=&quot;访问者模式&quot;&gt;&lt;br&gt;&lt;/p&gt;
    
    </summary>
    
      <category term="设计模式" scheme="http://yoursite.com/categories/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
    
    
      <category term="DesignPatterns" scheme="http://yoursite.com/tags/DesignPatterns/"/>
    
  </entry>
  
  <entry>
    <title>java泛型归纳</title>
    <link href="http://yoursite.com/2019/10/12/generic/"/>
    <id>http://yoursite.com/2019/10/12/generic/</id>
    <published>2019-10-12T13:42:21.416Z</published>
    <updated>2019-09-02T05:47:59.446Z</updated>
    
    <content type="html"><![CDATA[<p>之前分享会做的泛型总结</p><h3 id="泛型类"><a href="#泛型类" class="headerlink" title="泛型类"></a>泛型类</h3><figure class="highlight java"><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="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Generic</span>&lt;<span class="title">T</span>&gt; </span>&#123;</span><br><span class="line">    <span class="comment">//key这个成员变量的类型为T,T的类型由外部指定</span></span><br><span class="line">    <span class="keyword">private</span> T key;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Generic</span><span class="params">(T key)</span> </span>&#123; <span class="comment">//构造方法形参key的类型也为T，T的类型由外部指定</span></span><br><span class="line">        <span class="keyword">this</span>.key = key;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> T <span class="title">getKey</span><span class="params">()</span> </span>&#123; <span class="comment">//方法getKey的返回值类型为T，T的类型由外部指定</span></span><br><span class="line">        <span class="keyword">return</span> key;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>&lt; &gt;里可以有多个参数</strong><br><a id="more"></a></p><h3 id="泛型接口"><a href="#泛型接口" class="headerlink" title="泛型接口"></a>泛型接口</h3><p>定义一个泛型接口</p><figure class="highlight java"><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">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">Generator</span>&lt;<span class="title">T</span>&gt; </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> T <span class="title">next</span><span class="params">()</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>传入泛型实参时：<br>定义一个生产器实现这个接口，虽然我们只创建了一个泛型接口Generator<t><br>但是我们可以为T传入无数种实参，形成无数种类型的Generator接口<br>在实现类实现泛型接口时，如已将泛型类型传入实参类型，则所有使用泛型的地方都要替换成传入的实参类型</t></p><p>即：Generator<t>，public T next();中的的T都要替换成传入的String类型</t></p><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">FruitGenerator</span> <span class="keyword">implements</span> <span class="title">Generator</span>&lt;<span class="title">String</span>&gt; </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> String[] fruits = <span class="keyword">new</span> String[]&#123;<span class="string">"Apple"</span>, <span class="string">"Banana"</span>, <span class="string">"Pear"</span>&#125;;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">next</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        Random rand = <span class="keyword">new</span> Random();</span><br><span class="line">        <span class="keyword">return</span> fruits[rand.nextInt(<span class="number">3</span>)];</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="泛型方法"><a href="#泛型方法" class="headerlink" title="泛型方法"></a>泛型方法</h3><p>不要混淆返回值为泛型的方法和泛型方法，二者不同<br>一个真正的泛型方法：<br>首先在public与返回值之间的<t>必不可少，这表明这是一个泛型方法，并且声明了一个泛型T<br>这个T可以出现在这个泛型方法的任意位置<br>泛型的数量也可以为任意多个，如：</t></p><figure class="highlight java"><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">public</span> &lt;T, K&gt; <span class="function">K <span class="title">showKeyName</span><span class="params">(Generic&lt;T&gt; container)</span> </span>&#123;</span><br><span class="line">    <span class="comment">//something</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight java"><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="keyword">public</span> &lt;T&gt; <span class="function">T <span class="title">showKeyName</span><span class="params">(Generic&lt;T&gt; container)</span> </span>&#123;</span><br><span class="line">    System.out.println(<span class="string">"container key :"</span> + container.getKey());</span><br><span class="line">    T test = container.getKey();</span><br><span class="line">    <span class="keyword">return</span> test;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>类中也可以有泛型方法</p><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Test1</span>&lt;<span class="title">T</span>&gt; </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">testMethod</span><span class="params">(T t)</span> </span>&#123;</span><br><span class="line">        System.out.println(t.getClass().getName());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> &lt;T&gt; <span class="function">T <span class="title">testMethod1</span><span class="params">(T t)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> t;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>泛型类中的类型参数T与泛型方法中的类型参数T是没有联系的，泛型方法始终以自己定义的类型参数为准</strong></p><p>如果静态方法要使用泛型的话，必须将静态方法也定义成泛型方法</p><figure class="highlight java"><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="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">StaticGenerator</span>&lt;<span class="title">T</span>&gt; </span>&#123;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 如果在类中定义使用泛型的静态方法，需要添加额外的泛型声明（将这个方法定义成泛型方法）</span></span><br><span class="line"><span class="comment">     * 即使静态方法要使用泛型类中已经声明过的泛型也不可以。</span></span><br><span class="line"><span class="comment">     * 如：public static void show(T t)&#123;..&#125;,此时编译器会提示错误信息：</span></span><br><span class="line"><span class="comment">     * "StaticGenerator cannot be refrenced from static context"</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> &lt;T&gt; <span class="function"><span class="keyword">void</span> <span class="title">show</span><span class="params">(T t)</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="通配符"><a href="#通配符" class="headerlink" title="通配符"></a>通配符</h3><h4 id="lt-gt"><a href="#lt-gt" class="headerlink" title="&lt;?&gt;"></a>&lt;?&gt;</h4><ul><li>被称作无限定的通配符</li><li>当&lt;?&gt;存在时，Collection对象失去add( )能力</li><li>即不能调用与类型有关的方法</li></ul><h4 id="lt-extends-T-gt"><a href="#lt-extends-T-gt" class="headerlink" title="&lt;? extends T&gt;"></a>&lt;? extends T&gt;</h4><ul><li>被称作有上限的通配符，以T为上界</li><li>对应集合丧失add( )能力</li><li>可以通过get( )返回元素，能用T以及T的父类接收</li><li>只能被T以及T就子类的集合赋值</li></ul><h4 id="lt-super-T-gt"><a href="#lt-super-T-gt" class="headerlink" title="&lt;? super T&gt;"></a>&lt;? super T&gt;</h4><ul><li>被称作有下限的通配符，以T为下界</li><li>对应集合可以add( )T以及T的子类</li><li>可以通过get( )返回元素，但只能用Object接收</li><li>只能被T以及T的父类的集合赋值</li></ul><p><strong>泛型的上下边界添加，必须与泛型的声明在一起</strong></p><blockquote><p>泛型的声明：即在类名后的&lt; &gt;，泛型方法修饰符和返回值之间的&lt; &gt;</p></blockquote><h3 id="类型擦除"><a href="#类型擦除" class="headerlink" title="类型擦除"></a>类型擦除</h3><figure class="highlight java"><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">List&lt;String&gt; l1 = <span class="keyword">new</span> ArrayList&lt;String&gt;();</span><br><span class="line">List&lt;Integer&gt; l2 = <span class="keyword">new</span> ArrayList&lt;Integer&gt;();</span><br><span class="line">System.out.println(l1.getClass()==l2.getClass()); <span class="comment">//true</span></span><br></pre></td></tr></table></figure><blockquote><p>类型擦除：泛型信息只存在于代码编译阶段，在进入 JVM 之前，与泛型相关的信息会被擦除掉</p></blockquote><p>在泛型类被类型擦除的时候，之前泛型类中的类型参数部分如果没有指定上限，如 <t> 则会被转译成普通的 Object 类型，如果指定了上限如 <t extends string> 则类型参数就被替换成类型上限String，并不存在<t super string>的写法</t></t></t></p><p>List<t> 一旦传入类型参数  List<integer>    就不能add(string)了，但基于对类型擦除的了解，其实T都是Object，可以利用反射绕开这个限制</integer></t></p><figure class="highlight java"><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">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">List</span>&lt;<span class="title">E</span>&gt; <span class="keyword">extends</span> <span class="title">Collection</span>&lt;<span class="title">E</span>&gt; </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">boolean</span> <span class="title">add</span><span class="params">(E e)</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>类型擦除后，add()方法等同于 boolean add(Object obj);</p><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line">List&lt;Integer&gt; ls = <span class="keyword">new</span> ArrayList&lt;&gt;();</span><br><span class="line">ls.add(<span class="number">23</span>);</span><br><span class="line"><span class="comment">//ls.add("text");</span></span><br><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">    Method method = ls.getClass().getDeclaredMethod(<span class="string">"add"</span>, Object.class);</span><br><span class="line">    method.invoke(ls, <span class="string">"test"</span>);</span><br><span class="line">    method.invoke(ls, <span class="number">42.9f</span>);</span><br><span class="line">&#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">    e.printStackTrace();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>可以看到，利用类型擦除的原理，用反射的手段就绕过了正常开发中编译器不允许的操作限制</p><p>参考资料：<br><a href="https://blog.csdn.net/s10461/article/details/53941091" target="_blank" rel="noopener">https://blog.csdn.net/s10461/article/details/53941091</a></p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;之前分享会做的泛型总结&lt;/p&gt;
&lt;h3 id=&quot;泛型类&quot;&gt;&lt;a href=&quot;#泛型类&quot; class=&quot;headerlink&quot; title=&quot;泛型类&quot;&gt;&lt;/a&gt;泛型类&lt;/h3&gt;&lt;figure class=&quot;highlight java&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;class&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;title&quot;&gt;Generic&lt;/span&gt;&amp;lt;&lt;span class=&quot;title&quot;&gt;T&lt;/span&gt;&amp;gt; &lt;/span&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;comment&quot;&gt;//key这个成员变量的类型为T,T的类型由外部指定&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;keyword&quot;&gt;private&lt;/span&gt; T key;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;function&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;title&quot;&gt;Generic&lt;/span&gt;&lt;span class=&quot;params&quot;&gt;(T key)&lt;/span&gt; &lt;/span&gt;&amp;#123; &lt;span class=&quot;comment&quot;&gt;//构造方法形参key的类型也为T，T的类型由外部指定&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &lt;span class=&quot;keyword&quot;&gt;this&lt;/span&gt;.key = key;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;function&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;public&lt;/span&gt; T &lt;span class=&quot;title&quot;&gt;getKey&lt;/span&gt;&lt;span class=&quot;params&quot;&gt;()&lt;/span&gt; &lt;/span&gt;&amp;#123; &lt;span class=&quot;comment&quot;&gt;//方法getKey的返回值类型为T，T的类型由外部指定&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; key;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;&lt;strong&gt;&amp;lt; &amp;gt;里可以有多个参数&lt;/strong&gt;&lt;br&gt;&lt;/p&gt;
    
    </summary>
    
      <category term="Java" scheme="http://yoursite.com/categories/Java/"/>
    
    
      <category term="Basic" scheme="http://yoursite.com/tags/Basic/"/>
    
      <category term="Java" scheme="http://yoursite.com/tags/Java/"/>
    
  </entry>
  
  <entry>
    <title>设计模式之享元模式、组合模式和代理模式</title>
    <link href="http://yoursite.com/2019/10/12/flyweight-composite-proxy/"/>
    <id>http://yoursite.com/2019/10/12/flyweight-composite-proxy/</id>
    <published>2019-10-12T13:42:21.396Z</published>
    <updated>2019-07-22T16:43:37.114Z</updated>
    
    <content type="html"><![CDATA[<p>结构型模式还剩下这三个，一起记录下来吧</p><h3 id="享元模式"><a href="#享元模式" class="headerlink" title="享元模式"></a>享元模式</h3><p><strong>描述：通过共享以便有效的支持大量小颗粒对象。</strong></p><p><img src="/2019/10/12/flyweight-composite-proxy/享元模式.png" alt="享元模式"><br><a id="more"></a><br>享元模式可以避免大量非常相似类的开销，在程序设计中，有时需要生成大量细粒度的类实例来表示数据。如果能发现这些实例除了几个参数外基本都是相同的，有时就能够受大幅度地减少需要实例化的类的数量。如果能把那些参数移到类实例的外面，在方法调用时将他们传递进来，就可以通过共享大幅度减少单个实例的数目。</p><p>什么时候用享元模式？<br>如果一个应用程序使用了大量的对象，而大量的这些对象造成了很大的存储开销时就应该考虑使用；还有就是对象的大多数状态可以是外部状态，如果删除对象的外部状态，那么可以用相对较少的共享对象取代很多组对象，此时可以考虑使用享元模式。</p><p>享元模式的4个角色</p><ul><li><p>Flyweight（抽象享元类）：通常是一个接口或抽象类，在抽象享元类中声明了具体享元类公共的方法，这些方法可以向外界提供享元对象的内部数据（内部状态），同时也可以通过这些方法来设置外部数据（外部状态）。</p></li><li><p>ConcreteFlyweight（具体享元类）：它实现了抽象享元类，其实例称为享元对象；在具体享元类中为内部状态提供了存储空间。通常我们可以结合单例模式来设计具体享元类，为每一个具体享元类提供唯一的享元对象。</p></li><li><p>UnsharedConcreteFlyweight（非共享具体享元类）：并不是所有的抽象享元类的子类都需要被共享，不能被共享的子类可设计为非共享具体享元类；当需要一个非共享具体享元类的对象时可以直接通过实例化创建。</p></li><li><p>FlyweightFactory（享元工厂类）：享元工厂类用于创建并管理享元对象，它针对抽象享元类编程，将各种类型的具体享元对象存储在一个享元池中，享元池一般设计为一个存储“键值对”的集合（也可以是其他类型的集合），可以结合工厂模式进行设计；当用户请求一个具体享元对象时，享元工厂提供一个存储在享元池中已创建的实例或者创建一个新的实例（如果不存在的话），返回新创建的实例并将其存储在享元池中。</p></li></ul><p>以棋类运动为例，棋子可以看作抽象享元类，围棋棋子，五子棋棋子，象棋棋子这些可以看作是具体享元类。各类棋子除了具体位置(在此处以(x,y)坐标表示)这一外部状态不同，其他属性都相同。此时就可以使用享元模式，比如在一局围棋游戏中，只有黑棋和白棋这两种具体享元类，大大减少了重复生成棋子的内存开销。</p><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">//Flyweight</span></span><br><span class="line"><span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">Piece</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title">setPosition</span><span class="params">(<span class="keyword">int</span> x, <span class="keyword">int</span> y)</span></span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//ConcreteFlyweight</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">GoPieces</span> <span class="keyword">extends</span> <span class="title">Piece</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> String type;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">GoPieces</span><span class="params">(String type)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.type = type;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setPosition</span><span class="params">(<span class="keyword">int</span> x, <span class="keyword">int</span> y)</span> </span>&#123;</span><br><span class="line">        System.out.println(type + <span class="string">" 下在("</span> + x + <span class="string">","</span> + y + <span class="string">")处。"</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">//FlyweightFactory</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">PieceFactory</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> Map&lt;String, Piece&gt; pieceMap = <span class="keyword">new</span> HashMap&lt;&gt;();</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Piece <span class="title">getPiece</span><span class="params">(String key)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (!pieceMap.containsKey(key)) &#123;</span><br><span class="line">            pieceMap.put(key, <span class="keyword">new</span> GoPieces(key));</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> pieceMap.get(key);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getNumberOfPiecesType</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> pieceMap.size();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>主程序</p><figure class="highlight java"><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="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Flyweight</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        PieceFactory pieceFactory = <span class="keyword">new</span> PieceFactory();</span><br><span class="line">        Piece black1 = pieceFactory.getPiece(<span class="string">"黑棋"</span>);</span><br><span class="line">        black1.setPosition(<span class="number">3</span>, <span class="number">4</span>);</span><br><span class="line"></span><br><span class="line">        Piece write1 = pieceFactory.getPiece(<span class="string">"白棋"</span>);</span><br><span class="line">        write1.setPosition(<span class="number">5</span>, <span class="number">6</span>);</span><br><span class="line"></span><br><span class="line">        Piece black2 = pieceFactory.getPiece(<span class="string">"黑棋"</span>);</span><br><span class="line">        black2.setPosition(<span class="number">1</span>, <span class="number">2</span>);</span><br><span class="line"></span><br><span class="line">        Piece write2 = pieceFactory.getPiece(<span class="string">"白棋"</span>);</span><br><span class="line">        write2.setPosition(<span class="number">3</span>, <span class="number">7</span>);</span><br><span class="line"></span><br><span class="line">        Piece black3 = pieceFactory.getPiece(<span class="string">"黑棋"</span>);</span><br><span class="line">        black3.setPosition(<span class="number">4</span>, <span class="number">8</span>);</span><br><span class="line"></span><br><span class="line">        System.out.println(pieceFactory.getNumberOfPiecesType());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="组合模式"><a href="#组合模式" class="headerlink" title="组合模式"></a>组合模式</h3><p><strong>描述：把多个对象组成树状结构来表示局部与整体，这样用户可以一样的对待单个对象和对象的组合。</strong></p><p><img src="/2019/10/12/flyweight-composite-proxy/组合模式.png" alt="组合模式"></p><p>组合模式要解决的是<strong>整体和部分可以被一致对待</strong>的问题</p><p>何时使用组合模式？<br>需求中是体现部分与整体层次的结构时，希望用户可以忽略组合对象与单个对象的不同，统一地使用组合结构中的所有对象时，就应该考虑用组合模式。</p><p>组合模式的3个角色</p><ul><li><p>Component（抽象构件）：它可以是接口或抽象类，为叶子构件和容器构件对象声明接口，在该角色中可以包含所有子类共有行为的声明和实现。在抽象构件中定义了访问及管理它的子构件的方法，如增加子构件、删除子构件、获取子构件等。</p></li><li><p>Leaf（叶子构件）：它在组合结构中表示叶子节点对象，叶子节点没有子节点，它实现了在抽象构件中定义的行为。对于那些访问及管理子构件的方法，可以通过异常等方式进行处理。</p></li><li><p>Composite（容器构件）：它在组合结构中表示容器节点对象，容器节点包含子节点，其子节点可以是叶子节点，也可以是容器节点，它提供一个集合用于存储子节点，实现了在抽象构件中定义的行为，包括那些访问及管理子构件的方法，在其业务方法中可以递归调用其子节点的业务方法。</p></li></ul><p>组合模式的关键是定义了一个抽象构件类，它既可以代表叶子，又可以代表容器，而客户端针对该抽象构件类进行编程，无须知道它到底表示的是叶子还是容器，可以对其进行统一处理。同时容器对象与抽象构件类之间还建立一个聚合关联关系，在容器对象中既可以包含叶子，也可以包含容器，以此实现递归组合，形成一个树形结构。</p><p>透明模式与安全模式</p><ul><li><p>透明模式在Component中声明所有管理子对象的方法，这样做的好处是叶结点和枝结点对于外界没有区别，它们具备完全一致的行为接口，但Leaf本身不具备add()、remove()功能，所有实现它们是没有意义的。</p></li><li><p>安全模式在Component接口中不去声明管理子对象的方法，那么子类Leaf就不需要实现它们，而是在Composite中实现，不过由于不够透明，叶结点和枝结点有不同的接口，客户端调用时要做相应的判断。</p></li></ul><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">//Component</span></span><br><span class="line"><span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">Company</span> </span>&#123;</span><br><span class="line">    <span class="keyword">protected</span> String name;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Company</span><span class="params">(String name)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.name = name;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title">add</span><span class="params">(Company company)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title">remove</span><span class="params">(Company company)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title">display</span><span class="params">(<span class="keyword">int</span> depth)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title">work</span><span class="params">()</span></span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//Composite</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">ConcreteCompany</span> <span class="keyword">extends</span> <span class="title">Company</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> List&lt;Company&gt; companyList = <span class="keyword">new</span> ArrayList&lt;&gt;();</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">ConcreteCompany</span><span class="params">(String name)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(name);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">add</span><span class="params">(Company company)</span> </span>&#123;</span><br><span class="line">        companyList.add(company);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">remove</span><span class="params">(Company company)</span> </span>&#123;</span><br><span class="line">        companyList.remove(company);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">display</span><span class="params">(<span class="keyword">int</span> depth)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; depth; ++i) &#123;</span><br><span class="line">            System.out.print(<span class="string">'-'</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println(name);</span><br><span class="line">        <span class="keyword">for</span> (Company component : companyList) &#123;</span><br><span class="line">            component.display(depth + <span class="number">2</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="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">work</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">for</span> (Company component : companyList) &#123;</span><br><span class="line">            component.work();</span><br><span class="line">        &#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">//Leaf</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">HRDepartment</span> <span class="keyword">extends</span> <span class="title">Company</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">HRDepartment</span><span class="params">(String name)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(name);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">add</span><span class="params">(Company company)</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">remove</span><span class="params">(Company company)</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">display</span><span class="params">(<span class="keyword">int</span> depth)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; depth; ++i) &#123;</span><br><span class="line">            System.out.print(<span class="string">'-'</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println(name);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">work</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(name + <span class="string">" 员工招聘培训管理"</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="class"><span class="keyword">class</span> <span class="title">FinanceDepartment</span> <span class="keyword">extends</span> <span class="title">Company</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">FinanceDepartment</span><span class="params">(String name)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(name);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">add</span><span class="params">(Company company)</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">remove</span><span class="params">(Company company)</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">display</span><span class="params">(<span class="keyword">int</span> depth)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; depth; ++i) &#123;</span><br><span class="line">            System.out.print(<span class="string">'-'</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println(name);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">work</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(name + <span class="string">" 公司财务收支管理"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>主程序</p><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Composite</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        ConcreteCompany root = <span class="keyword">new</span> ConcreteCompany(<span class="string">"北京总公司"</span>);</span><br><span class="line">        root.add(<span class="keyword">new</span> HRDepartment(<span class="string">"总公司人力资源部"</span>));</span><br><span class="line">        root.add(<span class="keyword">new</span> FinanceDepartment(<span class="string">"总公司财务部"</span>));</span><br><span class="line"></span><br><span class="line">        ConcreteCompany comp = <span class="keyword">new</span> ConcreteCompany(<span class="string">"上海华东分公司"</span>);</span><br><span class="line">        comp.add(<span class="keyword">new</span> HRDepartment(<span class="string">"华东分公司人力资源部"</span>));</span><br><span class="line">        comp.add(<span class="keyword">new</span> FinanceDepartment(<span class="string">"华东分公司财务部"</span>));</span><br><span class="line">        root.add(comp);</span><br><span class="line"></span><br><span class="line">        ConcreteCompany comp1 = <span class="keyword">new</span> ConcreteCompany(<span class="string">"南京办事处"</span>);</span><br><span class="line">        comp.add(<span class="keyword">new</span> HRDepartment(<span class="string">"南京办事处人力资源部"</span>));</span><br><span class="line">        comp.add(<span class="keyword">new</span> FinanceDepartment(<span class="string">"南京办事处财务部"</span>));</span><br><span class="line">        comp.add(comp1);</span><br><span class="line"></span><br><span class="line">        root.display(<span class="number">1</span>);</span><br><span class="line">        root.work();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="代理模式"><a href="#代理模式" class="headerlink" title="代理模式"></a>代理模式</h3><p><strong>描述：为其他对象提供一个代理以控制对这个对象的访问。</strong></p><p><img src="/2019/10/12/flyweight-composite-proxy/代理模式.png" alt="代理模式"></p><p>代理模式的3个角色  </p><ul><li><p>Subject（抽象角色）：通过接口或抽象类声明真实角色实现的业务方法。</p></li><li><p>Proxy（代理角色）：实现抽象角色，是真实角色的代理，通过真实角色的业务逻辑方法来实现抽象方法，并可以附加自己的操作。</p></li><li><p>RealSubject（真实角色）：实现抽象角色，定义真实角色所要实现的业务逻辑，供代理角色调用。</p></li></ul><p>代理模式又分为静态代理和动态代理</p><p>先来举个例子，电脑和手机都可以联网，现在有个代理VPN也可以联网，我们用VPN代理实现翻墙功能，用代码实现就是</p><figure class="highlight java"><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">//Subject</span></span><br><span class="line"><span class="class"><span class="keyword">interface</span> <span class="title">Networking</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">connection</span><span class="params">()</span></span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//RealSubject</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Computer</span> <span class="keyword">implements</span> <span class="title">Networking</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Computer</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Computer</span><span class="params">(String name)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.name = name;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">connection</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(name + <span class="string">" 已联网，但访问网站受限。"</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="class"><span class="keyword">class</span> <span class="title">SmartPhone</span> <span class="keyword">implements</span> <span class="title">Networking</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">SmartPhone</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">SmartPhone</span><span class="params">(String name)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.name = name;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">connection</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(name + <span class="string">" 已联网，但访问网站受限。"</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">//Proxy</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">ComputerVPNProxy</span> <span class="keyword">implements</span> <span class="title">Networking</span> </span>&#123;</span><br><span class="line">    Computer computer;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">ComputerVPNProxy</span><span class="params">(Computer computer)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.computer = computer;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">connection</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        computer.connection();</span><br><span class="line">        System.out.println(<span class="string">"已翻墙，开始网上冲浪吧！"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>此处用了静态代理的实现方式，可以看到实现起来非常简单，但有个缺点，如果我们不确定需要代理某个真实类的时候会比较麻烦，而且在类过多的时候，目标对象与代理对象都要维护，会使系统复杂度提升，维护起来也更加麻烦。这时我们就可以使用动态代理来进行解决。</p><p>这里记录两种动态代理的方式</p><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">//JDK动态代理</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">DynamicVPNProxy</span> <span class="keyword">implements</span> <span class="title">InvocationHandler</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> Networking networking;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">DynamicVPNProxy</span><span class="params">(Networking networking)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.networking = networking;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Object <span class="title">invoke</span><span class="params">(Object proxy, Method method, Object[] args)</span> <span class="keyword">throws</span> Throwable </span>&#123;</span><br><span class="line">        Object result = <span class="keyword">null</span>;</span><br><span class="line">        <span class="keyword">if</span> (<span class="string">"connection"</span>.equals(method.getName())) &#123;</span><br><span class="line">            result = method.invoke(networking, args);</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println(<span class="string">"已翻墙，开始网上冲浪吧！"</span>);</span><br><span class="line">        <span class="keyword">return</span> result;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">//cglib代理</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">CglibVPNProxy</span> <span class="keyword">implements</span> <span class="title">MethodInterceptor</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> Object target;<span class="comment">//注意 target最好写一个无参构造方法</span></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">CglibVPNProxy</span><span class="params">(Object target)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.target = target;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Object <span class="title">getVPNInstance</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="comment">//1工具类</span></span><br><span class="line">        Enhancer en = <span class="keyword">new</span> Enhancer();</span><br><span class="line">        <span class="comment">//2设置父类</span></span><br><span class="line">        en.setSuperclass(target.getClass());</span><br><span class="line">        <span class="comment">//3设置回调函数</span></span><br><span class="line">        en.setCallback(<span class="keyword">this</span>);</span><br><span class="line">        <span class="comment">//4创建子类(代理对象)</span></span><br><span class="line">        <span class="keyword">return</span> en.create();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Object <span class="title">intercept</span><span class="params">(Object o, Method method, Object[] objects, MethodProxy methodProxy)</span> <span class="keyword">throws</span> Throwable </span>&#123;</span><br><span class="line">        Object result = <span class="keyword">null</span>;</span><br><span class="line">        <span class="keyword">if</span> (<span class="string">"connection"</span>.equals(method.getName())) &#123;</span><br><span class="line">            result = method.invoke(target, objects);</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println(<span class="string">"已翻墙，开始网上冲浪吧！"</span>);</span><br><span class="line">        <span class="keyword">return</span> result;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>主程序</p><figure class="highlight java"><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">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Proxy</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        Computer computer = <span class="keyword">new</span> Computer(<span class="string">"IBN-5100"</span>);</span><br><span class="line">        computer.connection();</span><br><span class="line"></span><br><span class="line">        <span class="comment">//静态代理</span></span><br><span class="line">        Networking computerVPN = <span class="keyword">new</span> ComputerVPNProxy(computer);</span><br><span class="line">        computerVPN.connection();</span><br><span class="line"></span><br><span class="line">        <span class="comment">//JDK动态代理</span></span><br><span class="line">        Networking smartPhoneVPN = (Networking) java.lang.reflect.Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),</span><br><span class="line">                <span class="keyword">new</span> Class[]&#123;Networking.class&#125;, <span class="keyword">new</span> DynamicVPNProxy(<span class="keyword">new</span> SmartPhone(<span class="string">"IPhoneX"</span>)));</span><br><span class="line">        smartPhoneVPN.connection();</span><br><span class="line"></span><br><span class="line">        <span class="comment">//Cglib动态代理</span></span><br><span class="line">        Networking smartPhoneVPN1 = (Networking) <span class="keyword">new</span> CglibVPNProxy(<span class="keyword">new</span> SmartPhone(<span class="string">"MI6"</span>)).getVPNInstance();</span><br><span class="line">        smartPhoneVPN1.connection();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>参考资料：</p><ul><li><a href="https://blog.csdn.net/wwwdc1012/article/category/7152423" target="_blank" rel="noopener">https://blog.csdn.net/wwwdc1012/article/category/7152423</a></li><li>《大话设计模式》</li></ul>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;结构型模式还剩下这三个，一起记录下来吧&lt;/p&gt;
&lt;h3 id=&quot;享元模式&quot;&gt;&lt;a href=&quot;#享元模式&quot; class=&quot;headerlink&quot; title=&quot;享元模式&quot;&gt;&lt;/a&gt;享元模式&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;描述：通过共享以便有效的支持大量小颗粒对象。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/2019/10/12/flyweight-composite-proxy/享元模式.png&quot; alt=&quot;享元模式&quot;&gt;&lt;br&gt;&lt;/p&gt;
    
    </summary>
    
      <category term="设计模式" scheme="http://yoursite.com/categories/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
    
    
      <category term="DesignPatterns" scheme="http://yoursite.com/tags/DesignPatterns/"/>
    
  </entry>
  
  <entry>
    <title>初始Netty —— 实现简单的C/S通信</title>
    <link href="http://yoursite.com/2019/10/12/first_Netty/"/>
    <id>http://yoursite.com/2019/10/12/first_Netty/</id>
    <published>2019-10-12T13:42:21.376Z</published>
    <updated>2019-09-03T07:03:29.940Z</updated>
    
    <content type="html"><![CDATA[<p>写在前面：<br>Netty是Java的网络编程框架，既然是框架的学习，不免会碰到很多分支的知识和不熟悉的名词。这就需要不断的做“下潜”，耐心搜索，不求甚解，等到大致熟悉之后再去逐一深究。因此有些概念作者也不能做出详细解释，请参考贴出的相关文章或自行搜索以解决疑惑。<br><a id="more"></a></p><h3 id="什么是Netty"><a href="#什么是Netty" class="headerlink" title="什么是Netty"></a>什么是Netty</h3><p>网上很多文章都有作解释。以作者的使用体验来说，Netty是封装了 Java socket nio 来进行网络编程的工具。说到网络编程，大二软工的软件工程实训就有这个小课题，当时作者是用Java socket io来写，还没用到nio呢，就是参照网上的例子手动模拟通信过程，自己用最简单的 <strong>阻塞I/O</strong> 的模式写了一个Thread类来处理所有不同种类的请求，由于需求简单，尚能完成。 想要模拟效果更自然一点就要用 <strong>非阻塞I/O</strong> 模式，而nio就是用来写非阻塞I/O的api。但是nio的编写对java程序员是有比较高的要求的。Netty就可以简化这一系列操作。<br><!--more--></p><h3 id="预备知识"><a href="#预备知识" class="headerlink" title="预备知识"></a>预备知识</h3><p>贴几个比较靠谱的博客，不求甚解，大致了解一下就好。<br>关于NIO：<br><a href="https://www.jianshu.com/p/3cec590a122f" target="_blank" rel="noopener">https://www.jianshu.com/p/3cec590a122f</a>  (推荐，也包括I/O模型)<br><a href="https://my.oschina.net/andylucc/blog/614295" target="_blank" rel="noopener">https://my.oschina.net/andylucc/blog/614295</a><br>关于I/O模型：<br><a href="https://segmentfault.com/a/1190000003063859" target="_blank" rel="noopener">https://segmentfault.com/a/1190000003063859</a></p><h3 id="开发环境"><a href="#开发环境" class="headerlink" title="开发环境"></a>开发环境</h3><p>java JDK1.8 + IDEA + maven + Netty 4.1.6<br>maven依赖：<br><figure class="highlight plain"><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">&lt;dependency&gt;</span><br><span class="line">           &lt;groupId&gt;io.netty&lt;/groupId&gt;</span><br><span class="line">           &lt;artifactId&gt;netty-all&lt;/artifactId&gt;</span><br><span class="line">           &lt;version&gt;4.1.6.Final&lt;/version&gt;</span><br><span class="line">&lt;/dependency&gt;</span><br></pre></td></tr></table></figure></p><h3 id="实现功能"><a href="#实现功能" class="headerlink" title="实现功能"></a>实现功能</h3><p>C/S通信：C是客户端，S是服务端。在IDEA控制台开启服务端接收客户端的信息String, 并返回一个“hi!”+String，客户端收到服务端的信息后在控制台上输出。</p><h3 id="代码讲解"><a href="#代码讲解" class="headerlink" title="代码讲解"></a>代码讲解</h3><p>分为服务端和客户端两部分，各自又有一个处理连接逻辑的代码</p><h4 id="服务端代码"><a href="#服务端代码" class="headerlink" title="服务端代码"></a>服务端代码</h4><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> io.netty.bootstrap.ServerBootstrap;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelFuture;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelInitializer;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelPipeline;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.EventLoopGroup;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.nio.NioEventLoopGroup;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.socket.SocketChannel;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.socket.nio.NioServerSocketChannel;</span><br><span class="line"><span class="keyword">import</span> io.netty.handler.codec.DelimiterBasedFrameDecoder;</span><br><span class="line"><span class="keyword">import</span> io.netty.handler.codec.Delimiters;</span><br><span class="line"><span class="keyword">import</span> io.netty.handler.codec.string.StringDecoder;</span><br><span class="line"><span class="keyword">import</span> io.netty.handler.codec.string.StringEncoder;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">EchoServer</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">int</span> port;  <span class="comment">//1.设置服务端端口</span></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">EchoServer</span><span class="params">(<span class="keyword">int</span> port)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.port = port;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">start</span><span class="params">()</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">        EventLoopGroup group = <span class="keyword">new</span> NioEventLoopGroup();  <span class="comment">//2.创建 EventLoopGroup</span></span><br><span class="line">        <span class="keyword">try</span>&#123;</span><br><span class="line">            <span class="comment">/*</span></span><br><span class="line"><span class="comment">             * 客户端的是Bootstrap，服务端的则是    ServerBootstrap。</span></span><br><span class="line"><span class="comment">             **/</span></span><br><span class="line">            ServerBootstrap sbs = <span class="keyword">new</span> ServerBootstrap();  <span class="comment">//3.创建 ServerBootstrap</span></span><br><span class="line">            sbs.group(group)</span><br><span class="line">                    .channel(NioServerSocketChannel.class)  <span class="comment">//4.指定使用 NIO 的传输 Channel</span></span><br><span class="line">                    .childHandler(<span class="keyword">new</span> ChannelInitializer&lt;SocketChannel&gt;() &#123;</span><br><span class="line">                        <span class="meta">@Override</span></span><br><span class="line">                        <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">initChannel</span><span class="params">(SocketChannel ch)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">                            ChannelPipeline ph = ch.pipeline();</span><br><span class="line">                            <span class="comment">/*</span></span><br><span class="line"><span class="comment">                            * Netty中的编码/解码器，通过他你能完成字节与pojo、pojo与pojo的相互转换，</span></span><br><span class="line"><span class="comment">                            * 从而达到自定义协议的目的。</span></span><br><span class="line"><span class="comment">                            * 下面是以("\n")为结尾分割的 解码器</span></span><br><span class="line"><span class="comment">                            * */</span></span><br><span class="line">                            ph.addLast(<span class="string">"framer"</span>, <span class="keyword">new</span> DelimiterBasedFrameDecoder(<span class="number">8192</span>, Delimiters.lineDelimiter()))</span><br><span class="line">                                    .addLast(<span class="string">"decoder"</span>, <span class="keyword">new</span> StringDecoder())</span><br><span class="line">                                    .addLast(<span class="string">"encoder"</span>, <span class="keyword">new</span> StringEncoder())  <span class="comment">//解码和编码，应和客户端一致</span></span><br><span class="line">                                    .addLast(<span class="string">"handler"</span>, <span class="keyword">new</span> EchoServerHandler());  <span class="comment">//5.添加 EchoServerHandler 到 Channel 的 ChannelPipeline</span></span><br><span class="line"></span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;);</span><br><span class="line"></span><br><span class="line">            ChannelFuture cf = sbs.bind(<span class="keyword">this</span>.port).sync();  <span class="comment">//6.设置socket地址使用所选的端口 并且 绑定的服务器，sync 等待服务器关闭</span></span><br><span class="line"></span><br><span class="line">            System.out.println(<span class="string">"服务端启动成功..."</span>);</span><br><span class="line"></span><br><span class="line">            cf.channel().closeFuture().sync();  <span class="comment">//7.关闭 channel 和 块，直到它被关闭</span></span><br><span class="line"></span><br><span class="line">        &#125;<span class="keyword">finally</span> &#123;</span><br><span class="line">            group.shutdownGracefully();  <span class="comment">//8.关闭 EventLoopGroup，释放所有资源</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">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception</span>&#123;</span><br><span class="line">        <span class="keyword">new</span> EchoServer(<span class="number">65535</span>).start();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>注意这一段代码：<br><img src="http://blog.duohuo.org/wp-content/uploads/2019/01/2d5cd63552e26e1c12d11902c2a05bdc.png" alt><br>这是关键所在，其余代码基本上是套路代码，按部就班写就可以。<br>关键在于此处出现了：</p><ul><li>用ChannelPipeline引用了SocketChannel的pipeline，原因在于ChannelPipeline是用于存放ChannelHandler的容器，而接下来的解码编码操作和自定义的逻辑处理类都要涉及到ChannelHandler的子类<br>它们之间的关系可以用下图表示：</li></ul><p><img src="http://blog.duohuo.org/wp-content/uploads/2019/01/27d52ce0891ede08b68a32fcc9281ae2.png" alt></p><ul><li><p>Encoder(编码器)和Decoder(解码器)，属于Codec框架的内容，大致意思是：此处描述了服务端和客户端之间传输了什么类型的数据，这里要传输String就用到了StringDecoder/Encode   当然也可以传输其他类型的数据，详情参考这篇博客：<a href="https://www.jianshu.com/p/fd815bd437cd" target="_blank" rel="noopener">https://www.jianshu.com/p/fd815bd437cd</a></p></li><li><p>注释5.处的EchoServerHandler是自定义的类，可以看作是一种“规则”，规定了服务端以什么方式处理客户端发来的数据。</p></li></ul><h4 id="服务端处理连接的代码"><a href="#服务端处理连接的代码" class="headerlink" title="服务端处理连接的代码"></a>服务端处理连接的代码</h4><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.net.InetAddress;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelHandlerContext;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.SimpleChannelInboundHandler;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">EchoServerHandler</span> <span class="keyword">extends</span> <span class="title">SimpleChannelInboundHandler</span>&lt;<span class="title">String</span>&gt; </span>&#123;</span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">     * 收到消息时，返回信息</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">channelRead0</span><span class="params">(ChannelHandlerContext ctx, String msg)</span></span></span><br><span class="line"><span class="function">            <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"服务端接受的消息 : "</span> + msg);  <span class="comment">// 收到消息直接打印输出</span></span><br><span class="line">        <span class="keyword">if</span>(<span class="string">"quit"</span>.equals(msg))&#123;  <span class="comment">//服务端断开的条件</span></span><br><span class="line">            ctx.close();</span><br><span class="line">        &#125;</span><br><span class="line">        ctx.writeAndFlush(<span class="string">"hi! "</span>+msg+<span class="string">"\n"</span>);  <span class="comment">// 返回客户端消息</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">     * 建立连接时，返回消息</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">channelActive</span><span class="params">(ChannelHandlerContext ctx)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"连接的客户端地址:"</span> + ctx.channel().remoteAddress());</span><br><span class="line">        ctx.writeAndFlush(<span class="string">"客户端"</span>+ InetAddress.getLocalHost().getHostName() + <span class="string">"成功与服务端建立连接！ \n"</span>);</span><br><span class="line">        <span class="keyword">super</span>.channelActive(ctx);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="客户端代码"><a href="#客户端代码" class="headerlink" title="客户端代码"></a>客户端代码</h4><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> io.netty.bootstrap.Bootstrap;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.*;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.nio.NioEventLoopGroup;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.socket.SocketChannel;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.socket.nio.NioSocketChannel;</span><br><span class="line"><span class="keyword">import</span> io.netty.handler.codec.DelimiterBasedFrameDecoder;</span><br><span class="line"><span class="keyword">import</span> io.netty.handler.codec.Delimiters;</span><br><span class="line"><span class="keyword">import</span> io.netty.handler.codec.string.StringDecoder;</span><br><span class="line"><span class="keyword">import</span> io.netty.handler.codec.string.StringEncoder;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.util.Scanner;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">EchoClient</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> String host;  <span class="comment">//ip地址</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">int</span> port;     <span class="comment">//端口</span></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">EchoClient</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>(<span class="number">0</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">EchoClient</span><span class="params">(<span class="keyword">int</span> port)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>(<span class="string">"localhost"</span>, port);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">EchoClient</span><span class="params">(String host, <span class="keyword">int</span> port)</span> </span>&#123;</span><br><span class="line">         <span class="keyword">this</span>.host = host;</span><br><span class="line">         <span class="keyword">this</span>.port = port;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">start</span><span class="params">()</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">        EventLoopGroup group = <span class="keyword">new</span> NioEventLoopGroup();  <span class="comment">//1.创建 EventLoopGroup</span></span><br><span class="line">        <span class="keyword">try</span>&#123;</span><br><span class="line">            <span class="comment">/**</span></span><br><span class="line"><span class="comment">             * Netty创建全部都是实现自AbstractBootstrap。</span></span><br><span class="line"><span class="comment">             * 客户端的是Bootstrap，服务端的则是    ServerBootstrap。</span></span><br><span class="line"><span class="comment">             **/</span></span><br><span class="line">            Bootstrap bs = <span class="keyword">new</span> Bootstrap();  <span class="comment">//2.创建 Bootstrap</span></span><br><span class="line"></span><br><span class="line">            System.out.println(<span class="string">"客户端成功启动..."</span>);</span><br><span class="line"></span><br><span class="line">            bs.group(group)  <span class="comment">//3.指定 NioEventLoopGroup 来处理客户端事件。</span></span><br><span class="line">                    .channel(NioSocketChannel.class)</span><br><span class="line">                    .handler(<span class="keyword">new</span> ChannelInitializer&lt;SocketChannel&gt;() &#123;  <span class="comment">//4.指定使用 NIO 的传输 Channel</span></span><br><span class="line">                        <span class="meta">@Override</span></span><br><span class="line">                        <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">initChannel</span><span class="params">(SocketChannel ch)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">                            ChannelPipeline ph = ch.pipeline();</span><br><span class="line">                            ph.addLast(<span class="string">"framer"</span>, <span class="keyword">new</span> DelimiterBasedFrameDecoder(<span class="number">8192</span>, Delimiters.lineDelimiter()))</span><br><span class="line">                                    .addLast(<span class="string">"decoder"</span>, <span class="keyword">new</span> StringDecoder())</span><br><span class="line">                                    .addLast(<span class="string">"encoder"</span>, <span class="keyword">new</span> StringEncoder())  <span class="comment">// 解码和编码，应和服务端一致</span></span><br><span class="line">                                    .addLast(<span class="string">"handler"</span>, <span class="keyword">new</span> EchoClientHandler());  <span class="comment">//5.当建立一个连接和一个新的通道时，创建添加到 EchoClientHandler 实例 到 channel pipeline</span></span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;);</span><br><span class="line">            Channel ch = bs.connect(<span class="keyword">this</span>.host, <span class="keyword">this</span>.port).sync().channel();  <span class="comment">//6.设置服务器的ip和端口，并且连接到远程; 等待连接完成</span></span><br><span class="line"></span><br><span class="line">            Scanner in=<span class="keyword">new</span> Scanner(System.in);</span><br><span class="line"></span><br><span class="line">            <span class="keyword">while</span>(<span class="keyword">true</span>)&#123;</span><br><span class="line">                System.out.println(<span class="string">"请输入要发送的信息："</span>);</span><br><span class="line">                String str=in.next();</span><br><span class="line">                <span class="comment">//连接后发送数据</span></span><br><span class="line">                ch.writeAndFlush(str+ <span class="string">"\r\n"</span>);</span><br><span class="line">                System.out.println(<span class="string">"客户端发送数据:"</span>+str);</span><br><span class="line">                <span class="keyword">if</span> (str.equals(<span class="string">"quit"</span>))<span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            System.exit(<span class="number">0</span>);</span><br><span class="line"></span><br><span class="line">        &#125;<span class="keyword">finally</span> &#123;</span><br><span class="line">            group.shutdownGracefully();  <span class="comment">//8.关闭线程池和释放所有资源</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">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception</span>&#123;</span><br><span class="line">        <span class="keyword">new</span> EchoClient(<span class="string">"127.0.0.1"</span>, <span class="number">65535</span>).start();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>客户端和服务端代码样式基本一致，有几个关键点都已注释<br><img src="http://blog.duohuo.org/wp-content/uploads/2019/01/cefccb1c91e7593d6278109de2c6a89f.png" alt><br>此处以不断向客户端发送信息，输入“quit”终止连接。</p><h3 id="运行效果"><a href="#运行效果" class="headerlink" title="运行效果"></a>运行效果</h3><p>先运行服务端再运行客户端<br>服务端：<br><img src="http://blog.duohuo.org/wp-content/uploads/2019/01/781c5efd2db39cccd9f49ba70af253bc.png" alt><br>客户端1:<br><img src="http://blog.duohuo.org/wp-content/uploads/2019/01/6288b3985b7af37895fcf2f1b63be69a.png" alt><br>客户端2：<br><img src="http://blog.duohuo.org/wp-content/uploads/2019/01/3db8b4f3bc3dd1ce8d37dcc875dfc5c9.png" alt></p><p>参考文章</p><ul><li><a href="https://www.cnblogs.com/liuming1992/p/4758532.html" target="_blank" rel="noopener">https://www.cnblogs.com/liuming1992/p/4758532.html</a></li><li><a href="https://blog.csdn.net/qazwsxpcm/article/details/77750865" target="_blank" rel="noopener">https://blog.csdn.net/qazwsxpcm/article/details/77750865</a></li><li><a href="https://www.jianshu.com/p/b9f3f6a16911" target="_blank" rel="noopener">https://www.jianshu.com/p/b9f3f6a16911</a></li></ul>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;写在前面：&lt;br&gt;Netty是Java的网络编程框架，既然是框架的学习，不免会碰到很多分支的知识和不熟悉的名词。这就需要不断的做“下潜”，耐心搜索，不求甚解，等到大致熟悉之后再去逐一深究。因此有些概念作者也不能做出详细解释，请参考贴出的相关文章或自行搜索以解决疑惑。&lt;br&gt;&lt;/p&gt;
    
    </summary>
    
      <category term="Java" scheme="http://yoursite.com/categories/Java/"/>
    
    
      <category term="Java" scheme="http://yoursite.com/tags/Java/"/>
    
      <category term="Netty" scheme="http://yoursite.com/tags/Netty/"/>
    
  </entry>
  
  <entry>
    <title>设计模式之工厂模式：简单工厂&amp;工厂方法&amp;抽象工厂</title>
    <link href="http://yoursite.com/2019/10/12/factory/"/>
    <id>http://yoursite.com/2019/10/12/factory/</id>
    <published>2019-10-12T13:42:21.366Z</published>
    <updated>2019-07-14T08:25:24.819Z</updated>
    
    <content type="html"><![CDATA[<h3 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h3><p>设计模式有3大类，分为：<strong>创建型模式</strong>、<strong>结构型模式</strong>和<strong>行为型模式</strong>。工厂模式属于创建型模式，创建型模式提供了一种在创建对象的同时隐藏创建逻辑的方式，而不是使用 new运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。<br><a id="more"></a></p><h3 id="简单工厂"><a href="#简单工厂" class="headerlink" title="简单工厂"></a>简单工厂</h3><p><strong>描述：定义一个类用于创建父类相同的子类对象，由传入参数决定创建哪个子类。</strong><br><img src="/2019/10/12/factory/简单工厂.jpg" alt="简单工厂"><br>举个例子，我喜欢玩游戏。定义一个Game接口，让具体的游戏去实现这个接口<br><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">interface</span> <span class="title">Game</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">play</span><span class="params">()</span></span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">HeartStone</span> <span class="keyword">implements</span> <span class="title">Game</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">play</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"炉石传说，启动！"</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="class"><span class="keyword">class</span> <span class="title">Gwent</span> <span class="keyword">implements</span> <span class="title">Game</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">play</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"昆特牌，启动！"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>现在我们要开始玩了<br><figure class="highlight java"><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="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SimpleFactory</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        Game game = <span class="keyword">new</span> HeartStone();</span><br><span class="line">        game.play();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>现在假设我们的程序是个大型系统，提供游戏启动的代理功能，一开始我们玩的是炉石传说，因此项目中有很多地方的代码都使用 Game game = new HeartStone(); 这样的语句来启动游戏。<br>某天，我觉得炉石传说随机性太大了，辣鸡炉石！咱们去玩昆特牌吧！那么问题来了，想更换启动的游戏，我就得将整个系统的每一处Game game = new HeartStone(); 改成 Game game = new Gwent(); 可见工作量是十分庞大的。<br>简单工厂模式就是为了解决这类问题的：具体玩什么游戏应该是随时变动的需求，不应该在程序中写死具体实例化哪个游戏子类。<br>现在添加一个游戏工厂类<br><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">GameFactory</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String HeartStone = <span class="string">"HeartStone"</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String Gwent = <span class="string">"Gwent"</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> Game <span class="title">playGame</span><span class="params">(String game)</span> </span>&#123;</span><br><span class="line">        Game myGame = <span class="keyword">null</span>;</span><br><span class="line">        <span class="keyword">switch</span> (game) &#123;</span><br><span class="line">            <span class="keyword">case</span> HeartStone:</span><br><span class="line">                myGame = <span class="keyword">new</span> HeartStone();</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            <span class="keyword">case</span> Gwent:</span><br><span class="line">                myGame = <span class="keyword">new</span> Gwent();</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> myGame;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>主程序改为<br><figure class="highlight java"><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="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SimpleFactory</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        Game game = GameFactory.playGame(<span class="string">"Gwent"</span>);</span><br><span class="line">        game.play();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>现在，我们想启动什么游戏，只需要改动playGame中的参数，而这个参数是一个字符串变量，这就意味着，我们还可以以配置文件的方式为这个字符串变量赋值，最终做到，不改动任何一处代码，只修改配置文件中的游戏信息，就可以切换具体实例化哪个游戏。</p><p>简单工厂模式的最大优点在于工厂类中包含了必要的逻辑判断(switch)，根据客户端的选择动态实例化相关的 类，对于客户端来说，去除了与具体产品的依赖。</p><h3 id="工厂方法"><a href="#工厂方法" class="headerlink" title="工厂方法"></a>工厂方法</h3><p><strong>描述：定义一个接口用于创建对象，但是让子类决定初始化哪个类。工厂方法把一个类的初始化下放到子类。</strong><br><img src="/2019/10/12/factory/工厂方法.jpg" alt="工厂方法"><br>在简单工厂模式中，我们发现在添加子类的时候，相应的也需要在工厂类中添加一个判断分支(多加一个case)，是违背了开放-封闭原则的。而工厂方法模式就是主要解决这个问题的。</p><blockquote><p>开放-封闭原则：软件实体(类、模块、函数等)应该可以扩展，但是不可修改。</p></blockquote><p>回到玩游戏的例子，现在我又想玩LOL了，现在我需要添加一个LOL类<br><figure class="highlight java"><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="class"><span class="keyword">class</span> <span class="title">LOL</span> <span class="keyword">implements</span> <span class="title">Game</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">play</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"英雄联盟，启动！"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>在简单工厂模式下，还需要改动GameFactory的代码，添加一个Case,这样修改了源代码，违背了开闭原则。现在将简单工厂模式改成工厂方法模式，把GameFactory改成接口<br><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">interface</span> <span class="title">GameFactory</span> </span>&#123;</span><br><span class="line">    <span class="function">Game <span class="title">playGame</span><span class="params">()</span></span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">HeartStoneFactory</span> <span class="keyword">implements</span> <span class="title">GameFactory</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Game <span class="title">playGame</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> HeartStone();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">GwentFactory</span> <span class="keyword">implements</span> <span class="title">GameFactory</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Game <span class="title">playGame</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> Gwent();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>这样，想添加LOL这个游戏，只需要再添加一个工厂类即可，不用<strong>修改</strong>代码，而是<strong>扩展</strong>代码。<br><figure class="highlight java"><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="class"><span class="keyword">class</span> <span class="title">LOLFactory</span> <span class="keyword">implements</span> <span class="title">GameFactory</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Game <span class="title">playGame</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> LOL();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>主程序为<br><figure class="highlight java"><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="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">FactoryMethod</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        GameFactory gameFactory = <span class="keyword">new</span> LOLFactory();</span><br><span class="line">        Game game = gameFactory.playGame();</span><br><span class="line">        game.play();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>可以看到使用工厂方法模式之后，扩展性变高了，如果想增加一个游戏，只要扩展一个游戏工厂类就可以。但是随之而来的是在系统中增加了复杂度，每增加一个游戏时，都需要增加一个游戏类和工厂类。</p><h3 id="抽象工厂"><a href="#抽象工厂" class="headerlink" title="抽象工厂"></a>抽象工厂</h3><p><strong>描述：为一个产品族提供了统一的创建接口。当需要这个产品族的某一系列的时候，可以从抽象工厂中选出相应的系列创建一个具体的工厂类。</strong><br><img src="/2019/10/12/factory/抽象工厂.png" alt="抽象工厂"><br>抽象工厂模式是一种特殊的工厂方法模式。在上面的玩游戏例子中，游戏工厂接口(GameFactory)的子类，只实例化一种游戏父类(Game)。这时工厂方法模式就可以满足需求。但我们知道，游戏是分为很多种类型的，如MOBA、RPG、TCG等等。<br>现在我们把Game接口拆分成RPGGame接口和CardGame接口，需求变为：GameFactory的子类可以实例化多种游戏父类了(RPGGame、CardGame)。这时候就要用到抽象工厂模式。</p><p>原先我们讨论游戏时，说的是一个很宽泛的概念，我只知道你想玩游戏，不知道你想玩什么类型的游戏。现在给你两个选项：角色扮演游戏和卡牌游戏。有两家知名游戏厂商，波兰蠢驴和暴雪，他们都有角色扮演类游戏和卡牌类游戏。<br>那么众所周知，蠢驴的RPG游戏有巫师系列，卡牌游戏是昆特牌，暴雪的RPG游戏有魔兽世界，卡牌游戏有炉石传说。这些具体的游戏都叫做<strong>产品</strong> 而游戏这个大类则是<strong>产品族</strong>，巫师和昆特牌是蠢驴的产品族；魔兽世界和炉石传说是暴雪的产品族。<br>抽象工厂模式就是描述它们之间的关系的：将同一类的产品子类归为一类，让他们继承同一个接口，(巫师和魔兽世界都是RPG，让它们都继承RPG接口)，然后将不同类的产品归为一族，让不同类的产品都可以被一个工厂子类实例化(魔兽世界和炉石传说是不同类的游戏，但都可以被暴雪公司实例化)。<br>通过代码直观展示：<br>游戏类<br><figure class="highlight java"><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="class"><span class="keyword">interface</span> <span class="title">RPGGame</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">play</span><span class="params">()</span></span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">interface</span> <span class="title">CardGame</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">play</span><span class="params">()</span></span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">HeartStone</span> <span class="keyword">implements</span> <span class="title">CardGame</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">play</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"炉石传说，启动！"</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="class"><span class="keyword">class</span> <span class="title">Gwent</span> <span class="keyword">implements</span> <span class="title">CardGame</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">play</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"昆特牌，启动！"</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="class"><span class="keyword">class</span> <span class="title">WOW</span> <span class="keyword">implements</span> <span class="title">RPGGame</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">play</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"魔兽世界，启动！"</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="class"><span class="keyword">class</span> <span class="title">Witcher</span> <span class="keyword">implements</span> <span class="title">RPGGame</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">play</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"巫师，启动！"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>游戏工厂类<br><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">interface</span> <span class="title">GameFactory</span> </span>&#123;</span><br><span class="line">    <span class="function">RPGGame <span class="title">playRPGGame</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="function">CardGame <span class="title">playCardGame</span><span class="params">()</span></span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">CDProjektRed</span> <span class="keyword">implements</span> <span class="title">GameFactory</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> RPGGame <span class="title">playRPGGame</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> Witcher();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> CardGame <span class="title">playCardGame</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> Gwent();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Blizzard</span> <span class="keyword">implements</span> <span class="title">GameFactory</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> RPGGame <span class="title">playRPGGame</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> WOW();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> CardGame <span class="title">playCardGame</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> HeartStone();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>主程序<br><figure class="highlight java"><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="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">AbstractFactory</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        GameFactory gameFactory1 = <span class="keyword">new</span> CDProjektRed();</span><br><span class="line">        GameFactory gameFactory2 = <span class="keyword">new</span> Blizzard();</span><br><span class="line">        RPGGame game1 = gameFactory1.playRPGGame();</span><br><span class="line">        CardGame game2 = gameFactory1.playCardGame();</span><br><span class="line">        RPGGame game3 = gameFactory2.playRPGGame();</span><br><span class="line">        CardGame game4 = gameFactory2.playCardGame();</span><br><span class="line">        game1.play();</span><br><span class="line">        game2.play();</span><br><span class="line">        game3.play();</span><br><span class="line">        game4.play();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>现在，我们的游戏启动代理系统的功能就被进一步完善了，如果你是蠢驴的舔狗，你只想代理启动蠢驴的游戏，只需要修改一处代码GameFactory gameFactory = new CDProjektRed(); 就可以一键设置为蠢驴游戏全家桶，对于客户端来说，它们并不知道自己会启动哪个厂商的游戏，因为这对它们是透明的，客户端只知道自己启动了RPGGame和CardGame. 如果哪天你又变成暴雪舔狗了，也只需要改动一处代码，客户端就会启动暴雪的游戏族。</p><p>参考资料：</p><ul><li><a href="https://blog.csdn.net/qazwsxpcm/article/details/81141325" target="_blank" rel="noopener">https://blog.csdn.net/qazwsxpcm/article/details/81141325</a></li><li>《大话设计模式》</li></ul>]]></content>
    
    <summary type="html">
    
      &lt;h3 id=&quot;背景&quot;&gt;&lt;a href=&quot;#背景&quot; class=&quot;headerlink&quot; title=&quot;背景&quot;&gt;&lt;/a&gt;背景&lt;/h3&gt;&lt;p&gt;设计模式有3大类，分为：&lt;strong&gt;创建型模式&lt;/strong&gt;、&lt;strong&gt;结构型模式&lt;/strong&gt;和&lt;strong&gt;行为型模式&lt;/strong&gt;。工厂模式属于创建型模式，创建型模式提供了一种在创建对象的同时隐藏创建逻辑的方式，而不是使用 new运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。&lt;br&gt;&lt;/p&gt;
    
    </summary>
    
      <category term="设计模式" scheme="http://yoursite.com/categories/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
    
    
      <category term="DesignPatterns" scheme="http://yoursite.com/tags/DesignPatterns/"/>
    
  </entry>
  
  <entry>
    <title>初次部署SpringBoot项目的记录</title>
    <link href="http://yoursite.com/2019/10/12/ECS-jar-deployment/"/>
    <id>http://yoursite.com/2019/10/12/ECS-jar-deployment/</id>
    <published>2019-10-12T13:42:21.336Z</published>
    <updated>2019-09-03T11:58:09.272Z</updated>
    
    <content type="html"><![CDATA[<p>网上关于SpringBoot部署相关的文章一大堆，依葫芦画瓢就行，但还是会遇到些许坑点，主要还是自己没什么常识，在此简单记录一下部署项目从无到有的全过程吧。</p><p><img src="/2019/10/12/ECS-jar-deployment/部署成功.png" alt="部署成功"></p><a id="more"></a><h3 id="购买和连接服务器"><a href="#购买和连接服务器" class="headerlink" title="购买和连接服务器"></a>购买和连接服务器</h3><p>去阿里云的<a href="https://promotion.aliyun.com/ntms/act/campus2018.html" target="_blank" rel="noopener">云翼计划</a>买个轻量级应用服务器，9.5元一个月，预装环境选择LAMP，这样就省得安装MySQL了。</p><p>之后就可以连接买好的云服务器了，可以直接在阿里云的控制台右上角点击“远程连接”，然后就出现了Linux的命令行界面。这样连接每次都要打开网页登录，但省事。<br><img src="/2019/10/12/ECS-jar-deployment/控制台命令行.png" alt="控制台命令行"></p><p>也可以选择使用SSH客户端连接，Windows要装个OpenSSH客户端，然后命令行输入 ssh 用户名@公网IP 连接即可。<br><img src="/2019/10/12/ECS-jar-deployment/SSH客户端命令行.png" alt="SSH客户端命令行"></p><h3 id="开放配置端口"><a href="#开放配置端口" class="headerlink" title="开放配置端口"></a>开放配置端口</h3><p>连完了之后要开放一些端口供项目访问，需要注意一点，在阿里云控制台 安全-防火墙-添加规则 之后，还需要进入要服务器修改/etc/sysconfig/iptables中的配置。具体参考<a href="https://blog.csdn.net/julielele/article/details/78730796" target="_blank" rel="noopener">这篇博客</a>。</p><h3 id="上传文件"><a href="#上传文件" class="headerlink" title="上传文件"></a>上传文件</h3><p>服务器要获得项目的代码才能部署阿，可以在服务器上用git拉取代码，然后maven打jar包等一些列操作，感觉有点麻烦…第一次部署怕出错，所以就选择本地打完jar包之后上传到服务了。 上传文件的软件有很多，自行选择，我选择了WinSCP。图形界面软件操作就非常简单了。<br><img src="/2019/10/12/ECS-jar-deployment/上传文件.png" alt="上传文件"></p><h3 id="部署项目"><a href="#部署项目" class="headerlink" title="部署项目"></a>部署项目</h3><p>传好jar包，然后就可以部署了，SpringBoot以jar包方式部署的博客网上也一大堆，按部就班就可以，但能不能运行起来，运行起来能不能正常使用所有功能又是另外一回事了。</p><h3 id="自己遇到的问题"><a href="#自己遇到的问题" class="headerlink" title="自己遇到的问题"></a>自己遇到的问题</h3><ul><li><p>linux的MySQL表名区分大小写，自己建表时没有按照数据库命名规范来建表。</p></li><li><p>忘记MySQL密码 -&gt;<a href="https://blog.csdn.net/jiax_gg/article/details/80363828" target="_blank" rel="noopener">解决方案</a></p></li><li><p>Navicat连MySQL报1130，这个是因为系统表user中Host字段有个localhost造成了某些冲突，具体啥原因不懂。-&gt;<a href="https://blog.csdn.net/moqiang02/article/details/19496065" target="_blank" rel="noopener">解决方案</a></p></li><li><p>安装jdk -&gt;<a href="https://blog.csdn.net/qq_42815754/article/details/82968464" target="_blank" rel="noopener">参考文章</a><br><code>yum install -y java-1.8.0-openjdk-devel.x86_64</code></p></li><li><p>项目后台运行 -&gt;<a href="https://blog.csdn.net/xu906722/article/details/82558890" target="_blank" rel="noopener">解决方案</a></p></li><li><p>重装系统后无法SSH连接 -&gt;<a href="https://blog.csdn.net/wd2014610/article/details/79945424" target="_blank" rel="noopener">解决方案</a></p></li><li><p>打jar包后无法访问resource资源问题 -&gt;<a href="blog.csdn.net/baQiWangZhengLiang/article/details/84568846">解决方案</a></p></li><li><p>Docker部署，推送到DockerHub -&gt;<a href="https://www.cnblogs.com/shamo89/p/9201513.html" target="_blank" rel="noopener">参考文章1</a>，<a href="https://www.cnblogs.com/gaving10/p/10339223.html" target="_blank" rel="noopener">参考文章2</a></p></li></ul>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;网上关于SpringBoot部署相关的文章一大堆，依葫芦画瓢就行，但还是会遇到些许坑点，主要还是自己没什么常识，在此简单记录一下部署项目从无到有的全过程吧。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/2019/10/12/ECS-jar-deployment/部署成功.png&quot; alt=&quot;部署成功&quot;&gt;&lt;/p&gt;
    
    </summary>
    
      <category term="SpringBoot" scheme="http://yoursite.com/categories/SpringBoot/"/>
    
    
      <category term="Operation &amp; Maintenance" scheme="http://yoursite.com/tags/Operation-Maintenance/"/>
    
      <category term="SpringBoot" scheme="http://yoursite.com/tags/SpringBoot/"/>
    
  </entry>
  
  <entry>
    <title>设计模式之装饰模式和外观模式</title>
    <link href="http://yoursite.com/2019/10/12/decorator-facade/"/>
    <id>http://yoursite.com/2019/10/12/decorator-facade/</id>
    <published>2019-10-12T13:42:21.316Z</published>
    <updated>2019-07-19T08:36:04.184Z</updated>
    
    <content type="html"><![CDATA[<h3 id="装饰模式"><a href="#装饰模式" class="headerlink" title="装饰模式"></a>装饰模式</h3><p><strong>描述：向某个对象动态地添加更多的功能。修饰模式是除类继承外另一种扩展功能的方法。</strong><br><img src="/2019/10/12/decorator-facade/装饰模式.png" alt="装饰模式"><br><a id="more"></a><br>什么时候使用装饰模式？<br>装饰模式是为已有功能动态添加更多功能的一种方式，当系统需要新功能时，是向旧类添加新的代码。这些新加的代码通常装饰了原有类的<strong>核心职责或主要行为</strong>，在主类中加入了新的字段、方法、逻辑，从而增加了主类的复杂度，而这些新加入的东西仅仅是为了满足一些只在特殊需求下才会执行的行为。<br>装饰模式提供了很好的解决方案，它把每个要装饰的功能放在单独的类中，并让这个类包装它所要装饰的对象，因此，当需要执行特殊行为时，客户端代码就可以根据需要有选择地，按顺序地包装对象。<br>装饰模式有4个重要角色</p><ul><li><p>Component（抽象构件）：它是具体构件和抽象装饰类的共同父类，声明了在具体构件中实现的业务方法，它的引入可以使客户端以一致的方式处理未被装饰的对象以及装饰之后的对象，实现客户端的透明操作。</p></li><li><p>ConcreteComponent（具体构件）：它是抽象构件类的子类，用于定义具体的构件对象，实现了在抽象构件中声明的方法，装饰器可以给它增加额外的职责（方法）。</p></li><li><p>Decorator（抽象装饰类）：它也是抽象构件类的子类，用于给具体构件增加职责，但是具体职责在其子类中实现。它维护一个指向抽象构件对象的引用，通过该引用可以调用装饰之前构件对象的方法，并通过其子类扩展该方法，以达到装饰的目的。</p></li><li><p>ConcreteDecorator（具体装饰类）：它是抽象装饰类的子类，负责向构件添加新的职责。每一个具体装饰类都定义了一些新的行为，它可以调用在抽象装饰类中定义的方法，并可以增加新的方法用以扩充对象的行为。</p></li></ul><p>如果只有一个ConcreteComponent，没有Component，那Decorator可以是ConcreteComponent的一个子类，同理，如果只有一个ConcreteDecorator，那就没有必要建立一个单独的Decorator类，而可以把Decorator和ConcreteDecorator在责任合并成一个类</p><p>举个茶饮品的例子，茶饮品可以分为奶茶和果茶两大主类，我们可以给饮品加料，既可以不加，也可以来份全家福，还可以只选其中几种加料。像这种多变的需求，就可以用装饰模式来装饰茶饮品</p><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">//Component</span></span><br><span class="line"><span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">Tea</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">abstract</span> String <span class="title">getType</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">abstract</span> <span class="keyword">int</span> <span class="title">cost</span><span class="params">()</span></span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//ConcreteComponent</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MilkTea</span> <span class="keyword">extends</span> <span class="title">Tea</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function">String <span class="title">getType</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">"奶茶"</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">cost</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">5</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="class"><span class="keyword">class</span> <span class="title">FruitTea</span> <span class="keyword">extends</span> <span class="title">Tea</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function">String <span class="title">getType</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">"果茶"</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">cost</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">8</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">//Decorator</span></span><br><span class="line"><span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">AddStuff</span> <span class="keyword">extends</span> <span class="title">Tea</span> </span>&#123;</span><br><span class="line">    <span class="keyword">protected</span> Tea tea;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">AddStuff</span><span class="params">(Tea tea)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.tea = tea;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function">String <span class="title">getType</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> tea.getType();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">cost</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> tea.cost();</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">//ConcreteDecorator</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Pearl</span> <span class="keyword">extends</span> <span class="title">AddStuff</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Pearl</span><span class="params">(Tea tea)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(tea);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function">String <span class="title">getType</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">"珍珠"</span> + <span class="keyword">super</span>.getType();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">cost</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">super</span>.cost() + <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="class"><span class="keyword">class</span> <span class="title">Pudding</span> <span class="keyword">extends</span> <span class="title">AddStuff</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Pudding</span><span class="params">(Tea tea)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(tea);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function">String <span class="title">getType</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">"布丁"</span> + <span class="keyword">super</span>.getType();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">cost</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">super</span>.cost() + <span class="number">2</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>主程序</p><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Decorator</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        Tea tea1 = <span class="keyword">new</span> Pudding(<span class="keyword">new</span> Pearl(<span class="keyword">new</span> MilkTea()));</span><br><span class="line">        Tea tea2 = <span class="keyword">new</span> Pearl(<span class="keyword">new</span> Pudding(<span class="keyword">new</span> FruitTea()));</span><br><span class="line">        System.out.println(<span class="string">"点了一杯 "</span> + tea1.getType() + <span class="string">"，售价"</span> + tea1.cost() + <span class="string">"元。"</span>);</span><br><span class="line">        System.out.println(<span class="string">"点了一杯 "</span> + tea2.getType() + <span class="string">"，售价"</span> + tea2.cost() + <span class="string">"元。"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>总结：装饰模式的优点是把类中的装饰功能从类中搬移出去，简化原有类，有效地把类中的核心职责和装饰功能区分开了。</p><h3 id="外观模式"><a href="#外观模式" class="headerlink" title="外观模式"></a>外观模式</h3><p><strong>描述：为子系统中的一组接口提供一个一致的界面， 外观模式定义了一个高层接口，这个接口使得这一子系统更加容易使用。</strong></p><p><img src="/2019/10/12/decorator-facade/外观模式.png" alt="外观模式"></p><p>外观模式是一种使用频率非常高的结构型设计模式，它通过引入一个外观角色来简化客户端与子系统之间的交互，为复杂的子系统调用提供一个统一的入口，降低子系统与客户端的耦合度，且客户端调用非常方便。</p><p>外观模式又称为门面模式，它是一种对象结构型模式。外观模式是迪米特法则的一种具体实现，通过引入一个新的外观角色可以降低原有系统的复杂度，同时降低客户类与子系统的耦合度。</p><p>外观模式包含如下2个角色：</p><ul><li><p>Facade（外观角色）：在客户端可以调用它的方法，在外观角色中可以知道相关的（一个或者多个）子系统的功能和责任；在正常情况下，它将所有从客户端发来的请求委派到相应的子系统去，传递给相应的子系统对象处理。</p></li><li><p>SubSystem（子系统角色）：在软件系统中可以有一个或者多个子系统角色，每一个子系统可以不是一个单独的类，而是一个类的集合，它实现子系统的功能；每一个子系统都可以被客户端直接调用，或者被外观角色调用，它处理由外观类传过来的请求；子系统并不知道外观的存在，对于子系统而言，外观角色仅仅是另外一个客户端而已。</p></li></ul><p>外观模式的目的不是给予子系统添加新的功能接口，而是为了让外部减少与子系统内多个模块的交互，松散耦合，从而让外部能够更简单地使用子系统。</p><p>外观模式的本质是：<strong>封装交互，简化调用</strong>。</p><p>什么时候用外观模式？  </p><ul><li><p>在设计初期阶段，应该有意识的将不同的两个层分离，比如dao和service之间分层，service和controller之间分层。</p></li><li><p>在开发阶段，子系统往往因为不断的重构演化而变得越来越复杂，增加Facade可以提供一个简单的接口，减少它们之间的依赖。</p></li><li><p>维护遗留的大型系统时，可能这个系统以及很难维护和扩展了，增加Facade，来提供设计粗糙或高复杂的遗留代码比较清晰简单的接口，让新系统与Facade对象交互，Facade与遗留代码交互所有复杂的工作。</p></li></ul><p>外观模式十分简单，又常用，是很好的设计模式。因此直接上代码，不多BB</p><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Facade</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        Fund fund = <span class="keyword">new</span> Fund();</span><br><span class="line">        fund.buyFund();</span><br><span class="line">        fund.sellFund();</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">//Facade</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Fund</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> Stock stock;</span><br><span class="line">    <span class="keyword">private</span> NationalDebt nationalDebt;</span><br><span class="line">    <span class="keyword">private</span> Realty realty;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Fund</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        stock = <span class="keyword">new</span> Stock();</span><br><span class="line">        nationalDebt = <span class="keyword">new</span> NationalDebt();</span><br><span class="line">        realty = <span class="keyword">new</span> Realty();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">buyFund</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        stock.buy();</span><br><span class="line">        nationalDebt.buy();</span><br><span class="line">        realty.buy();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">sellFund</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        stock.sell();</span><br><span class="line">        nationalDebt.sell();</span><br><span class="line">        realty.sell();</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">//SubSystem</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Stock</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">sell</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"卖股票"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">buy</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"买股票"</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="class"><span class="keyword">class</span> <span class="title">NationalDebt</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">sell</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"卖国债"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">buy</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"买国债"</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="class"><span class="keyword">class</span> <span class="title">Realty</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">sell</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"卖房产"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">buy</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"买房产"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>参考资料：</p><ul><li><a href="https://blog.csdn.net/wwwdc1012/article/details/82764333" target="_blank" rel="noopener">https://blog.csdn.net/wwwdc1012/article/details/82764333</a></li><li>《大话设计模式》</li></ul>]]></content>
    
    <summary type="html">
    
      &lt;h3 id=&quot;装饰模式&quot;&gt;&lt;a href=&quot;#装饰模式&quot; class=&quot;headerlink&quot; title=&quot;装饰模式&quot;&gt;&lt;/a&gt;装饰模式&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;描述：向某个对象动态地添加更多的功能。修饰模式是除类继承外另一种扩展功能的方法。&lt;/strong&gt;&lt;br&gt;&lt;img src=&quot;/2019/10/12/decorator-facade/装饰模式.png&quot; alt=&quot;装饰模式&quot;&gt;&lt;br&gt;&lt;/p&gt;
    
    </summary>
    
      <category term="设计模式" scheme="http://yoursite.com/categories/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
    
    
      <category term="DesignPatterns" scheme="http://yoursite.com/tags/DesignPatterns/"/>
    
  </entry>
  
  <entry>
    <title>设计模式之命令模式和责任链模式</title>
    <link href="http://yoursite.com/2019/10/12/command-responsibility/"/>
    <id>http://yoursite.com/2019/10/12/command-responsibility/</id>
    <published>2019-10-12T13:42:21.286Z</published>
    <updated>2019-07-24T08:52:29.267Z</updated>
    
    <content type="html"><![CDATA[<p>终于来到了行为型模式的学习，这一部分是最多的，慢慢啃吧</p><h3 id="命令模式"><a href="#命令模式" class="headerlink" title="命令模式"></a>命令模式</h3><p><strong>描述：将一个请求封装为一个对象，从而使你可用不同的请求对客户进行参数化；对请求排队或记录请求日志，以及支持可取消的操作。</strong></p><p><img src="/2019/10/12/command-responsibility/命令模式.png" alt="命令模式"><br><a id="more"></a><br>对请求排队或记录请求日志，以及支持可撤销操作时，“行为请求者”和“行为实现者”紧耦合是不合适的。<br>命令模式用来解决“行为请求者”和“行为实现者”紧耦合的问题。</p><p>命令模式的4个角色</p><ul><li><p>Command（命令接口）：用来声明操作的接口。</p></li><li><p>ConcreteCommand（具体命令类）：将一个接收者对象绑定于一个动作，调用接收者相应的操作。</p></li><li><p>Receiver（命令执行对象）：知道如何实施与执行一个请求相关的操作，任何类都可能作为一个接收者。</p></li><li><p>Invoker（命令请求对象）：用于执行这个请求，可以动态的对命令进行控制。</p></li></ul><figure class="highlight java"><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">//Receiver</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Barbecuer</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">bakeMutton</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"烤羊肉串"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">bakeChickenWing</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"烤鸡翅"</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">//Command</span></span><br><span class="line"><span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">Order</span> </span>&#123;</span><br><span class="line">    <span class="keyword">protected</span> Barbecuer barbecuer;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Order</span><span class="params">(Barbecuer barbecuer)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.barbecuer = barbecuer;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">abstract</span> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title">excuteOrder</span><span class="params">()</span></span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//ConcreteCommand</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">BakeMuttonOrder</span> <span class="keyword">extends</span> <span class="title">Order</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">BakeMuttonOrder</span><span class="params">(Barbecuer barbecuer)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(barbecuer);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">excuteOrder</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        barbecuer.bakeMutton();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">BakeChickenWingOrder</span> <span class="keyword">extends</span> <span class="title">Order</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">BakeChickenWingOrder</span><span class="params">(Barbecuer barbecuer)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(barbecuer);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">excuteOrder</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        barbecuer.bakeChickenWing();</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">//Invoker</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Waiter</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> List&lt;Order&gt; orders = <span class="keyword">new</span> ArrayList&lt;&gt;();</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setOrder</span><span class="params">(Order order)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (order <span class="keyword">instanceof</span> BakeChickenWingOrder) &#123;</span><br><span class="line">            System.out.println(<span class="string">"服务员：鸡翅没有了！"</span>);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            orders.add(order);</span><br><span class="line">            System.out.println(<span class="string">"增加订单："</span> + order.getClass().getSimpleName() + <span class="string">"，时间："</span> + <span class="keyword">new</span> Date().toString());</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">public</span> <span class="keyword">void</span> <span class="title">cancelOrder</span><span class="params">(Order order)</span> </span>&#123;</span><br><span class="line">        orders.remove(order);</span><br><span class="line">        System.out.println(<span class="string">"取消订单："</span> + order.getClass().getSimpleName() + <span class="string">"，时间："</span> + <span class="keyword">new</span> Date().toString());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">notifyOrders</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">for</span> (Order order : orders) &#123;</span><br><span class="line">            order.excuteOrder();</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 java"><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="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Command</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        Barbecuer barbecuer=<span class="keyword">new</span> Barbecuer();</span><br><span class="line">        Order order1=<span class="keyword">new</span> BakeChickenWingOrder(barbecuer);</span><br><span class="line">        Order order2=<span class="keyword">new</span> BakeMuttonOrder(barbecuer);</span><br><span class="line">        Order order3=<span class="keyword">new</span> BakeMuttonOrder(barbecuer);</span><br><span class="line"></span><br><span class="line">        Waiter waiter=<span class="keyword">new</span> Waiter();</span><br><span class="line">        waiter.setOrder(order1);</span><br><span class="line">        waiter.setOrder(order2);</span><br><span class="line">        waiter.setOrder(order3);</span><br><span class="line">        waiter.notifyOrders();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>总结命令模式的优点  </p><ol><li>能较容易地设计一个命令队列</li><li>在需要的情况下，可以较容易地将命令记入日志</li><li>允许接收请求的一方决定是否要否决请求</li><li>能容易地实现对请求的撤销和重做</li><li>由于添加新的具体命令类不影响其他类，因此增加新的具体命令类很容易</li></ol><p>最关键的优点是：<strong>把请求一个操作的对象与知道怎么执行一个操作的对象分隔开</strong></p><h3 id="责任链模式"><a href="#责任链模式" class="headerlink" title="责任链模式"></a>责任链模式</h3><p><strong>描述：为解除请求的发送者和接收者之间耦合，而使多个对象都有机会处理这个请求。将这些对象连成一条链，并沿着这条链传递该请求，直到有一个对象处理它。</strong></p><p><img src="/2019/10/12/command-responsibility/责任链模式.png" alt="责任链模式"></p><p>责任链模式的2个角色</p><ul><li><p>Handler（抽象处理者）：它定义了一个处理请求的接口，一般设计为抽象类，由于不同的具体处理者处理请求的方式不同，因此在其中定义了抽象请求处理方法。因为每一个处理者的下家还是一个处理者，因此在抽象处理者中定义了一个抽象处理者类型的对象，作为其对下家的引用。通过该引用，处理者可以连成一条链。</p></li><li><p>ConcreteHandler（具体处理者）：它是抽象处理者的子类，可以处理用户请求，在具体处理者类中实现了抽象处理者中定义的抽象请求处理方法，在处理请求之前需要进行判断，看是否有相应的处理权限，如果可以处理请求就处理它，否则将请求转发给后继者；在具体处理者中可以访问链中下一个对象，以便请求的转发。</p></li></ul><p>纯的责任链模式：</p><p>一个具体处理者对象只能在两个行为中选择一个：要么承担全部责任，要么将责任推给下家，不允许出现某一个具体处理者对象在承担了一部分或全部责任后<br>又将责任向下传递的情况<br>一个请求必须被某一个处理者对象所接收，不能出现某个请求未被任何一个处理者对象处理的情况</p><p>不纯的责任链模式：</p><p>允许某个请求被一个具体处理者部分处理后再向下传递<br>或者一个具体处理者处理完某请求后其后继处理者可以继续处理该请求<br>而且一个请求可以最终不被任何处理者对象所接收</p><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Request</span> </span>&#123;</span><br><span class="line">    String type;</span><br><span class="line">    <span class="keyword">int</span> number;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Request</span><span class="params">(String type, <span class="keyword">int</span> number)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.type = type;</span><br><span class="line">        <span class="keyword">this</span>.number = number;</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">//Handler</span></span><br><span class="line"><span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">Manager</span> </span>&#123;</span><br><span class="line">    <span class="keyword">protected</span> String name;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">protected</span> Manager superior;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Manager</span><span class="params">(String name)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.name = name;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setSuperior</span><span class="params">(Manager superior)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.superior = superior;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">abstract</span> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title">requestApplications</span><span class="params">(Request request)</span></span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//ConcreteHandler</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">CommonManager</span> <span class="keyword">extends</span> <span class="title">Manager</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">CommonManager</span><span class="params">(String name)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(name);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">requestApplications</span><span class="params">(Request request)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (request.type.equals(<span class="string">"请假"</span>) &amp;&amp; request.number &lt;= <span class="number">2</span>) &#123;</span><br><span class="line">            System.out.println(<span class="string">"请求："</span> + request.type + <span class="string">", 数量："</span> + request.number + <span class="string">", 被"</span>+name+<span class="string">"批准。"</span>);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (superior != <span class="keyword">null</span>) &#123;</span><br><span class="line">                superior.requestApplications(request);</span><br><span class="line">            &#125;</span><br><span class="line">        &#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="class"><span class="keyword">class</span> <span class="title">Director</span> <span class="keyword">extends</span> <span class="title">Manager</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Director</span><span class="params">(String name)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(name);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">requestApplications</span><span class="params">(Request request)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (request.type.equals(<span class="string">"请假"</span>) &amp;&amp; request.number &lt;= <span class="number">5</span>) &#123;</span><br><span class="line">            System.out.println(<span class="string">"请求："</span> + request.type + <span class="string">", 数量："</span> + request.number + <span class="string">", 被"</span>+name+<span class="string">"批准。"</span>);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (superior != <span class="keyword">null</span>) &#123;</span><br><span class="line">                superior.requestApplications(request);</span><br><span class="line">            &#125;</span><br><span class="line">        &#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="class"><span class="keyword">class</span> <span class="title">GeneralManager</span> <span class="keyword">extends</span> <span class="title">Manager</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">GeneralManager</span><span class="params">(String name)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(name);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">requestApplications</span><span class="params">(Request request)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (request.type.equals(<span class="string">"请假"</span>)) &#123;</span><br><span class="line">            System.out.println(<span class="string">"请求："</span> + request.type + <span class="string">", 数量："</span> + request.number + <span class="string">", 被"</span>+name+<span class="string">"批准。"</span>);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (superior != <span class="keyword">null</span>) &#123;</span><br><span class="line">                superior.requestApplications(request);</span><br><span class="line">            &#125;</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 java"><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">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Responsibility</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        CommonManager commonManager=<span class="keyword">new</span> CommonManager(<span class="string">"commonManager"</span>);</span><br><span class="line">        Director director=<span class="keyword">new</span> Director(<span class="string">"director"</span>);</span><br><span class="line">        GeneralManager generalManager=<span class="keyword">new</span> GeneralManager(<span class="string">"generalManager"</span>);</span><br><span class="line"></span><br><span class="line">        commonManager.setSuperior(director);</span><br><span class="line">        director.setSuperior(generalManager);</span><br><span class="line"></span><br><span class="line">        Request request1=<span class="keyword">new</span> Request(<span class="string">"请假"</span>, <span class="number">1</span>);</span><br><span class="line">        Request request2=<span class="keyword">new</span> Request(<span class="string">"请假"</span>, <span class="number">4</span>);</span><br><span class="line">        Request request3=<span class="keyword">new</span> Request(<span class="string">"请假"</span>, <span class="number">8</span>);</span><br><span class="line"></span><br><span class="line">        commonManager.requestApplications(request1);</span><br><span class="line">        commonManager.requestApplications(request2);</span><br><span class="line">        commonManager.requestApplications(request3);</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>责任链有什么好处？</p><p>当客户提交一个请求时，请求时沿链传递直至有一个ConcreteHandler对象负责处理它，这就使得接收者和发送者都没有对方的明确信息，且链中的对象自己也不知道链的结构，结果是责任链可以简化对象的相互连接，它们仅保持一个指向其后继者的引用，而不需要保持它所有候选接收者的引用。可以随时地增加或修改处理一个请求的结构，增强了独享指派职责的灵活性。</p><p>但一个请求有可能到了链的末端都得不到处理，或者因为没有正确配置而得不到处理。</p><p>参考资料：</p><ul><li><a href="https://blog.csdn.net/wwwdc1012/article/details/83592323" target="_blank" rel="noopener">https://blog.csdn.net/wwwdc1012/article/details/83592323</a></li><li><a href="https://xuwujing.blog.csdn.net/article/category/7784915" target="_blank" rel="noopener">https://xuwujing.blog.csdn.net/article/category/7784915</a></li><li>《大话设计模式》</li></ul>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;终于来到了行为型模式的学习，这一部分是最多的，慢慢啃吧&lt;/p&gt;
&lt;h3 id=&quot;命令模式&quot;&gt;&lt;a href=&quot;#命令模式&quot; class=&quot;headerlink&quot; title=&quot;命令模式&quot;&gt;&lt;/a&gt;命令模式&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;描述：将一个请求封装为一个对象，从而使你可用不同的请求对客户进行参数化；对请求排队或记录请求日志，以及支持可取消的操作。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/2019/10/12/command-responsibility/命令模式.png&quot; alt=&quot;命令模式&quot;&gt;&lt;br&gt;&lt;/p&gt;
    
    </summary>
    
      <category term="设计模式" scheme="http://yoursite.com/categories/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
    
    
      <category term="DesignPatterns" scheme="http://yoursite.com/tags/DesignPatterns/"/>
    
  </entry>
  
  <entry>
    <title>java集合类归纳</title>
    <link href="http://yoursite.com/2019/10/12/collection-map/"/>
    <id>http://yoursite.com/2019/10/12/collection-map/</id>
    <published>2019-10-12T13:42:21.266Z</published>
    <updated>2019-09-02T05:47:30.919Z</updated>
    
    <content type="html"><![CDATA[<p>粗略看了下集合类的常用类，没耐心看源码，看了到面试那会也忘了，就简单记录下各个类的基本特点吧(摸鱼真爽)</p><p>先来张架构图撑下场面，好看的<br><img src="/2019/10/12/collection-map/Collection架构.jpg" alt="Collection架构"></p><a id="more"></a><h3 id="List"><a href="#List" class="headerlink" title="List"></a>List</h3><p>接口特性：有序的，元素可重复，允许元素为null</p><h4 id="ArrayList-🔥"><a href="#ArrayList-🔥" class="headerlink" title="ArrayList 🔥"></a>ArrayList 🔥</h4><ul><li>底层结构是<strong>数组</strong>，初始容量为10，每次扩容1.5倍</li><li>在增删时，用的是数组复制System.arraycopy方法，是native方法，效率不错</li><li>适合随机查找和遍历，不适合插入和删除</li><li>非线程安全</li></ul><h4 id="LinkedList-🔥"><a href="#LinkedList-🔥" class="headerlink" title="LinkedList 🔥"></a>LinkedList 🔥</h4><ul><li>底层结构是<strong>双向链表</strong></li><li>实现Deque接口，可以像栈和队列操作它</li><li>适合插入和删除，不适合查找</li><li>非线程安全</li></ul><h4 id="Vector"><a href="#Vector" class="headerlink" title="Vector"></a>Vector</h4><ul><li>底层结构是<strong>数组</strong>，初始容量为10，每次扩容2倍</li><li>线程安全，但不推荐，可以使用List list = Collections.synchronizedList(new ArrayList(…));</li></ul><h3 id="Map"><a href="#Map" class="headerlink" title="Map"></a>Map</h3><p>存储key-value键值对，没有实现Collection接口</p><h4 id="HashMap-🔥"><a href="#HashMap-🔥" class="headerlink" title="HashMap 🔥"></a>HashMap 🔥</h4><ul><li>底层结构是<strong>散列表+红黑树</strong>，初始容量为16，装载因子0.75，每次扩容2倍</li><li>key不可重复，允许key和value为null</li><li>存储无序</li><li>非线程安全</li></ul><h4 id="LinkedHashMap"><a href="#LinkedHashMap" class="headerlink" title="LinkedHashMap"></a>LinkedHashMap</h4><ul><li>底层结构是<strong>散列表+红黑树+双向链表</strong>，父类是HashMap</li><li>允许key和value为null</li><li>插入有序</li><li>提供两种遍历顺序，访问顺序和插入顺序（默认）</li><li>非线程安全</li></ul><h4 id="TreeMap-🔥"><a href="#TreeMap-🔥" class="headerlink" title="TreeMap 🔥"></a>TreeMap 🔥</h4><ul><li>底层结构是<strong>红黑树</strong></li><li>不允许key为null</li><li>使用Comparator/Comparable实现排序</li><li>可以根据key进行排序，默认使用key的自然排序</li><li>非线程安全</li></ul><h4 id="Hashtable"><a href="#Hashtable" class="headerlink" title="Hashtable"></a>Hashtable</h4><ul><li>底层结构是<strong>散列表</strong></li><li>不允许key或value为null</li><li>线程安全，但效率低，已过时</li></ul><h4 id="ConcurrentHashMap"><a href="#ConcurrentHashMap" class="headerlink" title="ConcurrentHashMap"></a>ConcurrentHashMap</h4><ul><li>底层结构是<strong>散列表+红黑树</strong></li><li>key和value都不能为null</li><li>使用锁分段技术保证线程安全</li></ul><h3 id="Set"><a href="#Set" class="headerlink" title="Set"></a>Set</h3><p>Set接口特性：不允许重复的数据。检索效率低下，删除和插入效率高</p><h4 id="HashSet-🔥"><a href="#HashSet-🔥" class="headerlink" title="HashSet 🔥"></a>HashSet 🔥</h4><ul><li>底层结构是<strong>哈希表+红黑树</strong></li><li>无序</li><li>数据可为null</li><li>非线程安全</li></ul><h4 id="TreeSet"><a href="#TreeSet" class="headerlink" title="TreeSet"></a>TreeSet</h4><ul><li>底层结构是<strong>红黑树</strong></li><li>有序，可实现排序功能</li><li>数据不可为null</li><li>非线程安全</li></ul><h4 id="LinkedHashSet"><a href="#LinkedHashSet" class="headerlink" title="LinkedHashSet"></a>LinkedHashSet</h4><ul><li>底层结构是<strong>哈希表+双向链表</strong>，父类是HashSet</li><li>无序</li><li>数据可为null</li><li>写入比HashSet强，新增和删除比HashSet差</li><li>非线程安全</li></ul><p>参考链接</p><ul><li><p><a href="https://www.jianshu.com/p/0d5d18ee7905" target="_blank" rel="noopener">https://www.jianshu.com/p/0d5d18ee7905</a></p></li><li><p><a href="https://blog.csdn.net/qazwsxpcm/article/details/79703898" target="_blank" rel="noopener">https://blog.csdn.net/qazwsxpcm/article/details/79703898</a></p></li></ul>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;粗略看了下集合类的常用类，没耐心看源码，看了到面试那会也忘了，就简单记录下各个类的基本特点吧(摸鱼真爽)&lt;/p&gt;
&lt;p&gt;先来张架构图撑下场面，好看的&lt;br&gt;&lt;img src=&quot;/2019/10/12/collection-map/Collection架构.jpg&quot; alt=&quot;Collection架构&quot;&gt;&lt;/p&gt;
    
    </summary>
    
      <category term="Java" scheme="http://yoursite.com/categories/Java/"/>
    
    
      <category term="Basic" scheme="http://yoursite.com/tags/Basic/"/>
    
      <category term="Java" scheme="http://yoursite.com/tags/Java/"/>
    
  </entry>
  
  <entry>
    <title>设计模式之建造者模式和原型模式(深/浅复制问题)</title>
    <link href="http://yoursite.com/2019/10/12/builder-deep_clone/"/>
    <id>http://yoursite.com/2019/10/12/builder-deep_clone/</id>
    <published>2019-10-12T13:42:21.246Z</published>
    <updated>2019-07-22T16:44:11.940Z</updated>
    
    <content type="html"><![CDATA[<h3 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h3><p>建造者模式和原型模式属于创建型模式，建造者模式和工厂模式有相似之处，他们都可以动态获得不同产品的类型，这些具体产品都来源于一个产品父类，而建造者模式更加注重产品内部组件的装配过程。原型模式则涉及到对象的深复制和浅复制的问题。</p><h3 id="建造者模式"><a href="#建造者模式" class="headerlink" title="建造者模式"></a>建造者模式</h3><p><strong>描述：将一个复杂对象的构建与它的表示分离，使得同样的构建过程可以创建不同的表示。</strong><br><img src="/2019/10/12/builder-deep_clone/建造者模式.png" alt="建造者模式"><br><a id="more"></a><br>建造者模式又称生成器模式，可以将一个产品的内部表象与产品的生成过程分割开来，从而使一个建造过程生成具有不同内部表象的产品对象。如果我们用了建造者模式，那么用户只需指定建造的类型就可以得到它们，而具体的建造细节就不需要知道了。</p><p>举个例子，玩RPG游戏时要创建一个角色然后开始探险。角色有很多类型：战士，射手，法师，刺客等等。他们都有一些相似的共有属性，比如，用什么武器，应该穿什么防具，初始血量、法力值等等。所以，<strong>创建这些角色的过程都大体相似</strong>，依次设置这些共有属性的值。对于玩家而言，他们只需要在创建角色页面选择自己要玩的角色，并不关心后台是如何生成这些角色的。</p><p>定义一个角色类<br><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Character</span> </span>&#123;</span><br><span class="line">    String weapon;</span><br><span class="line">    String guard;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">getWeapon</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> weapon;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setWeapon</span><span class="params">(String weapon)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.weapon = weapon;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">getGuard</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> guard;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setGuard</span><span class="params">(String guard)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.guard = guard;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">show</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"这个角色的武器是："</span> + weapon + <span class="string">"，防具精通："</span> + guard);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>角色建造器接口<br><figure class="highlight java"><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="class"><span class="keyword">interface</span> <span class="title">CharacterBuilder</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">buildWeapon</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">buildGuard</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="function">Character <span class="title">createCharacter</span><span class="params">()</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>这个接口规定了建造器需要设置角色的武器和防具，具体怎么设置，则交给子类具体的建造器。</p><p>具体角色的建造器<br><figure class="highlight java"><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="class"><span class="keyword">class</span> <span class="title">SwordsmanBuilder</span> <span class="keyword">implements</span> <span class="title">CharacterBuilder</span> </span>&#123;</span><br><span class="line">    Character character = <span class="keyword">new</span> Character();</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">buildWeapon</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        character.setWeapon(<span class="string">"残破的剑"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">buildGuard</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        character.setGuard(<span class="string">"重甲"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Character <span class="title">createCharacter</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> character;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">GunnerBuilder</span> <span class="keyword">implements</span> <span class="title">CharacterBuilder</span> </span>&#123;</span><br><span class="line">    Character character = <span class="keyword">new</span> Character();</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">buildWeapon</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        character.setWeapon(<span class="string">"没子弹的枪"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">buildGuard</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        character.setGuard(<span class="string">"皮甲"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Character <span class="title">createCharacter</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> character;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>还有一个建造者模式核心的类，Director。用来指挥具体的建造过程，至于建造的是什么角色，它不用关心，由调用者决定。<br><figure class="highlight java"><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="class"><span class="keyword">class</span> <span class="title">CharacterPanel</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> Character <span class="title">createCharacter</span><span class="params">(CharacterBuilder characterBuilder)</span> </span>&#123;</span><br><span class="line">        characterBuilder.buildWeapon();</span><br><span class="line">        characterBuilder.buildGuard();</span><br><span class="line">        <span class="keyword">return</span> characterBuilder.createCharacter();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>主程序<br><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Builder</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        CharacterPanel characterPanel = <span class="keyword">new</span> CharacterPanel();</span><br><span class="line">        Character swordsman = characterPanel.createCharacter(<span class="keyword">new</span> SwordsmanBuilder());</span><br><span class="line">        Character gunner = characterPanel.createCharacter(<span class="keyword">new</span> GunnerBuilder());</span><br><span class="line">        swordsman.show();</span><br><span class="line">        gunner.show();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>简单的介绍了下建造者模式的运作原理，可以概况为这4点:</p><ul><li><p>Builder：指定一个抽象的接口，规定该产品所需实现部件的创建，并不涉及具体的对象部件的创建。</p></li><li><p>ConcreteBuilder：需实现Builder接口，并且针对不同的逻辑，进行不同方法的创建，最终提供该产品的实例。</p></li><li><p>Director：用来创建复杂对象的部分，对该部分进行完整的创建或者按照一定的规则进行创建。</p></li><li><p>Product：示被构造的复杂对象。</p></li></ul><p>什么时候使用建造者模式?<br>当要创建一些复杂的对象，这些对象内部构建间的建造顺序通常是稳定的，但对象内部的构建通常面临着复杂的变化时。</p><p>优点：<br>使得建造代码与表示代码分离，由于建造者隐藏了该产品是如何组装的，所以若需要改变一个产品的内部表示，只需要再定义一个具体的建造者ConcreteBuilder就可以了。</p><h3 id="原型模式"><a href="#原型模式" class="headerlink" title="原型模式"></a>原型模式</h3><p><strong>描述：用原型实例指定创建对象的种类，并且通过拷贝这些原型,创建新的对象。</strong><br><img src="/2019/10/12/builder-deep_clone/原型模式.png" alt="原型模式"><br>原型模式是从一个对象再创建另外一个可定制的对象，而且不需要知道任何创建的细节。<br>举个简历的例子：简历上有各自个人信息，其中工作经历这个信息又可以包含更具体的信息，如工作时间，工作地点等，因此可以封装成一个类。<br><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Resume</span> <span class="keyword">implements</span> <span class="title">Serializable</span>, <span class="title">Cloneable</span> </span>&#123;</span><br><span class="line">    String name;</span><br><span class="line">    String sex;</span><br><span class="line">    String age;</span><br><span class="line">    WorkExperience workExperience;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Resume</span><span class="params">(String name)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.name = name;</span><br><span class="line">        workExperience = <span class="keyword">new</span> WorkExperience();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setPersonalInfo</span><span class="params">(String sex, String age)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.sex = sex;</span><br><span class="line">        <span class="keyword">this</span>.age = age;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setWorkExperience</span><span class="params">(String workDate, String company)</span> </span>&#123;</span><br><span class="line">        workExperience.workDate = workDate;</span><br><span class="line">        workExperience.company = company;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">toString</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">"Resume&#123;"</span> +</span><br><span class="line">                <span class="string">"name='"</span> + name + <span class="string">'\''</span> +</span><br><span class="line">                <span class="string">", sex='"</span> + sex + <span class="string">'\''</span> +</span><br><span class="line">                <span class="string">", age='"</span> + age + <span class="string">'\''</span> +</span><br><span class="line">                <span class="string">", workExperience="</span> + workExperience +</span><br><span class="line">                <span class="string">'&#125;'</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> Object <span class="title">clone</span><span class="params">()</span> <span class="keyword">throws</span> CloneNotSupportedException </span>&#123;</span><br><span class="line">        Resume resume = (Resume) <span class="keyword">super</span>.clone();</span><br><span class="line">        <span class="keyword">if</span> (workExperience != <span class="keyword">null</span>) &#123;</span><br><span class="line">            resume.workExperience = (WorkExperience) workExperience.clone();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> resume;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">WorkExperience</span> <span class="keyword">implements</span> <span class="title">Serializable</span>, <span class="title">Cloneable</span> </span>&#123;</span><br><span class="line">    String workDate;</span><br><span class="line">    String company;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">toString</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">"WorkExperience&#123;"</span> +</span><br><span class="line">                <span class="string">"workDate='"</span> + workDate + <span class="string">'\''</span> +</span><br><span class="line">                <span class="string">", company='"</span> + company + <span class="string">'\''</span> +</span><br><span class="line">                <span class="string">'&#125;'</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> Object <span class="title">clone</span><span class="params">()</span> <span class="keyword">throws</span> CloneNotSupportedException </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">super</span>.clone();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>如果现在我想要很多份简历，这些简历除了工作经历属性可以因投递公司的不同而有详有略外，其他信息都一样。如果每创建一份简历都要new一次，都要执行一次构造函数，假如构造函数的执行时间很长，那么多次执行构造函数这个初始化操作的效率就很低了。  这时候就可以使用原型模式，实际上java有个Cloneable接口，重写clone()方法即可实现原型模式。原型模式的基础实现可自行了解。  </p><p>主程序：<br><figure class="highlight java"><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">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Prototype</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        Resume resume1 = <span class="keyword">new</span> Resume(<span class="string">"bxy"</span>);</span><br><span class="line">        resume1.setPersonalInfo(<span class="string">"male"</span>, <span class="string">"21"</span>);</span><br><span class="line">        resume1.setWorkExperience(<span class="string">"2017-2021"</span>, <span class="string">"NUIST"</span>);</span><br><span class="line"></span><br><span class="line">        System.out.println(resume1.toString());</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            Resume resume2 = (Resume) resume1.clone();</span><br><span class="line">            resume2.setWorkExperience(<span class="string">"2017-2019"</span>,<span class="string">"A公司"</span>);</span><br><span class="line"></span><br><span class="line">            Resume resume3 = (Resume) resume1.clone();</span><br><span class="line">            resume3.setWorkExperience(<span class="string">"2017-2018"</span>,<span class="string">"B公司"</span>);</span><br><span class="line"></span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e)&#123;</span><br><span class="line">            e.printStackTrace();</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>使用场景：<br>当需要创建多个对象，而这些对象有很多初始化信息都不变。<br>原型模式的好处是不用重新初始化对象，而是动态地获得对象运行时的状态。</p><p>既然原型模式是为了克隆对象而生的，一个对象的属性有基本类型(值类型)和非基本类型(引用类型)。对于非基本类型属性的复制就引出了深复制和浅复制的问题。</p><h4 id="浅复制"><a href="#浅复制" class="headerlink" title="浅复制"></a>浅复制</h4><blockquote><p>创建一个新对象，新对象的属性和原来对象完全相同，对于非基本类型属性，仍指向原有属性所指向的对象的内存地址。</p></blockquote><p>在上述例子中，WorkExperience是非基本属性，在Resume对象中存的是引用。浅复制的Resume2和原来的Resume1中的WorkExperience都指向同一个对象。</p><h4 id="深复制"><a href="#深复制" class="headerlink" title="深复制"></a>深复制</h4><blockquote><p>创建一个新对象，属性中引用的其他对象也会被克隆，不再指向原有对象地址。</p></blockquote><p>在一些特定的场合，我们会需要深复制。即希望Resume2和Resume1中的WorkExperience指向不同的对象。为了做到这点，可以让待复制对象的非基本类型的对象也实现Cloneable接口，也可以使用对象流将对象写出流再读出。<br><figure class="highlight java"><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="comment">//深复制实现1</span></span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">protected</span> Object <span class="title">clone</span><span class="params">()</span> <span class="keyword">throws</span> CloneNotSupportedException </span>&#123;</span><br><span class="line">    Resume resume = (Resume) <span class="keyword">super</span>.clone();</span><br><span class="line">    <span class="keyword">if</span> (workExperience != <span class="keyword">null</span>) &#123;</span><br><span class="line">        resume.workExperience = (WorkExperience) workExperience.clone();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> resume;</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">ByteArrayOutputStream byteOut=<span class="keyword">new</span> ByteArrayOutputStream();</span><br><span class="line">ObjectOutputStream objOut=<span class="keyword">new</span> ObjectOutputStream(byteOut);</span><br><span class="line">objOut.writeObject(resume1);</span><br><span class="line"></span><br><span class="line">ByteArrayInputStream byteIn=<span class="keyword">new</span> ByteArrayInputStream(byteOut.toByteArray());</span><br><span class="line">ObjectInputStream objIn=<span class="keyword">new</span> ObjectInputStream(byteIn);</span><br><span class="line">Resume resume3=(Resume)objIn.readObject();</span><br><span class="line">System.out.println(resume1.workExperience == resume3.workExperience);</span><br></pre></td></tr></table></figure></p><p>在选择深复制方法时，应根据对象的复杂程度，如引用类型属性是否有多层引用类型属性关系。如果对象只有一层或者两层引用类型的属性，让引用类型的对象也实现Cloneable接口较为方便，反之则使用对象流。</p><p>参考资料：</p><ul><li><a href="https://blog.csdn.net/qazwsxpcm/article/details/81292529" target="_blank" rel="noopener">https://blog.csdn.net/qazwsxpcm/article/details/81292529</a></li><li><a href="https://www.cnblogs.com/liqiangchn/p/9465186.html" target="_blank" rel="noopener">https://www.cnblogs.com/liqiangchn/p/9465186.html</a></li><li>《大话设计模式》</li></ul>]]></content>
    
    <summary type="html">
    
      &lt;h3 id=&quot;背景&quot;&gt;&lt;a href=&quot;#背景&quot; class=&quot;headerlink&quot; title=&quot;背景&quot;&gt;&lt;/a&gt;背景&lt;/h3&gt;&lt;p&gt;建造者模式和原型模式属于创建型模式，建造者模式和工厂模式有相似之处，他们都可以动态获得不同产品的类型，这些具体产品都来源于一个产品父类，而建造者模式更加注重产品内部组件的装配过程。原型模式则涉及到对象的深复制和浅复制的问题。&lt;/p&gt;
&lt;h3 id=&quot;建造者模式&quot;&gt;&lt;a href=&quot;#建造者模式&quot; class=&quot;headerlink&quot; title=&quot;建造者模式&quot;&gt;&lt;/a&gt;建造者模式&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;描述：将一个复杂对象的构建与它的表示分离，使得同样的构建过程可以创建不同的表示。&lt;/strong&gt;&lt;br&gt;&lt;img src=&quot;/2019/10/12/builder-deep_clone/建造者模式.png&quot; alt=&quot;建造者模式&quot;&gt;&lt;br&gt;&lt;/p&gt;
    
    </summary>
    
      <category term="设计模式" scheme="http://yoursite.com/categories/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
    
    
      <category term="DesignPatterns" scheme="http://yoursite.com/tags/DesignPatterns/"/>
    
  </entry>
  
  <entry>
    <title>设计模式之适配器模式和桥接模式</title>
    <link href="http://yoursite.com/2019/10/12/adapter-bridge/"/>
    <id>http://yoursite.com/2019/10/12/adapter-bridge/</id>
    <published>2019-10-12T13:42:21.226Z</published>
    <updated>2019-07-17T13:23:49.225Z</updated>
    
    <content type="html"><![CDATA[<p>学完了创建型模式，该学结构型模式了。</p><h3 id="适配器模式"><a href="#适配器模式" class="headerlink" title="适配器模式"></a>适配器模式</h3><p><strong>描述：将某个类的接口转换成客户端期望的另一个接口表示。适配器模式可以消除由于接口不匹配所造成的类兼容性问题。</strong><br><img src="/2019/10/12/adapter-bridge/适配器模式.jpg" alt="适配器模式"><br><a id="more"></a></p><ul><li>解决什么问题？<br>在软件开发中，系统的数据和行为都正确，但接口不符，我们应该考虑用适配器，目的是使控制范围之外的一个原有对象与某个接口匹配。适配器模式主要应用于希望复用一些现存的类，但是接口又与复用环境要求不一致的情况，比如在需要对早期代码复用一些功能等应用上很有实际价值。</li><li>何时使用适配器？<br>在你想使用一些已经存在的类，这些类的作用和复用环境里其他的类功能相似，但它们的接口不同时，考虑使用适配器模式。客户端代码可以统一调用同一接口，这样更简单、直接、紧凑。</li><li>注意事项：<br>其实使用适配器模式是无奈之举，有点“亡羊补牢”的感觉。我们不应该在设计阶段使用它，因为在初期，没有必要把功能相似的类的接口设计得不同，就算发现有设计不同的地方，也应该及时重构统一接口。当我们因不同开发人员、产品、厂家而造成功能类似而接口不同的情况，<strong>双方都不太容易修改自己的接口时</strong>，才是适配器模式大展拳脚的时候。</li></ul><p>举个翻译器的例子，我们用两种语言说同一句话时，意思是一样的，但说出来，发出的声音肯定有很大区别。这时，我们可以把这句话的意思看作是“功能类似”，而说出的话语看作是“接口不同”，我们假设双方都不能听懂对方的语言，这时候就需要一个翻译器了，它充当适配器的角色。<br>用代码表示如下<br><figure class="highlight java"><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="comment">//Target</span></span><br><span class="line"><span class="class"><span class="keyword">interface</span> <span class="title">Chinese</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">speakChinese</span><span class="params">()</span></span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//ConcreteTarget</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">ChineseSpeaker</span> <span class="keyword">implements</span> <span class="title">Chinese</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">speakChinese</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"你吃午饭了吗"</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">//Adaptee</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">JapaneseSpeaker</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">speakJapanese</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"昼食はもう食べましたか"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>现在这个中国人想听懂日本人说的话，需要一个翻译器(Adapter),它有两种实现方式</p><h4 id="类适配器"><a href="#类适配器" class="headerlink" title="类适配器"></a>类适配器</h4><figure class="highlight java"><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="comment">//Adapter by class</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">JapaneseToChineseTranslatorByClass</span> <span class="keyword">extends</span> <span class="title">JapaneseSpeaker</span> <span class="keyword">implements</span> <span class="title">Chinese</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">speakChinese</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>.speakJapanese();</span><br><span class="line">        System.out.println(<span class="string">"经过翻译，中国人听得懂，表达的意思一样"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="接口适配器"><a href="#接口适配器" class="headerlink" title="接口适配器"></a>接口适配器</h4><figure class="highlight java"><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="comment">//Adapter by interface</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">JapaneseToChineseTranslatorByInterface</span> <span class="keyword">implements</span> <span class="title">Chinese</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> JapaneseSpeaker japaneseSpeaker;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">JapaneseToChineseTranslatorByInterface</span><span class="params">(JapaneseSpeaker japaneseSpeaker)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.japaneseSpeaker = japaneseSpeaker;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">speakChinese</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        japaneseSpeaker.speakJapanese();</span><br><span class="line">        System.out.println(<span class="string">"经过翻译，中国人听得懂，表达的意思一样"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>他们的调用是这样子的<br><figure class="highlight java"><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="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Adapter</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        Chinese chinese = <span class="keyword">new</span> ChineseSpeaker();</span><br><span class="line">        chinese.speakChinese();</span><br><span class="line"></span><br><span class="line">        Chinese chinese1 = <span class="keyword">new</span> JapaneseToChineseTranslatorByInterface(<span class="keyword">new</span> JapaneseSpeaker());</span><br><span class="line">        chinese1.speakChinese();</span><br><span class="line"></span><br><span class="line">        Chinese chinese2 = <span class="keyword">new</span> JapaneseToChineseTranslatorByClass();</span><br><span class="line">        chinese2.speakChinese();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>可以看到，客户端看到的都是Chinese类型的引用，都调用了统一的speakChinese()接口。<br>总结：如果能事先预防接口不同问题，不匹配的问题就不会发生，小接口不统一，及时重构问题不至于扩大，只有碰到无法改变原有设计和代码的情况时，才考虑用适配器，如果无视场合盲目使用，其实是本末倒置了。</p><h3 id="桥接模式"><a href="#桥接模式" class="headerlink" title="桥接模式"></a>桥接模式</h3><p><strong>描述：将一个抽象与实现解耦，以便两者可以独立的变化。</strong><br><img src="/2019/10/12/adapter-bridge/桥接模式.png" alt="桥接模式"><br>字面的意思解读就是通过一个中间的桥梁对两边的东西进行关联起来，但是关联的两者之间又不相互影响。<br>《大话设计模式》在讲到此节时提到了合成/复用原则</p><blockquote><p>合成/复用原则：优先使用对象的合成或聚合，而不是类继承。</p></blockquote><p>合成和聚合都是关联的特殊种类</p><blockquote><p>合成：表示一种强的“拥有”关系，体现了严格的部分和整体关系，部分和整体的生命周期一样。</p></blockquote><blockquote><p>聚合：表示一种弱的“拥有”关系，体现的是A对象可以包含B对象，但B对象不是A对象的一部分。</p></blockquote><p>如下图所示<br><img src="/2019/10/12/adapter-bridge/合成聚合.jpg" alt="合成聚合"><br>盲目地使用继承会造成麻烦，其本质是，继承是一种强耦合结构，父类变，子类必须变。所以我们在用继承时，一定要在是“is-a”的关系再考虑使用，而不是任何时候都去使用。<br>用更详细的语言解释是：<br><strong>对象的继承关系是在编译时就定好了，所以无法在运行时改变从父类继承的实现。子类的实现与它的父类有非常紧密的依赖关系，以至于父类实现中的任何变化必然会导致子类的变化。当需要复用子类时，如果继承下来的实现不适合新的需求，则父类必须重写或被其他更适合的类替换。这种依赖关系限制了灵活性并最终限制了复用性</strong></p><p>举个游戏中的例子，DNF中鬼剑士的武器是剑类。鬼剑士可以转职成4个职业：剑魂，狂战士，阿修罗，鬼泣。剑类也有巨剑，太刀，钝器，短剑之分。这时候就可以把鬼剑士和剑类当做抽象类(虽然鬼剑士不转职时也是一个具体的角色，但在这个语境中我们姑且当他是抽象类)，不同的职业可以使用不同的剑，不同的剑也可以被不同的职业使用。这时候就可以使用桥接模式把角色和武器任意组合了。<br><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">//Implementor</span></span><br><span class="line"><span class="class"><span class="keyword">interface</span> <span class="title">Sword</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">attack</span><span class="params">()</span></span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//ConcreteImplementor</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">GreatSword</span> <span class="keyword">implements</span> <span class="title">Sword</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">attack</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"使用巨剑挥砍"</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="class"><span class="keyword">class</span> <span class="title">TooKnife</span> <span class="keyword">implements</span> <span class="title">Sword</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">attack</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"使用太刀突刺"</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">//Abstraction</span></span><br><span class="line"><span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">Slayer</span> </span>&#123;</span><br><span class="line">    <span class="keyword">protected</span> Sword sword;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">setSword</span><span class="params">(Sword sword)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.sword = sword;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title">attack</span><span class="params">()</span></span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//RefinedAbstraction</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">BladeMaster</span> <span class="keyword">extends</span> <span class="title">Slayer</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">attack</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.print(<span class="string">"剑魂"</span>);</span><br><span class="line">        sword.attack();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Berserker</span> <span class="keyword">extends</span> <span class="title">Slayer</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">attack</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.print(<span class="string">"狂战士"</span>);</span><br><span class="line">        sword.attack();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>主程序<br><figure class="highlight java"><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="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Bridge</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        Slayer bladeMaster = <span class="keyword">new</span> BladeMaster();</span><br><span class="line">        bladeMaster.setSword(<span class="keyword">new</span> TooKnife());</span><br><span class="line">        bladeMaster.attack();</span><br><span class="line"></span><br><span class="line">        Slayer berserker = <span class="keyword">new</span> Berserker();</span><br><span class="line">        berserker.setSword(<span class="keyword">new</span> GreatSword());</span><br><span class="line">        berserker.attack();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>做个总结：对于桥接模式，通俗的理解就是：实现系统可能有多个角度分类，每一种分类都可能发生变化，那么就把这种多角度分离出来让它们独立变化，减少它们之间的耦合。</p><p>参考资料：</p><ul><li><a href="https://blog.csdn.net/qazwsxpcm/article/details/81948697" target="_blank" rel="noopener">https://blog.csdn.net/qazwsxpcm/article/details/81948697</a></li><li>《大话设计模式》</li></ul>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;学完了创建型模式，该学结构型模式了。&lt;/p&gt;
&lt;h3 id=&quot;适配器模式&quot;&gt;&lt;a href=&quot;#适配器模式&quot; class=&quot;headerlink&quot; title=&quot;适配器模式&quot;&gt;&lt;/a&gt;适配器模式&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;描述：将某个类的接口转换成客户端期望的另一个接口表示。适配器模式可以消除由于接口不匹配所造成的类兼容性问题。&lt;/strong&gt;&lt;br&gt;&lt;img src=&quot;/2019/10/12/adapter-bridge/适配器模式.jpg&quot; alt=&quot;适配器模式&quot;&gt;&lt;br&gt;&lt;/p&gt;
    
    </summary>
    
      <category term="设计模式" scheme="http://yoursite.com/categories/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
    
    
      <category term="DesignPatterns" scheme="http://yoursite.com/tags/DesignPatterns/"/>
    
  </entry>
  
</feed>
