<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://toby1024.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://toby1024.github.io/" rel="alternate" type="text/html" /><updated>2026-07-15T04:50:07+00:00</updated><id>https://toby1024.github.io/feed.xml</id><title type="html">Jason’s babble</title><subtitle>houghtful, not talkative.</subtitle><entry><title type="html">如何保证系统“稳定性”</title><link href="https://toby1024.github.io/blog/2026/07/15/how-to-ensure-stability/" rel="alternate" type="text/html" title="如何保证系统“稳定性”" /><published>2026-07-15T00:00:00+00:00</published><updated>2026-07-15T00:00:00+00:00</updated><id>https://toby1024.github.io/blog/2026/07/15/how-to-ensure-stability</id><content type="html" xml:base="https://toby1024.github.io/blog/2026/07/15/how-to-ensure-stability/"><![CDATA[<h1 id="系统稳定性">系统稳定性</h1>
<p>软件系统开发过程一般过程是：需求调研-系统开发-质量测试-发布部署-业务运营/系统运维。我们谈到稳定性，从软件系统的角度来讲需要做到需求理解正确，功能实现得当，功能测试完备，发布计划执行完整，运营和运维监控及时几个方面</p>

<h2 id="需求调研">需求调研</h2>
<p>如何保证需求理解正确，以我的经验来看：</p>

<h3 id="1学会聆听">1.学会聆听</h3>
<p>要理解真正的业务目标，不要被一些花哨的描述混淆视线，我理解的业务目标一般分为上下游协作需求，数据统计分析需求，效率提升需求等，我曾经听过一个笑话：现代互联网软件系统就是excel的导入导出+一个工作群，初听觉得很可笑，细想一下大部分的需求不就是为解决这两个问题吗？因为大量企业软件本质：</p>

<ul>
  <li>数据采集</li>
  <li>数据流转</li>
  <li>数据展示</li>
  <li>协作沟通</li>
</ul>

<h3 id="2及时反馈">2.及时反馈</h3>
<p>我们听取了业务方的需求介绍，厘清了需求目标，需要通过PRD，Demo页面，表格等协作工具及时跟业务方确认需求理解是否正确，实现手段是否满足业务需求，产出结果是否符合业务目标</p>

<p>正确的理解需求是提升系统稳定性的基石</p>

<h2 id="系统开发">系统开发</h2>
<p>软件开发在“古法编程”阶段分为前端工程师和服务端工程师（AI时代vibecoding已经要求人人都是全栈工程师了），我的经验告诉我这两类人的通病是：前端工程师“不看”PRD，服务端工程师“不看”UI设计稿，这里的不看不是说软件工程师们完全不看，而是指不爱看，或者说看不懂，前端工程师习惯了对着高保真进行还原，服务端工程师习惯对着PRD coding，大家往往忽略了业务目标，认为业务是产品的事情，跟工程师没啥关系，只管实现需求，而不在意需求对应的业务目标。</p>

<p>很多团队会要求工程师们在开发之前写技术文档，来进行软件系统架构设计、交互设计，最终设计出的系统满足了“高并发，高可用，低耦合”等技术指标，但面对业务的快速变化时，却显得有些力不从心。</p>

<p>我认为保证系统稳定性的前提是工程师能够真正理解业务目标，具体的手段除了常规的PRD评审来了解需求，也需要接受业务方的业务培训，真正理解开发出来的系统在业务上的价值，面对的用户群体，市场规模，行业前景。</p>

<p>我认为这也是一个软件工程师的竞争力。</p>

<p>在系统开发过程中提供系统稳定性的保证，我认为可以从：</p>

<h3 id="架构设计和技术选型">架构设计和技术选型</h3>
<p>在理解业务目标和需求分解之后，进行适当的技术架构和技术选型，是保证系统开发的第一步，理解了业务的前景和市场规模，可以帮助我们在系统扩展性做好预留空间，业务起步阶段到业务高速扩张阶段，是否做好了幂等、分布式、事务控制等设计，能够通过简单的水平扩展或增加中间件来满足业务增长的性能需求，技术选型是否支持团队扩充后带来的协作问题。</p>

<p>群体决策总是好过单打独斗。</p>

<h3 id="开发过程">开发过程</h3>
<p>不论是TDD还是BDD，还是“只写代码不写测试”，开发的目标总是快速、稳定的交付，最重要的还是需要认真阅读PRD文档真正理解业务需求，不要遗漏需求项。</p>

<p>开发过程中“先想清楚再动手”总是不会错的，在AI时代，提示词中写明需求目标，交互的出入参，总是好过让AI自由发挥，所以技术方案不仅仅是一个任务，而应该是一个动手实现前的行动纲领，我认为好的技术方案必须：明确实现的需求，定义交互的上下游以及出入参，明确异常情况处理。</p>

<p>开发过程中及时和leader、产品、测试同学进行沟通：需求的疑惑、实现手段的处理、延期风险等等问题，不要等到“问题出现我再告诉大家”。</p>

<h3 id="开发交付">开发交付</h3>
<ol>
  <li>必须完成自测</li>
  <li>必须完成冒烟用例测试</li>
  <li>必须有代码审查工具进行代码审查</li>
</ol>

<h2 id="质量测试">质量测试</h2>
<p>我们往往高估了质量测试的能力同时也限制的质量测试权限。</p>

<p>很多公司制定了明确的规章制度要求测试不通过不准上线，往往又在排期面前妥协，带着bug上线“业务先跑起来”已经绑架了一切。</p>

<p>对于系统稳定性，在测试方面我的理解是：</p>

<h3 id="提前介入">提前介入</h3>
<p>在需求评审阶段和技术方案阶段，测试同学就需要介入：理解需求、了解技术实现手段、明确测试目标、准备测试用例和测试工具。</p>

<p>提前沟通清楚业务目标，了解软件工程师们准备怎么干，提前和产品沟通测试覆盖的业务场景是否有遗漏，向工程师提供测试用例会极大的提高交付质量</p>

<h2 id="发布部署">发布部署</h2>
<p>编写发布计划提前审查，制定发布流程规范并严格执行。</p>

<p>发布过程中容易出现的问题：</p>

<ol>
  <li>遗漏关键配置，导致请求错误，风控异常，业务错误造成资损。</li>
  <li>遗漏数据库变更，上线就报错，业务崩溃。</li>
  <li>开发和线上版本兼容问题：底层依赖问题，中间件版本问题，基础设施问题等等，导致上线一切正常，特定业务执行报错，用户兼容性问题导致流量损失。</li>
  <li>监控没有覆盖，新的服务、中间件基础设施没有上监控，异常不被感知。</li>
</ol>

<h2 id="业务运营系统运维">业务运营/系统运维</h2>
<h3 id="业务数据监控">业务数据监控</h3>
<p>业务上线后，需要关注业务数据，保持数据的敏感性，判断业务数据和预期结果的偏差值是否合理。要有明确的统计口径和统计手段，不论是查数据库，还是通过三方的统计工具，自研的数据报表系统等，总之要能看到准确的数据，并对数据的合理性有正确的判断。</p>

<h3 id="系统运行稳定性">系统运行稳定性</h3>
<p>系统运维需要有完整的监控手段和预警机制，以及异常处理和回滚措施，保证系统能用比新业务/功能是否上线更重要。</p>]]></content><author><name></name></author><category term="Architecture" /><category term="稳定性" /><category term="系统设计" /><category term="软件工程" /><category term="架构设计" /><summary type="html"><![CDATA[从需求调研、系统开发、质量测试、发布部署到业务运营和系统运维，探讨如何通过全流程方法保证软件系统稳定性。]]></summary></entry><entry><title type="html">设计模式-创建型</title><link href="https://toby1024.github.io/blog/2020/11/26/designpatterns2/" rel="alternate" type="text/html" title="设计模式-创建型" /><published>2020-11-26T06:47:26+00:00</published><updated>2020-11-26T06:47:26+00:00</updated><id>https://toby1024.github.io/blog/2020/11/26/designpatterns2</id><content type="html" xml:base="https://toby1024.github.io/blog/2020/11/26/designpatterns2/"><![CDATA[<p><img src="/images/DesignPatterns/20201126144901.jpg" alt="image" />
<!--more--></p>
<h2 id="单例模式">单例模式</h2>
<p>单例模式，顾名思义就是一个类只有一个实例。
单例主要的好处就是，1:可以解决资源访问冲突的问题。2:减少资源浪费。</p>

<h3 id="单例的实现方式">单例的实现方式</h3>

<p>1:饿汉式</p>

<p>在类加载的时候就实力化对象，不支持延迟加载。</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">HungryDemo</span> <span class="o">{</span>
    <span class="kd">private</span> <span class="nc">AtomicInteger</span> <span class="n">id</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">AtomicInteger</span><span class="o">(</span><span class="mi">0</span><span class="o">);</span>
    <span class="kd">public</span> <span class="kd">static</span> <span class="nc">HungryDemo</span> <span class="n">instance</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">HungryDemo</span><span class="o">();</span>

    <span class="kd">private</span> <span class="nf">HungryDemo</span><span class="o">()</span> <span class="o">{</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="kd">static</span> <span class="nc">HungryDemo</span> <span class="nf">getInstance</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">return</span> <span class="n">instance</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="kt">int</span> <span class="nf">getId</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">return</span> <span class="n">id</span><span class="o">.</span><span class="na">incrementAndGet</span><span class="o">();</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="nc">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">10</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
            <span class="k">new</span> <span class="nf">Thread</span><span class="o">(()</span> <span class="o">-&gt;</span> <span class="o">{</span>
                <span class="k">try</span> <span class="o">{</span>
                    <span class="nc">Thread</span><span class="o">.</span><span class="na">sleep</span><span class="o">(</span><span class="k">new</span> <span class="nc">Random</span><span class="o">().</span><span class="na">nextInt</span><span class="o">(</span><span class="mi">10000</span><span class="o">));</span>
                <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="nc">InterruptedException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
                    <span class="n">e</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span>
                <span class="o">}</span>
                <span class="nc">HungryDemo</span> <span class="n">instance</span> <span class="o">=</span> <span class="nc">HungryDemo</span><span class="o">.</span><span class="na">getInstance</span><span class="o">();</span>
                <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">instance</span> <span class="o">+</span> <span class="s">"==&gt;"</span> <span class="o">+</span> <span class="n">instance</span><span class="o">.</span><span class="na">getId</span><span class="o">());</span>
            <span class="o">}).</span><span class="na">start</span><span class="o">();</span>
        <span class="o">}</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>2:懒汉式</p>

<p>支持延迟加载，在使用的时候才真正加载。</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">LazyDemo</span> <span class="o">{</span>
    <span class="kd">private</span> <span class="nc">AtomicInteger</span> <span class="n">id</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">AtomicInteger</span><span class="o">(</span><span class="mi">0</span><span class="o">);</span>

    <span class="kd">public</span> <span class="kd">static</span> <span class="nc">LazyDemo</span> <span class="n">instance</span><span class="o">;</span>

    <span class="kd">private</span> <span class="nf">LazyDemo</span><span class="o">()</span> <span class="o">{</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="kd">static</span> <span class="kd">synchronized</span> <span class="nc">LazyDemo</span> <span class="nf">getInstance</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">if</span> <span class="o">(</span><span class="nc">Objects</span><span class="o">.</span><span class="na">isNull</span><span class="o">(</span><span class="n">instance</span><span class="o">))</span> <span class="o">{</span>
            <span class="n">instance</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">LazyDemo</span><span class="o">();</span>
        <span class="o">}</span>
        <span class="k">return</span> <span class="n">instance</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="kt">int</span> <span class="nf">getId</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">return</span> <span class="n">id</span><span class="o">.</span><span class="na">incrementAndGet</span><span class="o">();</span>
    <span class="o">}</span>
    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="nc">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">10</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
            <span class="k">new</span> <span class="nf">Thread</span><span class="o">(()</span> <span class="o">-&gt;</span> <span class="o">{</span>
                <span class="k">try</span> <span class="o">{</span>
                    <span class="nc">Thread</span><span class="o">.</span><span class="na">sleep</span><span class="o">(</span><span class="k">new</span> <span class="nc">Random</span><span class="o">().</span><span class="na">nextInt</span><span class="o">(</span><span class="mi">10000</span><span class="o">));</span>
                <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="nc">InterruptedException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
                    <span class="n">e</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span>
                <span class="o">}</span>
                <span class="nc">LazyDemo</span> <span class="n">instance</span> <span class="o">=</span> <span class="nc">LazyDemo</span><span class="o">.</span><span class="na">getInstance</span><span class="o">();</span>
                <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">instance</span> <span class="o">+</span> <span class="s">"==&gt;"</span> <span class="o">+</span> <span class="n">instance</span><span class="o">.</span><span class="na">getId</span><span class="o">());</span>
            <span class="o">}).</span><span class="na">start</span><span class="o">();</span>
        <span class="o">}</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>饿汉和懒汉的区别就是一个支持延迟加载，一个不支持。懒汉模式还要加锁，防止并发问题。从这一点来看，懒汉模式性能是不如饿汉模式，饿汉模式在类加载时就进行实力化，也可以提前将实力化过程中发生的资源不足等问题提前暴露，而不是等到业务访问后才发现无法进行初始化，引发线上事故。
但这两种都有各自的不足，所以现在有第三种方式。</p>

<p>3:双重检测</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">DoubleCheckDemo</span> <span class="o">{</span>
    <span class="kd">private</span> <span class="nc">AtomicInteger</span> <span class="n">id</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">AtomicInteger</span><span class="o">(</span><span class="mi">0</span><span class="o">);</span>
    <span class="kd">public</span> <span class="kd">static</span> <span class="nc">DoubleCheckDemo</span> <span class="n">instance</span><span class="o">;</span>

    <span class="kd">private</span> <span class="nf">DoubleCheckDemo</span><span class="o">()</span> <span class="o">{</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="kd">static</span> <span class="nc">DoubleCheckDemo</span> <span class="nf">getInstance</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">if</span> <span class="o">(</span><span class="nc">Objects</span><span class="o">.</span><span class="na">isNull</span><span class="o">(</span><span class="n">instance</span><span class="o">))</span> <span class="o">{</span>
            <span class="kd">synchronized</span> <span class="o">(</span><span class="nc">DoubleCheckDemo</span><span class="o">.</span><span class="na">class</span><span class="o">)</span> <span class="o">{</span>
                <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">"加锁操作"</span><span class="o">);</span>
                <span class="k">if</span> <span class="o">(</span><span class="nc">Objects</span><span class="o">.</span><span class="na">isNull</span><span class="o">(</span><span class="n">instance</span><span class="o">))</span> <span class="o">{</span>
                    <span class="n">instance</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">DoubleCheckDemo</span><span class="o">();</span>
                <span class="o">}</span>
            <span class="o">}</span>
        <span class="o">}</span>
        <span class="k">return</span> <span class="n">instance</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="kt">int</span> <span class="nf">getId</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">return</span> <span class="n">id</span><span class="o">.</span><span class="na">incrementAndGet</span><span class="o">();</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="nc">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">10</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
            <span class="k">new</span> <span class="nf">Thread</span><span class="o">(()</span> <span class="o">-&gt;</span> <span class="o">{</span>
                <span class="k">try</span> <span class="o">{</span>
                    <span class="nc">Thread</span><span class="o">.</span><span class="na">sleep</span><span class="o">(</span><span class="k">new</span> <span class="nc">Random</span><span class="o">().</span><span class="na">nextInt</span><span class="o">(</span><span class="mi">10000</span><span class="o">));</span>
                <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="nc">InterruptedException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
                    <span class="n">e</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span>
                <span class="o">}</span>
                <span class="nc">DoubleCheckDemo</span> <span class="n">instance</span> <span class="o">=</span> <span class="nc">DoubleCheckDemo</span><span class="o">.</span><span class="na">getInstance</span><span class="o">();</span>
                <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">instance</span> <span class="o">+</span> <span class="s">"==&gt;"</span> <span class="o">+</span> <span class="n">instance</span><span class="o">.</span><span class="na">getId</span><span class="o">());</span>
            <span class="o">}).</span><span class="na">start</span><span class="o">();</span>
        <span class="o">}</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>双重检测解决了懒汉模式的并发性能问题，同时支持懒加载。</p>

<p>4:静态内部类</p>

<p>静态内部类的实现比双重检测要更加简单，同时也能做到懒加载。</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">InnerClassDemo</span> <span class="o">{</span>

    <span class="kd">private</span> <span class="nc">AtomicInteger</span> <span class="n">id</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">AtomicInteger</span><span class="o">(</span><span class="mi">0</span><span class="o">);</span>

    <span class="kd">private</span> <span class="nf">InnerClassDemo</span><span class="o">()</span> <span class="o">{</span>
    <span class="o">}</span>

    <span class="kd">private</span> <span class="kd">static</span> <span class="kd">class</span> <span class="nc">InnerClassDemoHolder</span> <span class="o">{</span>
        <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">InnerClassDemo</span> <span class="n">instance</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">InnerClassDemo</span><span class="o">();</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="kd">static</span> <span class="nc">InnerClassDemo</span> <span class="nf">getInstance</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">return</span> <span class="nc">InnerClassDemoHolder</span><span class="o">.</span><span class="na">instance</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="kt">int</span> <span class="nf">getId</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">return</span> <span class="n">id</span><span class="o">.</span><span class="na">incrementAndGet</span><span class="o">();</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="nc">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">10</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
            <span class="k">new</span> <span class="nf">Thread</span><span class="o">(()</span> <span class="o">-&gt;</span> <span class="o">{</span>
                <span class="k">try</span> <span class="o">{</span>
                    <span class="nc">Thread</span><span class="o">.</span><span class="na">sleep</span><span class="o">(</span><span class="k">new</span> <span class="nc">Random</span><span class="o">().</span><span class="na">nextInt</span><span class="o">(</span><span class="mi">10000</span><span class="o">));</span>
                <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="nc">InterruptedException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
                    <span class="n">e</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span>
                <span class="o">}</span>
                <span class="nc">InnerClassDemo</span> <span class="n">instance</span> <span class="o">=</span> <span class="nc">InnerClassDemo</span><span class="o">.</span><span class="na">getInstance</span><span class="o">();</span>
                <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">instance</span> <span class="o">+</span> <span class="s">"==&gt;"</span> <span class="o">+</span> <span class="n">instance</span><span class="o">.</span><span class="na">getId</span><span class="o">());</span>
            <span class="o">}).</span><span class="na">start</span><span class="o">();</span>
        <span class="o">}</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">InnerClassDemoHolder</code> 是静态内部类，一开始并不会加载，等到调用<code class="language-plaintext highlighter-rouge">getInstance()</code>方法时才会被加载，这是一种无锁的实现，线程安全由jvm来保证。</p>

<p>5:枚举</p>

<p>采用枚举的方式来实现是最简单的方式。</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">enum</span> <span class="nc">EnumDemo</span> <span class="o">{</span>
    <span class="no">INSTANCE</span><span class="o">;</span>
    <span class="kd">private</span> <span class="nc">AtomicInteger</span> <span class="n">id</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">AtomicInteger</span><span class="o">(</span><span class="mi">0</span><span class="o">);</span>

    <span class="kd">public</span> <span class="kt">int</span> <span class="nf">getId</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">return</span> <span class="n">id</span><span class="o">.</span><span class="na">incrementAndGet</span><span class="o">();</span>
    <span class="o">}</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">EnumDemoTest</span> <span class="o">{</span>
    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="nc">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">10</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
            <span class="k">new</span> <span class="nf">Thread</span><span class="o">(()</span> <span class="o">-&gt;</span> <span class="o">{</span>
                <span class="k">try</span> <span class="o">{</span>
                    <span class="nc">Thread</span><span class="o">.</span><span class="na">sleep</span><span class="o">(</span><span class="k">new</span> <span class="nc">Random</span><span class="o">().</span><span class="na">nextInt</span><span class="o">(</span><span class="mi">10000</span><span class="o">));</span>
                <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="nc">InterruptedException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
                    <span class="n">e</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span>
                <span class="o">}</span>
                <span class="nc">EnumDemo</span> <span class="n">instance</span> <span class="o">=</span> <span class="nc">EnumDemo</span><span class="o">.</span><span class="na">INSTANCE</span><span class="o">;</span>
                <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">instance</span> <span class="o">+</span> <span class="s">"==&gt;"</span> <span class="o">+</span> <span class="n">instance</span><span class="o">.</span><span class="na">getId</span><span class="o">());</span>
            <span class="o">}).</span><span class="na">start</span><span class="o">();</span>
        <span class="o">}</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<h3 id="单例模式的问题">单例模式的问题</h3>
<ul>
  <li>单例对OOP特性的支持不友好，单例对继承，多态特性支持不友好，违背了面向抽象编程的思想。</li>
  <li>单例对代码的扩展性不好。</li>
  <li>单例对代码的测试性不好，单例往往硬编码进业务，后续mock不方便，需要在一开始就对单例进行包装。</li>
  <li>单例不支持有参数的构造函数，单例的设计是无状态的，就无法进行参数传递来构建实例。</li>
</ul>

<h3 id="单例的范围">单例的范围</h3>
<p>如果需要在线程内实现单例，可以使用ThreadLocal并发工具类。</p>

<p>如果需要在集群环境下实现单例，可以借助外部共享存储，例如redis。当进行实例创建的时候，从外部存储加载对象，如果没有则创建后存储回去，这里需要分布式锁的接入，实现起来比较麻烦。</p>

<h2 id="工厂模式">工厂模式</h2>
<h3 id="简单工厂">简单工厂</h3>
<p>简单工厂可以看作是将对象的创建抽取出来独立的一种实现，这种实现方式比较简单，目的就是将创建对象的负责业务代码从业务中剥离出来，实现复用。
缺点是逻辑过于集中，不利于后续扩展。</p>

<h3 id="工厂方法">工厂方法</h3>
<p>工厂方法模式是简单工厂的进一步抽象。使用面向对象的多态性，保持了简单工厂的的优点。将不同的构建逻辑分开到不同的实现中，避免了修改单个逻辑影响所有的构建逻辑。</p>

<h3 id="抽象工厂">抽象工厂</h3>
<p>抽象工厂提供一个抽象，而不是具体实现。符合面向抽象编程思想，增加工厂也不影响具体业务代码。</p>

<h2 id="建造者模式">建造者模式</h2>
<p>比较典型的实现是lombok的builder方法。</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">BuilderDemo</span> <span class="o">{</span>
    <span class="kd">private</span> <span class="nc">String</span> <span class="n">name</span><span class="o">;</span>
    <span class="kd">private</span> <span class="kt">int</span> <span class="n">age</span><span class="o">;</span>
    <span class="kd">private</span> <span class="nc">String</span> <span class="n">phone</span><span class="o">;</span>
    <span class="kd">private</span> <span class="nc">String</span> <span class="n">email</span><span class="o">;</span>

    <span class="kd">private</span> <span class="nf">BuilderDemo</span><span class="o">(</span><span class="nc">BuilderDemoBuilder</span> <span class="n">builder</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">this</span><span class="o">.</span><span class="na">name</span> <span class="o">=</span> <span class="n">builder</span><span class="o">.</span><span class="na">name</span><span class="o">;</span>
        <span class="k">this</span><span class="o">.</span><span class="na">age</span> <span class="o">=</span> <span class="n">builder</span><span class="o">.</span><span class="na">age</span><span class="o">;</span>
        <span class="k">this</span><span class="o">.</span><span class="na">phone</span> <span class="o">=</span> <span class="n">builder</span><span class="o">.</span><span class="na">phone</span><span class="o">;</span>
        <span class="k">this</span><span class="o">.</span><span class="na">email</span> <span class="o">=</span> <span class="n">builder</span><span class="o">.</span><span class="na">email</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="kd">static</span> <span class="kd">class</span> <span class="nc">BuilderDemoBuilder</span> <span class="o">{</span>
        <span class="kd">private</span> <span class="nc">String</span> <span class="n">name</span><span class="o">;</span>
        <span class="kd">private</span> <span class="kt">int</span> <span class="n">age</span><span class="o">;</span>
        <span class="kd">private</span> <span class="nc">String</span> <span class="n">phone</span><span class="o">;</span>
        <span class="kd">private</span> <span class="nc">String</span> <span class="n">email</span><span class="o">;</span>

        <span class="kd">public</span> <span class="nc">BuilderDemo</span> <span class="nf">build</span><span class="o">()</span> <span class="o">{</span>
            <span class="k">return</span> <span class="k">new</span> <span class="nf">BuilderDemo</span><span class="o">(</span><span class="k">this</span><span class="o">);</span>
        <span class="o">}</span>

        <span class="kd">public</span> <span class="nc">BuilderDemoBuilder</span> <span class="nf">setName</span><span class="o">(</span><span class="nc">String</span> <span class="n">name</span><span class="o">)</span> <span class="o">{</span>
            <span class="k">if</span> <span class="o">(</span><span class="nc">Objects</span><span class="o">.</span><span class="na">isNull</span><span class="o">(</span><span class="n">name</span><span class="o">))</span> <span class="o">{</span>
                <span class="k">throw</span> <span class="k">new</span> <span class="nf">IllegalArgumentException</span><span class="o">(</span><span class="s">"name must not null"</span><span class="o">);</span>
            <span class="o">}</span>
            <span class="k">this</span><span class="o">.</span><span class="na">name</span> <span class="o">=</span> <span class="n">name</span><span class="o">;</span>
            <span class="k">return</span> <span class="k">this</span><span class="o">;</span>
        <span class="o">}</span>

        <span class="kd">public</span> <span class="nc">BuilderDemoBuilder</span> <span class="nf">setAge</span><span class="o">(</span><span class="kt">int</span> <span class="n">age</span><span class="o">)</span> <span class="o">{</span>
            <span class="k">this</span><span class="o">.</span><span class="na">age</span> <span class="o">=</span> <span class="n">age</span><span class="o">;</span>
            <span class="k">return</span> <span class="k">this</span><span class="o">;</span>
        <span class="o">}</span>

        <span class="kd">public</span> <span class="nc">BuilderDemoBuilder</span> <span class="nf">setPhone</span><span class="o">(</span><span class="nc">String</span> <span class="n">phone</span><span class="o">)</span> <span class="o">{</span>
            <span class="k">if</span> <span class="o">(</span><span class="nc">Objects</span><span class="o">.</span><span class="na">isNull</span><span class="o">(</span><span class="n">name</span><span class="o">))</span> <span class="o">{</span>
                <span class="k">throw</span> <span class="k">new</span> <span class="nf">IllegalArgumentException</span><span class="o">(</span><span class="s">"phone must not null"</span><span class="o">);</span>
            <span class="o">}</span>
            <span class="k">this</span><span class="o">.</span><span class="na">phone</span> <span class="o">=</span> <span class="n">phone</span><span class="o">;</span>
            <span class="k">return</span> <span class="k">this</span><span class="o">;</span>
        <span class="o">}</span>

        <span class="kd">public</span> <span class="nc">BuilderDemoBuilder</span> <span class="nf">setEmail</span><span class="o">(</span><span class="nc">String</span> <span class="n">email</span><span class="o">)</span> <span class="o">{</span>
            <span class="k">this</span><span class="o">.</span><span class="na">email</span> <span class="o">=</span> <span class="n">email</span><span class="o">;</span>
            <span class="k">return</span> <span class="k">this</span><span class="o">;</span>
        <span class="o">}</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="nc">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
        <span class="nc">BuilderDemo</span> <span class="n">build</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">BuilderDemoBuilder</span><span class="o">().</span><span class="na">setAge</span><span class="o">(</span><span class="mi">18</span><span class="o">).</span><span class="na">setEmail</span><span class="o">(</span><span class="s">"email"</span><span class="o">).</span><span class="na">setName</span><span class="o">(</span><span class="s">"name"</span><span class="o">).</span><span class="na">setPhone</span><span class="o">(</span><span class="s">"13812340987"</span><span class="o">).</span><span class="na">build</span><span class="o">();</span>
        <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">build</span><span class="o">.</span><span class="na">name</span><span class="o">);</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<h2 id="原型模式">原型模式</h2>
<p>基于已有对象原型创建对象。</p>

<p>实现方式分为深拷贝和浅拷贝。深拷贝需要递归负责，创建一个完全独立的对象。浅拷贝只是拷贝引用地址，适合不会变的对象。</p>]]></content><author><name></name></author><category term="DesignPatterns" /><summary type="html"><![CDATA[设计模式-创建型]]></summary></entry><entry><title type="html">设计模式-行为篇</title><link href="https://toby1024.github.io/blog/2020/11/09/designpatterns1/" rel="alternate" type="text/html" title="设计模式-行为篇" /><published>2020-11-09T03:04:08+00:00</published><updated>2020-11-09T03:04:08+00:00</updated><id>https://toby1024.github.io/blog/2020/11/09/designpatterns1</id><content type="html" xml:base="https://toby1024.github.io/blog/2020/11/09/designpatterns1/"><![CDATA[<p><img src="/images/DesignPatterns/202011091107.png" alt="image" />
<!--more--></p>
<h2 id="状态模式">状态模式</h2>

<h3 id="有限状态机">有限状态机</h3>

<p>1:状态转换较多，但每个状态的转换业务不复杂的，推荐使用查表法。 <br />
通过二维数组等方式确定下一个转换的状态，并处理对应业务<br />
2:状态转换业务较复杂的，推荐使用状态模式，使用单独的类来定义状态和状态业务<br />
3:业务非常简单，状态也很少，直接使用if else就可以实现，不需要过度设计<br /></p>

<h3 id="迭代器模式">迭代器模式</h3>

<p>通过模拟游标的滑动来遍历集合中的数据<br />
迭代器需要实现 hasNext，currentItem，next三个方法，用于滑动游标和判断是否迭代结束<br />
遍历过程中不支持元素的添加和删除操作，因为会引起未决结果<br /></p>

<h2 id="访问者模式">访问者模式</h2>

<p>一个或多个操作应用到一组对象上，设计的意图是解耦操作和对象本身，保持类的职责单一、满足开闭原则已应对代码的复杂性。<br />
推荐使用策略模式替代访问者模式。1:访问者模式使用重载实现，容易让访问者代码爆炸。2:不巧妙，不灵活，将一组操作封装在一起，增加功能时需要修改的代码太多。<br />
支持Double Dispatch的语言不需要访问者模式，直接动态执行就可以了<br /></p>

<h2 id="备忘录模式">备忘录模式</h2>

<p>1:备份以便于恢复数据<br />
2:不能破坏封装原则<br />
3:低频率全量备份结合高频率增量备份（redis RDF和AOF）<br /></p>

<h2 id="命令模式">命令模式</h2>

<p>1:将行为封装成对象进行传递<br />
2:和策略模式的区别在于，每个命令执行的是不同的业务；策略模式指同一个业务的不同实现。<br />
例如：控制电灯的开，关属于命令；是交流电还是直流电给电灯供电属于策略。<br /></p>

<h2 id="解释器模式">解释器模式</h2>

<p>典型的场景是编译器的语法解释。<br />
解释器是针对特定的语句进行解释，调用特定的业务代码进行执行的过程。<br /></p>

<h2 id="中介模式">中介模式</h2>

<p>1:接口对象之间的交互关系，将多对多关系通过中介类转换为一对多。<br />
2:中介模式和观察者模式的区别在于，观察者关系是单向固定的，中介则可以是双向的。<br />
3:中介模式接收消息后进行业务编排调度。<br />
4:副作用：可能会产生一个大而全的上帝类，包含类所有的业务代码。（是否可以用命令模式进行拆分？）<br /></p>]]></content><author><name></name></author><category term="DesignPatterns" /><summary type="html"><![CDATA[设计模式-行为篇]]></summary></entry><entry><title type="html">领域驱动实践-从需求到代码</title><link href="https://toby1024.github.io/blog/2020/11/05/ling-yu-qu-dong-shi-jian-cong-xu-qiu-dao-dai-ma/" rel="alternate" type="text/html" title="领域驱动实践-从需求到代码" /><published>2020-11-05T09:37:41+00:00</published><updated>2020-11-05T09:37:41+00:00</updated><id>https://toby1024.github.io/blog/2020/11/05/ling-yu-qu-dong-shi-jian-cong-xu-qiu-dao-dai-ma</id><content type="html" xml:base="https://toby1024.github.io/blog/2020/11/05/ling-yu-qu-dong-shi-jian-cong-xu-qiu-dao-dai-ma/"><![CDATA[<h2 id="业务需求">业务需求</h2>
<p>网约车出行项目mvp
<!--more--></p>
<ul>
  <li>作为乘客我希望创建⼀个出⾏订单，以便于从A地前往B地</li>
  <li>作为司机我希望履⾏⼀个订单，以便于获取收⼊</li>
  <li>作为运营我希望能取消订单，以便于乘客联系不上司机时重新下单</li>
</ul>

<h2 id="传统mvc模式">传统mvc模式</h2>
<p>传统mvc往往基于数据模型进行开发，通过需求分析，确定数据模型，然后在数据模型上做CRUD开发<br />
<img src="/images/ddd/20201105175028.jpg" alt="image" /><br />
server类中聚集类所有的业务代码<br />
<img src="/images/ddd/20201105175158.jpg" alt="image" /><br />
所有的操作都是在操作数据，当业务变得越来越复杂时，service中的代码越来越臃肿，然后根据业务进行模块拆分，但是由于业务纵横交错，后续修改业务代码时，可能会需要修改多个模块。</p>

<h2 id="微服务开发">微服务开发</h2>
<p>微服务的出现一部分原因就是希望将业务划分清楚，解决模块耦合的问题，借助领域驱动设计，我们可以通过一些方法论来进行业务建模和微服务划分。</p>

<h3 id="统一语言">统一语言</h3>
<p>针对不同的角色，同一个事务可能有不同定义。<br /></p>

<ul>
  <li>对于乘客来说，出行订单应该是一个行程。乘客关心的是起点，终点，司机的实时位置，需要支出的费用。<br /></li>
  <li>对于设计来说，出行订单是一笔生意。司机关心的是乘客的位置，目的地，该笔行程的收入、奖励。<br /></li>
  <li>对于运营人员来说，出行订单是一个合约，合约的双方是乘客和司机，运营人员关注合约的履约情况，合约的抽成信息等。<br /></li>
</ul>

<p>针对不同的参与角色，我们定义不同的模型概念。
<img src="/images/ddd/20201106085636.jpg" alt="image" /><br />
通过对业务进行限界上下文划分，很容易就可以进行代码的隔离，不同的上下文分开进行编码，上下文之间的业务调用通过api接口方式进行交互，这样后续的业务演进，系统部署升级以及扩容都相对独立。<br />
但是一个userstor就划分一个微服务肯定是不现实的，我们需要根据业务的相关性来进行，从两个方向来进行组织限界上下文。<br /></p>

<h4 id="语义相关性">语义相关性</h4>
<p>不同的用例存在语义相关性就可以考虑放在一个限界上线文内。例如创建行程，取消行程都跟行程有关，就适合放在一个限界上下文来处理。</p>

<h4 id="功能相关性">功能相关性</h4>
<p>有些用例虽然都是操作相同的对象，但是在功能上有相互的独立性，应该考虑分割成独立的上下文。例如支付行程，虽然也是在操作行程对象，但其实更侧重于支付动作，后续的业务扩展也多围绕在支付上，如增加支付渠道，增加租金统计等和行程关联不大。</p>

<h3 id="demo">DEMO</h3>
<p>代码参考：<a href="https://github.com/toby1024/ddd-demo">ddd-demo</a>
<img src="/images/ddd/20201106091014.jpg" alt="image" /><br /></p>]]></content><author><name></name></author><category term="DDD" /><summary type="html"><![CDATA[领域驱动实践-从需求分析到代码实践]]></summary></entry><entry><title type="html">mysql_covering_index</title><link href="https://toby1024.github.io/blog/2020/11/05/mysql-covering-index/" rel="alternate" type="text/html" title="mysql_covering_index" /><published>2020-11-05T03:37:02+00:00</published><updated>2020-11-05T03:37:02+00:00</updated><id>https://toby1024.github.io/blog/2020/11/05/mysql-covering-index</id><content type="html" xml:base="https://toby1024.github.io/blog/2020/11/05/mysql-covering-index/"><![CDATA[<h2 id="index">index</h2>
<p>索引用于快速查找具有特定列值的行，如果没有索引，mysql必须从第一行开始，扫描全表找到对应的行。表越大，花费越多。如果表中有相关的索引，mysql可以快速确定要在数据文件中查找的位置。大多数mysql索引（primary key，unique，index和fulltext）存储在B-trees。空间数据类型的索引使用R-trees；MEMORY表还支持hash indexes；InnoDB对fulltext使用倒排表。<br />
MySQL使用索引进行以下操作：
<!--more--></p>
<ul>
  <li>通过where条件快速查询。</li>
  <li>最优匹配。如果可以在多个索引之间进行选择，则MySQL通常会使用查找最小行数的索引。</li>
  <li>如果表具有多列索引，那么优化器可以使用索引的任何最左前缀来查找行。举例来说，如果你有一个三列的索引 (col1, col2, col3)，你可以索引的搜索有(col1)， (col1, col2)以及(col1, col2, col3)。</li>
  <li>执行联接时从其他表中检索行。如果声明相同的类型和大小，MySQL可以更有效地在列上使用索引。在这种情况下， VARCHAR与 CHAR被认为是相同的，如果它们被声明为相同的大小。例如， VARCHAR(10)和 CHAR(10)是相同的大小，但是 VARCHAR(10)和 CHAR(15)不是。<br />
  对于非二进制字符串列之间的比较，两个列应使用相同的字符集，不同的字符集将导致索引失效。例如，将utf8列与latin1列进行比较会排除使用索引。<br />
  如果不能不通过转换直接比较值，则比较不同的列（例如，将字符串列与时间或数字列进行比较）可能会阻止使用索引。对于给定的值，如数值列的值为1，它可能比较等于在字符串列，例如任何数量的值 ‘1’，’ 1’， ‘00001’，或’01.e1’，导致索引失效。</li>
  <li>在索引列使用MIN()或 MAX()。mysql预处理器将进行优化，预处理器会检查您是否正在索引出现的所有关键部分上使用。在这种情况下，MySQL为每个表达式执行一次键查找，并将其替换为常量，所有表达式都用常量替换完成后，查询将立即返回。</li>
  <li>排序或分组查询（order by， group by）使用最左匹配索引（order by key1, key2）；如果倒序排序（order by key1, key2 desc）,将按相反顺序使用索引key。</li>
  <li>某些情况下，mysql会直接从索引中获取数据，而不用查询表；例如：只查询索引列数据。注意：当开启列长事务时，可能导致该优化失效，回表查询。</li>
  <li>当全表扫描快于走索引查询时，mysql也不会走索引。</li>
</ul>

<h2 id="covering-index">covering index</h2>
<p>查询的所有列都包含在索引中时，mysql不会扫描表（即不会回表查询），这种情况mysql定义为covering index；InnoDb引擎下，开启事务时，将不会使用这种优化查询。</p>

<h2 id="参考资料">参考资料</h2>
<p><a href="https://dev.mysql.com/doc/refman/8.0/en/optimizing-innodb-transaction-management.html">8.5.2 Optimizing InnoDB Transaction Management</a></p>]]></content><author><name></name></author><category term="mysql" /><summary type="html"><![CDATA[mysql covering index]]></summary></entry><entry><title type="html">领域驱动-事件风暴</title><link href="https://toby1024.github.io/blog/2020/11/04/event-storming/" rel="alternate" type="text/html" title="领域驱动-事件风暴" /><published>2020-11-04T01:24:36+00:00</published><updated>2020-11-04T01:24:36+00:00</updated><id>https://toby1024.github.io/blog/2020/11/04/event-storming</id><content type="html" xml:base="https://toby1024.github.io/blog/2020/11/04/event-storming/"><![CDATA[<blockquote>

  <ul>
    <li>
      <font size="3">佛，在信徒眼里是佛，是希望；在工艺品厂里，对于工人来说，就是一个活，是工作对象，是收入的来源；对于物流公司来说，是货，是责任担当，是运输的标的。</font>
    </li>
    <li>
      <font size="3">不同的事件主题关注的业务事件是不同，领域模型也是不同的。</font>
    </li>
    <li>
      <font size="3">在不同的领域模型中，统一语言。</font>
    </li>
  </ul>
</blockquote>

<!--more-->
<h2 id="事件风暴">事件风暴</h2>
<p>事件风暴是一种快速探索复杂业务领域和对领域建模的实践。
事件风暴从领域关注的业务事件出发，经过团队的充分讨论，统一语言，最终找到领域模型。</p>

<h2 id="如何确定领域关注的业务事件">如何确定领域关注的业务事件</h2>
<p>在通用语言中存在“如果A发生，我们就需要做到B。”，这样的表述，那么A就可以定义成为一个领域事件。<br />
领域事件的命名一般采用 “产生事件的对象名称+完成动作的过去形式” 的形式，这有点类似用户故事的描述。其实用户故事就可以看作是一个领域事件，只是用户故事转换成业务事件时，需要根据业务领域统一语言。</p>

<h2 id="如何开展事件风暴">如何开展事件风暴</h2>
<p>大部分的资料都是站在全局的高度去做事件风暴，将整个系统一起进行事件拆分，划分领域模型。<br />
这样做当然没错，但是实际开发过程中，我们往往1:缺少领域专家；2:缺少足够是时间来做领域分析，事件风暴。这往往导致事件风暴成为理论，而缺少实践。<br />
从我个人观点来看，架构是不断演进的，业务一直在变化，代码也一直在修改，那么，我们就可以从业务点出发，从一个需求出发，做事件风暴。</p>

<h3 id="准备工作">准备工作</h3>
<p>必不可少的便利贴，开放的空间，大白板。<br />
全员参与，包括业务，产品，开发，测试，UI。</p>

<h3 id="核心概念">核心概念</h3>
<p>事件风暴将系统拆分为不同的元素，用不同颜色的便利贴表示。<br />
<img src="/images/ddd/event0.jpg" alt="image" /><br /></p>

<h3 id="统一语言">统一语言</h3>
<p>统一语言非常重要，是沟通的起点，如果一个业务内，包括业务方，产品，开发之间对于概念的表述不统一，会造成沟通不顺畅，甚至出现南辕北辙的现象。由于前期我们是从单个需求出发，可能统一语言定义出来的概念并不准确，或者在命名上有争议，不必介意，统一语言除了准确描述业务对象以外，更主要的功能是上下文的沟通和传递，只要上下文是统一的，业务就可以顺利开展，代码也可以准确编写。如果后续其他需求增加变更，发现之前定义的名称不准确，概念上修改过来就可以了。<br /></p>

<h3 id="事件风暴过程">事件风暴过程</h3>
<p><img src="/images/ddd/event_flow.jpg" alt="image" /><br /></p>

<h4 id="识别领域事件">识别领域事件</h4>
<p>事件风暴以识别领域事件开始。书写领域事件的规则是使用被动语态，按照事件发展顺序贴在白板上。<br />
遇到有争议的事件，不必过多纠结，先标记成热点事件，后续可以重点讨论。<br />
事件一般由名次和动词组合而成，例如：订单已创建；地址已填写。<br /></p>
<blockquote>

</blockquote>
<font size="3">注意：用户的前端操作不是事件，例如：用户提交订单，用户提交表单；这些只是为事件提供数据。</font>

<h4 id="识别参与者">识别参与者</h4>
<p>事件一共有四种参与者：
<img src="/images/ddd/event.jpg" alt="image" /><br /></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>- 角色：触发事件的人
- 策略：触发事件的规则
- 外部系统
- 事件：即当前事件的前置事件 &gt;
</code></pre></div></div>
<font size="3">注意：策略是规则，但规则不是策略。策略是规则+定时器的组合。策略会触发事件，但规则不会。 </font>

<h4 id="识别限界上下文">识别限界上下文</h4>
<p>从两个方向识别限界上下文：<br /></p>

<ul>
  <li>纵向：识别事件流中的事件，倘若相邻两个事件的关系较弱，或者体现了两个非常明显的阶段，就可以对其进行分割。</li>
  <li>横向：梳理是有的事件，根据组成事件的名词和动词去发现事件之间的相关性（相同、相似的名词），然后去提炼一个整体的概念。</li>
</ul>

<p>限界上下文包含场景，角色，活动，知识和能力，不包含UI部分。<br />
限界上下文可以由不连续的事件组成。<br />
限界上下文在命名的时候使用名词来定义。</p>

<h5 id="识别限界上下文遵循的原则">识别限界上下文遵循的原则</h5>
<ul>
  <li>
    <p>单一抽象层次原则
每个限界上下文从概念上应该尽量处于统一抽象层次，不能嵌套。
<img src="/images/ddd/event3.jpg" alt="image" /><br /></p>
  </li>
  <li>
    <p>正交原则
限界上下文之间不能互相影响，互相包含。
<img src="/images/ddd/event4.jpg" alt="image" /><br /></p>
  </li>
</ul>

<h4 id="识别上下文映射">识别上下文映射</h4>
<p>通过事件风暴：</p>
<ul>
  <li>首先识别跨界限界上下文之间相邻事件的关系。</li>
  <li>事件之间是否存在直接触发的关系（参与者为前置事件），需要确定这两个事件所述的限界上下文。</li>
  <li>判断这两个事件所属的限界上下文，谁是主要的。主要的上下文就是下游。通常，前置事件为下游，或是事件的发布者。
    <blockquote>

    </blockquote>
  </li>
</ul>
<font size="3">下游调用上游。<br />
事件依赖关系为单向依赖.<br />
避免下游使用上游的的领域模型（尊奉者模式），由上游来定义参数上和返回值，下游根据情况来决定是否需要定义防腐层。<br />
一般来说，事件如果由自己的角色参与者（角色，策略，外部系统），就与前置事件脱离来关系。</font>

<h4 id="领域分析建模">领域分析建模</h4>
<p>一个事件只能有一个写模型，如果出现多个写模型，要么就是这几个写模型存在包含关系，要么就是写模型遗漏了对应的事件。<br />
对于读模型，要注意它属于那个限界上下文，如果不是当前上下文，则：</p>
<blockquote>

  <ul>
    <li>定义自己的读模型，通过防腐层进行转换，尽量不要迎合下游</li>
    <li>使用ID值对象（用于建立关联）（基本类型偏执）</li>
    <li>读模型和写模型就是领域模型对象
<img src="/images/ddd/event5.jpg" alt="image" /><br /></li>
  </ul>
</blockquote>

<h5 id="识别聚合">识别聚合</h5>
<ul>
  <li>针对领域分享模型，梳理模型对象之间的关系（继承，合成，聚合，依赖，无关系）</li>
  <li>确定领域模型对象是实体还是值对象</li>
  <li>将具有继承或合成关系的领域模型对象放在一个聚合边界内</li>
  <li>根据聚合的本质（概念完整性，概念独立性，不变量Invariant，事务一致性）梳理聚合</li>
</ul>

<h3 id="代码实现">代码实现</h3>
<h4 id="角色构造型">角色构造型</h4>
<p><img src="/images/ddd/event6.jpg" alt="image" /><br />
DomainService来协调单个领域模型/值对象无法完成的业务功能，主要是数据持久化，外部接口调用获取数据等<br />
AppService则负责业务编排<br />
Factory负责封装复杂的创建逻辑，用于创建领域对象</p>]]></content><author><name></name></author><category term="DDD" /><summary type="html"><![CDATA[领域驱动中的事件风暴]]></summary></entry><entry><title type="html">设计模式-面向对象编程</title><link href="https://toby1024.github.io/blog/2020/10/23/design-pattern-rules/" rel="alternate" type="text/html" title="设计模式-面向对象编程" /><published>2020-10-23T10:04:17+00:00</published><updated>2020-10-23T10:04:17+00:00</updated><id>https://toby1024.github.io/blog/2020/10/23/design-pattern-rules</id><content type="html" xml:base="https://toby1024.github.io/blog/2020/10/23/design-pattern-rules/"><![CDATA[<h1 id="面向对象">面向对象</h1>
<p><img src="/images/blogs/mianxiangduixiang.png" alt="image" />
<!--more--></p>
<h2 id="多用组合少用继承">多用组合少用继承</h2>
<p>继承过多过深影响代码可读性，提高维护成本</p>

<h1 id="设计原则与思想">设计原则与思想</h1>
<p><img src="/images/blogs/设计原则与思想.png" alt="image" /></p>

<h2 id="单一职责原则--single-responsibility-principle">单一职责原则  Single Responsibility Principle</h2>

<h3 id="如何理解单一职责">如何理解单一职责</h3>

<p>一个类（方法）只完成一件事<br />
不要设计大而全的类（接口)<br />
高内聚低耦合<br /></p>

<h3 id="如何判断是否职责单一">如何判断是否职责单一</h3>

<p>类或方法无法准确命名<br />
依赖过多外部资源<br />
代码行数过多，类的属性或者函数过多<br />
类的方法都在集中操作某几个属性<br /></p>

<h3 id="拆分粒度是否越细越好">拆分粒度是否越细越好？</h3>

<p>考虑功能的相关性，避免低耦合过度失去高内聚，提高维护成本</p>

<h2 id="开闭原则--openclosed-principle">开闭原则  Open/Closed Principle</h2>

<p>对扩展开放，对修改关闭<br />
合理设计，不要过度设计<br />
职责单一，避免相互影响，保证稳定性<br /></p>

<h2 id="dry">DRY</h2>
<p>重复主要指：代码重复，语义重复，执行重复<br />
代码重复指的是同样的代码出现在多个地方，这种情况可以通过抽取方法，工具类的方式解决<br />
语义重复指的是相同的功能采用了不同的实现方法，出现在多个地方，这种情况可以通过抽取工具类的方法解决<br />
执行重复指的业务上下文执行了同样的逻辑，一般是参数校验，格式化等，需要从业务角度梳理，去除不必要的重复执行<br />
复用需要考虑扩展性，避免和业务耦合，保证代码的稳定性<br /></p>

<h1 id="规范与重构">规范与重构</h1>
<p><img src="/images/blogs/规范与重构.png" alt="image" />
重构是一个持续的过程，重构不光是代码级别的重构，也针对业务架构等<br />
重构的前提是单元测试要覆盖到位</p>

<h2 id="如何评价代码质量的高低">如何评价代码质量的高低</h2>
<p>代码质量高低是通过综合各种因素得到的结论，常见的描述有可读性，可维护性，简洁性，灵活性等，对应的一般都是面向对象的几个特性。所以好的代码应该是面向对象的。</p>

<h2 id="如何写出高质量的代码">如何写出高质量的代码</h2>
<p>需要掌握面向对象的思想，做好设计再写代码，在编码过程中灵活使用设计模式，遵循编码规范，不断的重构完善代码。
<img src="/images/DesignPatterns/20201124.jpg" alt="image" /></p>]]></content><author><name></name></author><category term="DesignPatterns" /><summary type="html"><![CDATA[面向对象：设计原则与思想]]></summary></entry></feed>