<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>领域构建 | Next-gen Tech Edu</title><link>https://ng-tech.icu/books/spring-notes/04.%E5%B7%A5%E7%A8%8B%E5%AE%9E%E8%B7%B5/ddd/%E9%A2%86%E5%9F%9F%E6%9E%84%E5%BB%BA/</link><atom:link href="https://ng-tech.icu/books/spring-notes/04.%E5%B7%A5%E7%A8%8B%E5%AE%9E%E8%B7%B5/ddd/%E9%A2%86%E5%9F%9F%E6%9E%84%E5%BB%BA/index.xml" rel="self" type="application/rss+xml"/><description>领域构建</description><generator>Wowchemy (https://wowchemy.com)</generator><language>zh</language><image><url>https://ng-tech.icu/media/sharing.png</url><title>领域构建</title><link>https://ng-tech.icu/books/spring-notes/04.%E5%B7%A5%E7%A8%8B%E5%AE%9E%E8%B7%B5/ddd/%E9%A2%86%E5%9F%9F%E6%9E%84%E5%BB%BA/</link></image><item><title>对象分类</title><link>https://ng-tech.icu/books/spring-notes/04.%E5%B7%A5%E7%A8%8B%E5%AE%9E%E8%B7%B5/ddd/%E9%A2%86%E5%9F%9F%E6%9E%84%E5%BB%BA/%E5%AF%B9%E8%B1%A1%E5%88%86%E7%B1%BB/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://ng-tech.icu/books/spring-notes/04.%E5%B7%A5%E7%A8%8B%E5%AE%9E%E8%B7%B5/ddd/%E9%A2%86%E5%9F%9F%E6%9E%84%E5%BB%BA/%E5%AF%B9%E8%B1%A1%E5%88%86%E7%B1%BB/</guid><description>&lt;h1 id="java-对象分类">Java 对象分类&lt;/h1>
&lt;h2 id="popersistant-object-持久对象">PO(persistant object) 持久对象&lt;/h2>
&lt;p>在 o/r 映射的时候出现的概念，如果没有 o/r 映射，没有这个概念存在了。通常对应数据模型 ( 数据库 ), 本身还有部分业务逻辑的处理。可以看成是与数据库中的表相映射的 java 对象。最简单的 PO 就是对应数据库中某个表中的一条记录，多个记录可以用 PO 的集合。PO 中应该不包含任何对数据库的操作。&lt;/p>
&lt;h2 id="dodomain-objectentity-领域对象">DO（Domain Object）/Entity 领域对象&lt;/h2>
&lt;p>就是从现实世界中抽象出来的有形或无形的业务实体。一般和数据中的表结构对应。&lt;/p>
&lt;h2 id="totransfer-object数据传输对象">TO(Transfer Object)，数据传输对象&lt;/h2>
&lt;p>在应用程序不同 tie（关系）之间传输的对象&lt;/p>
&lt;h2 id="dtodata-transfer-object数据传输对象">DTO（Data Transfer Object）数据传输对象&lt;/h2>
&lt;p>这个概念来源于 J2EE 的设计模式，原来的目的是为了 EJB 的分布式应用提供粗粒度的数据实体，以减少分布式调用的次数，从而提高分布式调用的性能和降低网络负载，但在这里，我泛指用于展示层与服务层之间的数据传输对象。&lt;/p>
&lt;h2 id="voview-object-值对象">VO(view object) 值对象&lt;/h2>
&lt;p>视图对象，用于展示层，它的作用是把某个指定页面（或组件）的所有数据封装起来。&lt;/p>
&lt;h2 id="bobusiness-object-业务对象">BO(business object) 业务对象&lt;/h2>
&lt;p>从业务模型的角度看, 见 UML 元件领域模型中的领域对象。封装业务逻辑的 java 对象, 通过调用 DAO 方法, 结合 PO,VO 进行业务操作。business object: 业务对象 主要作用是把业务逻辑封装为一个对象。这个对象可以包括一个或多个其它的对象。比如一个简历，有教育经历、工作经历、社会关系等等。我们可以把教育经历对应一个 PO，工作经历对应一个 PO，社会关系对应一个 PO。建立一个对应简历的 BO 对象处理简历，每个 BO 包含这些 PO。这样处理业务逻辑时，我们就可以针对 BO 去处理。&lt;/p>
&lt;h2 id="pojoplain-ordinary-java-object-简单无规则-java-对象">POJO(plain ordinary java object) 简单无规则 java 对象&lt;/h2>
&lt;p>纯的传统意义的 java 对象。就是说在一些 Object/Relation Mapping 工具中，能够做到维护数据库表记录的 persisent object 完全是一个符合 Java Bean 规范的纯 Java 对象，没有增加别的属性和方法。我的理解就是最基本的 Java Bean，只有属性字段及 setter 和 getter 方法！。&lt;/p>
&lt;h2 id="daodata-access-object-数据访问对象">DAO(data access object) 数据访问对象&lt;/h2>
&lt;p>是一个 sun 的一个标准 j2ee 设计模式，这个模式中有个接口就是 DAO，它负持久层的操作。为业务层提供接口。此对象用于访问数据库。通常和 PO 结合使用，DAO 中包含了各种数据库的操作方法。通过它的方法, 结合 PO 对数据库进行相关的操作。夹在业务逻辑与数据库资源中间。配合 VO, 提供数据库的 CRUD 操作&lt;/p>
&lt;h1 id="links">Links&lt;/h1>
&lt;ul>
&lt;li>&lt;a href="https://zhuanlan.zhihu.com/p/77478908" target="_blank" rel="noopener">https://zhuanlan.zhihu.com/p/77478908&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>领域事件</title><link>https://ng-tech.icu/books/spring-notes/04.%E5%B7%A5%E7%A8%8B%E5%AE%9E%E8%B7%B5/ddd/%E9%A2%86%E5%9F%9F%E6%9E%84%E5%BB%BA/%E9%A2%86%E5%9F%9F%E4%BA%8B%E4%BB%B6/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://ng-tech.icu/books/spring-notes/04.%E5%B7%A5%E7%A8%8B%E5%AE%9E%E8%B7%B5/ddd/%E9%A2%86%E5%9F%9F%E6%9E%84%E5%BB%BA/%E9%A2%86%E5%9F%9F%E4%BA%8B%E4%BB%B6/</guid><description>&lt;h1 id="观察者模式">观察者模式&lt;/h1>
&lt;p>大家在日常业务开发工作中相信多多少少遇到过下面这样的几个场景：&lt;/p>
&lt;ul>
&lt;li>当某一个特定事件或动作发生以后，需要执行很多联动动作，如果串行去执行的话太耗时，如果引入消息中间件的话又太重了；&lt;/li>
&lt;li>想要针对不同的传参执行不同的策略，也就是我们常说的策略模式，但 10 个人可能有 10 种不同的写法，夹杂在一起总感觉不那么优雅；&lt;/li>
&lt;li>自己的系统想要调用其他系统提供的能力，但其他系统总是偶尔给你一点“小惊喜”，可能因网络问题报超时异常或被调用的某一台分布式应用机器突然宕机，我们想要优雅无侵入式地引入重试机制。&lt;/li>
&lt;/ul>
&lt;p>其实上面提到的几个典型业务开发场景 Spring 都为我们提供了很好的特性支持，我们只需要引入 Spring 相关依赖就可以方便快速的在业务代码当中使用啦，而不用引入过多的三方依赖包或自己重复造轮子。观察者模式定义对象间的一种一对多的依赖关系，当一个对象的状态发生改变时，所有依赖于它的对象都得到通知并被自动更新，其主要解决一个对象状态改变给其他关联对象通知的问题，保证易用和低耦合。一个典型的应用场景是：当用户注册以后，需要给用户发送邮件，发送优惠券等操作，如下图所示。&lt;/p>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img src="https://s3.ax1x.com/2021/02/28/6CAEDI.md.png" alt="原有流程" loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>&lt;/p>
&lt;p>使用观察者模式后：&lt;/p>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img src="https://s3.ax1x.com/2021/02/28/6CAQ2Q.png" alt="使用观察者模式后的变化" loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>&lt;/p>
&lt;p>UserService 在完成自身的用户注册逻辑之后，仅仅只需要发布一个 UserRegisterEvent 事件，而无需关注其它拓展逻辑。其它 Service 可以自己订阅 UserRegisterEvent 事件，实现自定义的拓展逻辑。Spring 的事件机制主要由 3 个部分组成。&lt;/p>
&lt;ul>
&lt;li>**ApplicationEvent：**通过继承它，实现自定义事件。另外，通过它的 source 属性可以获取事件源，timestamp 属性可以获得发生时间。&lt;/li>
&lt;li>**ApplicationEventPublisher：**通过实现它，来发布变更事件。&lt;/li>
&lt;li>**ApplicationEventListener：**通过实现它，来监听指定类型事件并响应动作。这里就以上面的用户注册为例，来看看代码示例。首先定义用户注册事件 UserRegisterEvent。&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">public&lt;/span> &lt;span class="kd">class&lt;/span> &lt;span class="nc">UserRegisterEvent&lt;/span> &lt;span class="kd">extends&lt;/span> &lt;span class="n">ApplicationEvent&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * 用户名
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">private&lt;/span> &lt;span class="n">String&lt;/span> &lt;span class="n">username&lt;/span>&lt;span class="o">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">public&lt;/span> &lt;span class="nf">UserRegisterEvent&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="n">Object&lt;/span> &lt;span class="n">source&lt;/span>&lt;span class="o">)&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">super&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="n">source&lt;/span>&lt;span class="o">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">public&lt;/span> &lt;span class="nf">UserRegisterEvent&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="n">Object&lt;/span> &lt;span class="n">source&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="n">String&lt;/span> &lt;span class="n">username&lt;/span>&lt;span class="o">)&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">super&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="n">source&lt;/span>&lt;span class="o">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">username&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">username&lt;/span>&lt;span class="o">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">public&lt;/span> &lt;span class="n">String&lt;/span> &lt;span class="nf">getUsername&lt;/span>&lt;span class="o">()&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">username&lt;/span>&lt;span class="o">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>然后定义用户注册服务类，实现 ApplicationEventPublisherAware 接口，从而将 ApplicationEventPublisher 注入进来。从下面代码可以看到，在执行完注册逻辑后，调用了 ApplicationEventPublisher 的 publishEvent(ApplicationEvent event) 方法，发布了 UserRegisterEvent 事件。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@Service&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">public&lt;/span> &lt;span class="kd">class&lt;/span> &lt;span class="nc">UserService&lt;/span> &lt;span class="kd">implements&lt;/span> &lt;span class="n">ApplicationEventPublisherAware&lt;/span> &lt;span class="o">{&lt;/span> &lt;span class="c1">// &amp;lt;1&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kd">private&lt;/span> &lt;span class="n">Logger&lt;/span> &lt;span class="n">logger&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">LoggerFactory&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">getLogger&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="n">getClass&lt;/span>&lt;span class="o">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">private&lt;/span> &lt;span class="n">ApplicationEventPublisher&lt;/span> &lt;span class="n">applicationEventPublisher&lt;/span>&lt;span class="o">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nd">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">public&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">setApplicationEventPublisher&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="n">ApplicationEventPublisher&lt;/span> &lt;span class="n">applicationEventPublisher&lt;/span>&lt;span class="o">)&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">applicationEventPublisher&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">applicationEventPublisher&lt;/span>&lt;span class="o">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">public&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">register&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="n">String&lt;/span> &lt;span class="n">username&lt;/span>&lt;span class="o">)&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// ... 执行注册逻辑
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">logger&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">info&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="s">&amp;#34;[register][执行用户({}) 的注册逻辑]&amp;#34;&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="n">username&lt;/span>&lt;span class="o">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// &amp;lt;2&amp;gt; ... 发布
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">applicationEventPublisher&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">publishEvent&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="k">new&lt;/span> &lt;span class="n">UserRegisterEvent&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="n">username&lt;/span>&lt;span class="o">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>创建邮箱 Service，实现 ApplicationListener 接口，通过 E 泛型设置感兴趣的事件，实现 onApplicationEvent(E event) 方法，针对监听的 UserRegisterEvent 事件，进行自定义处理。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@Service&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">public&lt;/span> &lt;span class="kd">class&lt;/span> &lt;span class="nc">EmailService&lt;/span> &lt;span class="kd">implements&lt;/span> &lt;span class="n">ApplicationListener&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">UserRegisterEvent&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="o">{&lt;/span> &lt;span class="c1">// &amp;lt;1&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kd">private&lt;/span> &lt;span class="n">Logger&lt;/span> &lt;span class="n">logger&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">LoggerFactory&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">getLogger&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="n">getClass&lt;/span>&lt;span class="o">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nd">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nd">@Async&lt;/span>&lt;span class="c1">// &amp;lt;3&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kd">public&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">onApplicationEvent&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="n">UserRegisterEvent&lt;/span> &lt;span class="n">event&lt;/span>&lt;span class="o">)&lt;/span> &lt;span class="o">{&lt;/span> &lt;span class="c1">// &amp;lt;2&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">logger&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">info&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="s">&amp;#34;[onApplicationEvent][给用户({}) 发送邮件]&amp;#34;&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="n">event&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">getUsername&lt;/span>&lt;span class="o">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>创建优惠券 Service，不同于上面的实现 ApplicationListener 接口方式，在方法上，添加 @EventListener 注解，并设置监听的事件为 UserRegisterEvent。这是另一种使用方式。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@Service&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">publicclass&lt;/span> &lt;span class="n">CouponService&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">private&lt;/span> &lt;span class="n">Logger&lt;/span> &lt;span class="n">logger&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">LoggerFactory&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">getLogger&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="n">getClass&lt;/span>&lt;span class="o">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nd">@EventListener&lt;/span>&lt;span class="c1">// &amp;lt;1&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kd">public&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">addCoupon&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="n">UserRegisterEvent&lt;/span> &lt;span class="n">event&lt;/span>&lt;span class="o">)&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">logger&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">info&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="s">&amp;#34;[addCoupon][给用户({}) 发放优惠劵]&amp;#34;&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="n">event&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">getUsername&lt;/span>&lt;span class="o">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>简单来说，发布订阅模式属于广义上的观察者模式，在观察者模式的 Subject 和 Observer 的基础上，引入 Event Channel 这个中介，进一步解耦。图示如下，可以看出，观察者模式更加轻量，通常用于单机，而发布订阅模式相对而言更重一些，通常用于分布式环境下的消息通知场景。&lt;/p>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img src="https://s3.ax1x.com/2021/02/28/6CeMoq.md.png" alt="观察者模式与发布订阅模式" loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>&lt;/p></description></item></channel></rss>