<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Amo&apos;s Blog</title><description>Amo 的部落格</description><link>https://blog.amowu.com/</link><language>zh-tw</language><atom:link href="https://blog.amowu.com/rss.xml" rel="self" type="application/rss+xml"/><item><title>Claude Code + VOICEVOX：二次元女僕語音提示設定教學</title><link>https://blog.amowu.com/posts/2026-07-12-claude-code-voicevox-notification/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2026-07-12-claude-code-voicevox-notification/</guid><description>一步步設定 Claude Code Hooks，從 macOS 內建語音指令到 VOICEVOX 純後台引擎，打造不用盯著螢幕、聽聲音就知道「等待許可」或「任務完成」的語音通知工作流。</description><pubDate>Sun, 12 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;前言&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#前言&quot;&gt;前言&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;在使用 Claude Code 的過程中，經常會遇到需要等待授權或任務完成的情況。為了讓你能夠即時收到這些通知，我們可以利用 Claude Code 提供的 Hooks 來實現語音提示。&lt;/p&gt;
&lt;p&gt;本文將介紹如何使用 macOS 內建的指令以及開源的 VOICEVOX 軟體來實現二次元女僕語音提示，讓你的工作流程更加高效和有趣。&lt;/p&gt;
&lt;h3 id=&quot;demo&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#demo&quot;&gt;Demo&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;等待許可：&lt;/p&gt;
&lt;video&gt;&lt;source src=&quot;https://blog.amowu.com/_astro/demo1.mfhevgyX.mp4&quot; type=&quot;video/mp4&quot;&gt;&lt;/source&gt;&lt;/video&gt;
&lt;p&gt;任務完成：&lt;/p&gt;
&lt;video&gt;&lt;source src=&quot;https://blog.amowu.com/_astro/demo2.Dp3VbxTX.mp4&quot; type=&quot;video/mp4&quot;&gt;&lt;/source&gt;&lt;/video&gt;
&lt;h2 id=&quot;什麼是-claude-code-hooks&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#什麼是-claude-code-hooks&quot;&gt;什麼是 Claude Code Hooks？&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Hooks 是 Claude Code 提供的一項自動化機制，它們就像是 AI 行為的&lt;strong&gt;中間件（Middleware）&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;Hooks 允許開發者在 Claude Code 運行的特定生命週期（例如：執行指令前、修改程式碼後、工作階段開始時），自動觸發自定義的 Shell 指令、HTTP 請求或 AI 提示詞。這提供了對 AI 行為的&lt;strong&gt;確定性控制&lt;/strong&gt;，確保某些安全規則或自動化任務百分之百會執行，而不需要盲目依賴大語言模型（LLM）去「記憶」或「選擇」遵守提示詞。&lt;/p&gt;
&lt;p&gt;設定 Hooks 主要有兩種方式：透過命令列的&lt;strong&gt;互動式指令&lt;/strong&gt;，或是&lt;strong&gt;手動編輯 settings.json 檔案&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;這裡我們選擇手動編輯全域（Global）範圍的 &lt;code&gt;~/.claude/settings.json&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;打開檔案之後，應該會看到 &lt;code&gt;hooks&lt;/code&gt; 這個欄位：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;hooks&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;我們可以新增或修改 &lt;code&gt;Stop&lt;/code&gt; 這個 hook，讓它在任務完成時自動唸出「任務完成」：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;hooks&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;Stop&quot;&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;&quot;matcher&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;&quot;hooks&quot;&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;&quot;type&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;command&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;&quot;command&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;say &lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;任務完成&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3 id=&quot;什麼是-say&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#什麼是-say&quot;&gt;什麼是 &lt;code&gt;say&lt;/code&gt;？&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;say&lt;/code&gt; 是 macOS 內建的指令，它的功能是文字轉語音（Text-to-Speech，TTS）。&lt;/p&gt;
&lt;p&gt;簡單來說，&lt;code&gt;say&lt;/code&gt; 是&lt;strong&gt;你輸入什麼字，Mac 就直接「唸」給你聽&lt;/strong&gt;。它非常適合放在終端機（Terminal）或自動化腳本中。&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;say&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;任務完成&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;你可以直接複製貼上到 Terminal 試試效果。&lt;/p&gt;
&lt;h3 id=&quot;加上提示音效&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#加上提示音效&quot;&gt;加上提示音效&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;除了唸出語音之外，我們還可以加上 macOS 內建的提示音效，讓整個通知更有存在感。&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;afplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/System/Library/Sounds/Glass.aiff&lt;/span&gt;&lt;span&gt; &amp;amp;&amp;amp; &lt;/span&gt;&lt;span&gt;say&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;任務完成&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;同樣，你可以直接複製貼上到 Terminal 試試效果。&lt;/p&gt;
&lt;h3 id=&quot;什麼是-afplay&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#什麼是-afplay&quot;&gt;什麼是 &lt;code&gt;afplay&lt;/code&gt;？&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;afplay&lt;/code&gt;（全稱 Audio File Play）是另一個 macOS 系統內建的&lt;strong&gt;命令列音訊播放器&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;簡單來說，它讓你可以不用打開「音樂」App 或 QuickTime，直接在終端機裡面透過指令來播放音樂或音效檔案。&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://blog.amowu.com/_astro/image1.gutgNC8C_Z24es0.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image1.gutgNC8C_ZnXO1L.webp 640w, https://blog.amowu.com/_astro/image1.gutgNC8C_Z21HmVY.webp 750w, https://blog.amowu.com/_astro/image1.gutgNC8C_Z1V8IKL.webp 828w, https://blog.amowu.com/_astro/image1.gutgNC8C_Z24es0.webp 1032w&quot; alt=&quot;macOS 內建音效檔案清單，位於 /System/Library/Sounds/&quot; width=&quot;1032&quot; height=&quot;548&quot; /&gt;&lt;figcaption&gt;macOS 內建音效&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h3 id=&quot;notification-hook&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#notification-hook&quot;&gt;&lt;code&gt;Notification&lt;/code&gt; Hook&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;除了 &lt;code&gt;Stop&lt;/code&gt; 之外，還可以使用 &lt;code&gt;Notification&lt;/code&gt; 這個泛用的 Hook 來播放其它通知：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;hooks&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;Notification&quot;&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;&quot;matcher&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;&quot;hooks&quot;&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;&quot;type&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;command&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;&quot;command&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;afplay /System/Library/Sounds/Funk.aiff &amp;amp;&amp;amp; say &lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;等待許可&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;Stop&quot;&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;&quot;matcher&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;&quot;hooks&quot;&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;&quot;type&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;command&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;&quot;command&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;afplay /System/Library/Sounds/Glass.aiff &amp;amp;&amp;amp; say &lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;任務完成&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2 id=&quot;vs-code-的侷限&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#vs-code-的侷限&quot;&gt;VS Code 的侷限&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;我自己習慣使用 &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=anthropic.claude-code&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Claude Code for VS Code&lt;/a&gt; 擴充功能（extension）多於 CLI，因為它提供了更完整的 IDE 體驗，透過直觀的圖形化介面與視覺化管理，免去切換視窗的繁瑣，讓 AI 協作更流暢、專注且高效。&lt;/p&gt;
&lt;p&gt;但是，這個 extension 目前有一個問題，就是 &lt;code&gt;Notification&lt;/code&gt; Hook 無法正常運作。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Notification&lt;/code&gt; Hook 原本是為終端機模式（CLI）設計的，當遇到等待授權或閒置時，用來通知可能已切到其它視窗的你。但在 VS Code extension 裡，這些狀態會被 IDE 的原生 UI 直接接手，核心便不再廣播 &lt;code&gt;Notification&lt;/code&gt;，Hook 因此失效。&lt;code&gt;Stop&lt;/code&gt; 則屬於核心生命週期，任何介面下結束對話都會觸發，不受影響。&lt;/p&gt;
&lt;h3 id=&quot;方案-avs-code-擴充功能&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#方案-avs-code-擴充功能&quot;&gt;方案 A：VS Code 擴充功能&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;可以安裝社群專門修補此問題的 VS Code extension，例如 &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=SingularityInc.claude-notifier&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Claude Notifier&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;但是 Claude Notifier 的問題是無法自訂通知內容。&lt;/p&gt;
&lt;h3 id=&quot;方案-b改用其它-hooks推薦&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#方案-b改用其它-hooks推薦&quot;&gt;方案 B：改用其它 Hooks（推薦）&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;另一個解決方法是直接改用其它 Hooks 來取代 &lt;code&gt;Notification&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;Hooks 的種類：&lt;/p&gt;



















































































&lt;div class=&quot;table-wrap&quot;&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;事件名稱&lt;/th&gt;&lt;th&gt;觸發時機&lt;/th&gt;&lt;th&gt;是否可阻斷&lt;/th&gt;&lt;th&gt;常見應用場景&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;SessionStart&lt;/code&gt;&lt;/td&gt;&lt;td&gt;新的工作階段啟動或恢復時&lt;/td&gt;&lt;td&gt;否&lt;/td&gt;&lt;td&gt;注入專案上下文、檢查環境變數（如 Docker 是否啟動）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;UserPromptSubmit&lt;/code&gt;&lt;/td&gt;&lt;td&gt;使用者送出提示詞，Claude 開始思考前&lt;/td&gt;&lt;td&gt;是&lt;/td&gt;&lt;td&gt;敏感資訊過濾、自動補全專案規則（Context Injection）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;UserPromptExpansion&lt;/code&gt;&lt;/td&gt;&lt;td&gt;當輸入的斜線指令（Slash commands）展開為 Prompt 時&lt;/td&gt;&lt;td&gt;是&lt;/td&gt;&lt;td&gt;調整擴充後的 Prompt 內容&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;PreToolUse&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Claude 準備呼叫任何工具（如 Bash, Edit, Write）之前&lt;/td&gt;&lt;td&gt;是&lt;/td&gt;&lt;td&gt;安全防火牆（如：封鎖 &lt;code&gt;rm -rf&lt;/code&gt;、禁止修改 &lt;code&gt;.env&lt;/code&gt;）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;PermissionRequest&lt;/code&gt;&lt;/td&gt;&lt;td&gt;系統彈出工具執行授權提示（Y/N）時&lt;/td&gt;&lt;td&gt;是&lt;/td&gt;&lt;td&gt;自動授權特定安全工具，減少人工確認&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;PostToolUse&lt;/code&gt;&lt;/td&gt;&lt;td&gt;工具成功執行完畢且回傳結果之後&lt;/td&gt;&lt;td&gt;否&lt;/td&gt;&lt;td&gt;自動執行代碼格式化（如 Prettier）、自動跑 Linter&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;PostToolUseFailure&lt;/code&gt;&lt;/td&gt;&lt;td&gt;工具執行失敗時&lt;/td&gt;&lt;td&gt;否&lt;/td&gt;&lt;td&gt;錯誤日誌分析、自動化環境修復&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;SubagentStart&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Claude 呼叫 Agent 工具派生出子代理（Subagent）時&lt;/td&gt;&lt;td&gt;否&lt;/td&gt;&lt;td&gt;初始化子代理的環境&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;SubagentStop&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Subagent 完成任務並結束時&lt;/td&gt;&lt;td&gt;是&lt;/td&gt;&lt;td&gt;驗證子代理產出的程式碼品質&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;Stop&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Claude 完成該輪對話的所有回應時&lt;/td&gt;&lt;td&gt;是&lt;/td&gt;&lt;td&gt;最終程式碼品質把關（Quality Gate）、發送通知&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;StopFailure&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Claude 產生回應失敗或中斷時&lt;/td&gt;&lt;td&gt;否&lt;/td&gt;&lt;td&gt;記錄錯誤、清理暫存檔&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;SessionEnd&lt;/code&gt;&lt;/td&gt;&lt;td&gt;使用者關閉或終止 Claude Code 工作階段時&lt;/td&gt;&lt;td&gt;否&lt;/td&gt;&lt;td&gt;清理環境、匯出本次對話的 Audit Log&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;以這邊的使用情境來說，&lt;code&gt;PermissionRequest&lt;/code&gt; Hook 是最適合的選擇，因為它會在系統彈出授權提示、等待使用者許可時觸發，讓我們可以自動播放通知音效：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;hooks&quot;: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line highlight del&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;Notification&quot;: [...],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line highlight ins&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;PermissionRequest&quot;: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line highlight ins&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line highlight ins&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;matcher&quot;: &quot;&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line highlight ins&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;hooks&quot;: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line highlight ins&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line highlight ins&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;type&quot;: &quot;command&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line highlight ins&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;command&quot;: &quot;afplay /System/Library/Sounds/Funk.aiff &amp;amp;&amp;amp; say \&quot;等待許可\&quot;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line highlight ins&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line highlight ins&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line highlight ins&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line highlight ins&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;Stop&quot;: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;matcher&quot;: &quot;&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;hooks&quot;: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;type&quot;: &quot;command&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;command&quot;: &quot;afplay /System/Library/Sounds/Glass.aiff &amp;amp;&amp;amp; say \&quot;任務完成\&quot;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2 id=&quot;進階玩法voicevox&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#進階玩法voicevox&quot;&gt;進階玩法：VOICEVOX&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;如果只是使用系統內建的語音指令，雖然方便，但聲音比較單調，缺乏個性。為了讓通知更有趣，我們可以使用 VOICEVOX 來實現二次元角色語音提示。&lt;/p&gt;
&lt;h3 id=&quot;什麼是-voicevox&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#什麼是-voicevox&quot;&gt;什麼是 VOICEVOX？&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;VOICEVOX 是一款由日本開發者所開發的&lt;strong&gt;免費、開源 AI 文字轉語音（TTS）軟體&lt;/strong&gt;。&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://blog.amowu.com/_astro/image2.B_FRJyMh_1R86v.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image2.B_FRJyMh_QX5IC.webp 640w, https://blog.amowu.com/_astro/image2.B_FRJyMh_1GesBM.webp 750w, https://blog.amowu.com/_astro/image2.B_FRJyMh_ZKSk03.webp 828w, https://blog.amowu.com/_astro/image2.B_FRJyMh_F5gK4.webp 1080w, https://blog.amowu.com/_astro/image2.B_FRJyMh_1R86v.webp 1136w&quot; alt=&quot;VOICEVOX 應用程式介面，左側可選擇角色與語音風格&quot; width=&quot;1136&quot; height=&quot;742&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;過去像 VOCALOID（如初音未來）這類的軟體主要是用來「唱歌」，而語音合成軟體則多半價格高昂。VOICEVOX 的出現徹底打破了門檻，讓沒有預算請配音員的獨立影片創作者，也能輕鬆做出高品質的配音影片。&lt;/p&gt;
&lt;h3 id=&quot;安裝-voicevox&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#安裝-voicevox&quot;&gt;安裝 VOICEVOX&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;前往 &lt;a href=&quot;https://voicevox.hiroshiba.jp/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;VOICEVOX 官方網站&lt;/a&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;ダウンロード（下載）&lt;/li&gt;
&lt;li&gt;Mac → CPU（Intel or Apple） → インストーラー（Installer）&lt;/li&gt;
&lt;li&gt;安裝應用程式&lt;/li&gt;
&lt;li&gt;打開 VOICEVOX，就會看到可以選擇的角色與語音風格。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;建立腳本&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#建立腳本&quot;&gt;建立腳本&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;效仿 &lt;code&gt;say&lt;/code&gt; 指令，建立一個 &lt;code&gt;say_maid&lt;/code&gt; 腳本檔案 &lt;code&gt;~/.scripts/say_maid&lt;/code&gt;（路徑可自行決定）：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;#!/bin/zsh&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# 檢查是否有輸入文字&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; [ &lt;/span&gt;&lt;span&gt;-z&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$1&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;請輸入你想讓女僕說的話，例如：say_maid &apos;主人，歡迎回來&apos;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;exit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# 1. 產生語音 Query JSON&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;curl&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-s&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-X&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;POST&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;http://localhost:50021/audio_query&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;     &lt;/span&gt;&lt;span&gt;--url-query&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;speaker=2&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;     &lt;/span&gt;&lt;span&gt;--url-query&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;text=&lt;/span&gt;&lt;span&gt;$1&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/tmp/voicevox_query.json&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# 2. 合成語音並下載為 wav 檔&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;curl&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-s&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-H&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Content-Type: application/json&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;     &lt;/span&gt;&lt;span&gt;-X&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;POST&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-d&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;@/tmp/voicevox_query.json&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;     &lt;/span&gt;&lt;span&gt;&quot;http://localhost:50021/synthesis&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;     &lt;/span&gt;&lt;span&gt;--url-query&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;speaker=2&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/tmp/maid_voice.wav&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# 3. 使用 afplay 播放出來&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;afplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/tmp/maid_voice.wav&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;給予執行權限：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;chmod&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+x&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/.scripts/say_maid&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;在 Terminal 測試效果：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;~&lt;/span&gt;&lt;span&gt;/.scripts/say_maid &lt;/span&gt;&lt;span&gt;&quot;終わりましたよー！ご主人様？お疲れ様です！&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;~&lt;/span&gt;&lt;span&gt;/.scripts/say_maid &lt;/span&gt;&lt;span&gt;&quot;ねえ、ご主人様、早く……私を確認して？&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;沒意外的話，應該就可以聽到「四国めたん」的聲音了（VOICEVOX 只支援日文）。&lt;/p&gt;
&lt;p&gt;這兩句也是開頭 demo 的語音內容，中文的意思大致是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;「任務完成了喔！主人？辛苦了！」&lt;/li&gt;
&lt;li&gt;「喂，主人，快點……確認我吧？」&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;聽到那句軟軟的日文從終端機傳出來的瞬間，是不是連盯著進度條的等待都變得有趣了一些？&lt;/p&gt;
&lt;h3 id=&quot;speaker-id&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#speaker-id&quot;&gt;Speaker ID&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;如果想要使用其它角色或風格，可以透過 VOICEVOX 的 API 來查詢 Speaker ID，以「春日部つむぎ」為例：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;打開 &lt;a href=&quot;http://localhost:50021/speakers&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;http://localhost:50021/speakers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;在 JSON 回傳結果中找到 &lt;code&gt;name&lt;/code&gt; 為「春日部つむぎ」的物件，裡面有 &lt;code&gt;styles.id&lt;/code&gt;，可以看到它的 Speaker ID 就是 8：&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;name&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;春日部つむぎ&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;speaker_uuid&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;35b2c544-660e-401e-b503-0e14c635303a&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;styles&quot;&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;&quot;name&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;ノーマル&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;&quot;id&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;8&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;&quot;type&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;talk&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;version&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;0.16.1&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;supported_features&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;&quot;permitted_synthesis_morphing&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;ALL&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;將 &lt;code&gt;say_maid&lt;/code&gt; 腳本中的 &lt;code&gt;speaker=2&lt;/code&gt; 改成 &lt;code&gt;speaker=8&lt;/code&gt;，就可以使用「春日部つむぎ」的聲音了：&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# 1. 產生語音 Query JSON&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;curl -s -X POST &quot;http://localhost:50021/audio_query&quot; \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line highlight del&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--url-query &quot;speaker=2&quot; \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line highlight ins&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--url-query &quot;speaker=8&quot; \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--url-query &quot;text=$1&quot; &amp;gt; /tmp/voicevox_query.json&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# 2. 合成語音並下載為 wav 檔&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;curl -s -H &quot;Content-Type: application/json&quot; \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-X POST -d @/tmp/voicevox_query.json \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;http://localhost:50021/synthesis&quot; \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line highlight del&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--url-query &quot;speaker=2&quot; &amp;gt; /tmp/maid_voice.wav&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line highlight ins&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--url-query &quot;speaker=8&quot; &amp;gt; /tmp/maid_voice.wav&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3 id=&quot;背景執行&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#背景執行&quot;&gt;背景執行&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;到這裡，&lt;code&gt;say_maid&lt;/code&gt; 腳本已經可以正常運作了，但每次執行前都需要先手動打開 VOICEVOX 應用，這樣就有點麻煩。&lt;/p&gt;
&lt;p&gt;我們可以在腳本中加入自動啟動 VOICEVOX 的功能：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;#!/bin/zsh&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# 檢查是否有輸入文字&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;if [ -z &quot;$1&quot; ]; then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;echo &quot;請輸入你想讓女僕說的話，例如：say_maid &apos;主人，歡迎回來&apos;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;exit 1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line highlight ins&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;# 偵測 Port 50021 是否有在運作&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line highlight ins&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;if ! nc -z localhost 50021 2&amp;gt;/dev/null; then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line highlight ins&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;echo &quot;正在為您默默喚醒女僕...&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line highlight ins&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;open -g -a VOICEVOX&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line highlight ins&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line highlight ins&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;# 循環等待，直到伺服器完全啟動成功為止&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line highlight ins&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;while ! nc -z localhost 50021 2&amp;gt;/dev/null; do&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line highlight ins&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;sleep 0.5&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line highlight ins&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;done&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line highlight ins&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;echo &quot;女僕已就位！&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line highlight ins&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# 1. 產生語音 Query JSON&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;但是，這樣的做法會啟動整個 VOICEVOX GUI 應用程式，對於只想要使用 TTS 功能的使用者來說，可能還是會覺得有點彆扭。&lt;/p&gt;
&lt;p&gt;好消息是，VOICEVOX 內建了純後台的引擎程式，直接啟動它就可以使用 TTS 功能，而不需要打開整個 GUI。&lt;/p&gt;
&lt;p&gt;首先，我們需要找到 VOICEVOX 的引擎程式路徑：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;find&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/Applications/VOICEVOX.app&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\(&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-name&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;voicevox_engine&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-o&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-name&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;run&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;以我的環境為例，找到的路徑會是：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;/Applications/VOICEVOX.app/Contents/Resources/vv-engine/run&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;修改 &lt;code&gt;say_maid&lt;/code&gt; 腳本，將啟動 VOICEVOX 的方式改成：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;#!/bin/zsh&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# 偵測 Port 50021 是否有在運作&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;if ! nc -z localhost 50021 2&amp;gt;/dev/null; then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;echo &quot;正在為您默默喚醒女僕...&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line highlight del&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;open -g -a VOICEVOX&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line highlight ins&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;# 直接啟動 VOICEVOX 內建的純後台引擎，並將輸出丟棄 (&amp;gt; /dev/null 2&amp;gt;&amp;amp;1 &amp;amp;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line highlight ins&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/Applications/VOICEVOX.app/Contents/Resources/vv-engine/run &amp;gt; /dev/null 2&amp;gt;&amp;amp;1 &amp;amp;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;# 循環等待，直到伺服器完全啟動成功為止&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;while ! nc -z localhost 50021 2&amp;gt;/dev/null; do&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;sleep 0.5&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;done&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;echo &quot;女僕已就位！&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;搞定！現在，當你執行 &lt;code&gt;say_maid&lt;/code&gt; 時，如果 VOICEVOX 還沒啟動，它會自動在背景啟動，並且不會打開 GUI。&lt;/p&gt;
&lt;p&gt;最後，我們只要把 &lt;code&gt;~/.claude/settings.json&lt;/code&gt; 中的 &lt;code&gt;say&lt;/code&gt; 指令改成 &lt;code&gt;say_maid&lt;/code&gt;，就可以在 Claude Code 的 Hooks 中使用二次元女僕的語音提示了：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;hooks&quot;: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;PermissionRequest&quot;: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;matcher&quot;: &quot;&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;hooks&quot;: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;type&quot;: &quot;command&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line highlight del&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;command&quot;: &quot;afplay /System/Library/Sounds/Funk.aiff &amp;amp;&amp;amp; say \&quot;等待許可\&quot;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line highlight ins&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;command&quot;: &quot;afplay /System/Library/Sounds/Funk.aiff &amp;amp;&amp;amp; ~/.scripts/say_maid \&quot;ねえ、ご主人様、早く……私を確認して？\&quot;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;Stop&quot;: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;matcher&quot;: &quot;&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;hooks&quot;: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;type&quot;: &quot;command&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line highlight del&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;command&quot;: &quot;afplay /System/Library/Sounds/Glass.aiff &amp;amp;&amp;amp; say \&quot;任務完成\&quot;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line highlight ins&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;command&quot;: &quot;afplay /System/Library/Sounds/Glass.aiff &amp;amp;&amp;amp; ~/.scripts/say_maid \&quot;終わりましたよー！ご主人様？お疲れ様です！\&quot;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2 id=&quot;總結&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#總結&quot;&gt;總結&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;從一開始的 &lt;code&gt;say&lt;/code&gt;「任務完成」，到最後讓「四国めたん」在背景默默待命、任務結束時對你說一聲「お疲れ様です」，我們其實只做了一件事：把 Claude Code 的等待與完成，變成能被聽見的訊號。&lt;/p&gt;
&lt;p&gt;回顧整個過程：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Hooks 是核心&lt;/strong&gt;：&lt;code&gt;Stop&lt;/code&gt; 與 &lt;code&gt;PermissionRequest&lt;/code&gt; 讓「任務完成」和「等待許可」這兩個關鍵時刻，能夠確定性地觸發通知，不必盯著螢幕。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;系統內建就很夠用&lt;/strong&gt;：&lt;code&gt;say&lt;/code&gt; 與 &lt;code&gt;afplay&lt;/code&gt; 兩個 macOS 原生指令，零安裝就能做出堪用的語音提示。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;VS Code 的坑&lt;/strong&gt;：Claude Code extension 下 &lt;code&gt;Notification&lt;/code&gt; Hook 會失效，改用 &lt;code&gt;PermissionRequest&lt;/code&gt; 是更乾淨的解法。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;VOICEVOX 進階玩法&lt;/strong&gt;：包成 &lt;code&gt;say_maid&lt;/code&gt; 腳本、查詢 Speaker ID 切換角色，再用純後台引擎自動喚醒，就能在不打開 GUI 的情況下獲得高品質的角色語音。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;比起效率工具，這更像是一點為日常寫程式加溫的小巧思。當冰冷的終端機開始有了聲音與個性，等待授權的空檔、任務完成的瞬間，也就多了一絲陪伴的溫度。希望這篇文章能為忙於工作的你，帶來一點會心一笑的樂趣。&lt;/p&gt;</content:encoded><category>Tech</category></item><item><title>為什麼 Netflix 截圖會變黑？聊聊 DRM 技術與未來趨勢</title><link>https://blog.amowu.com/posts/2026-05-04-drm/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2026-05-04-drm/</guid><description>為什麼 Netflix 截圖會變黑？聊聊 DRM 技術與未來趨勢 拆解影音防盜的技術拉鋸戰：一位前端工程師在建置 VOD 系統時的實務心得</description><pubDate>Mon, 04 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;從一張黑屏截圖說起&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#從一張黑屏截圖說起&quot;&gt;從一張黑屏截圖說起&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;你是否曾有過這樣的經驗：在追 Netflix 的熱門影集或觀看 Disney+ 的最新大片時，看到一個絕美的畫面想截圖當桌布，或是想錄下一段精彩對白分享給朋友，結果截出來的圖卻是一片漆黑？&lt;/p&gt;
&lt;p&gt;這並不是你的手機壞了，也不是 App 出了 Bug，而是串流平台背後那套嚴密的「數位版權管理」（Digital Rights Management，簡稱 DRM）機制在發揮作用。&lt;/p&gt;
&lt;p&gt;身為負責公司學習平台 VOD 建置的工程師，我常被問到：在技術如此發達的今天，為什麼我們還能看到盜版資源？為什麼有些設備就是跑不出 4K？這背後其實是持續多年的技術拉鋸戰。本文將帶你拆解 DRM 的運作核心，並揭開 2026 年即將普及的 AI 隱形浮水印技術，看看這場防盜技術的攻防戰如何演進。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.amowu.com/posts/2020-09-04-hls-signed-cookies/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://blog.amowu.com/posts/2020-09-04-hls-signed-cookies/&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;別誤會drm-不只是加密那麼簡單&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#別誤會drm-不只是加密那麼簡單&quot;&gt;別誤會，DRM 不只是「加密」那麼簡單&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;很多人以為 DRM 就是把影片加密（Video Encryption），但本質上，它是一整套複雜的「密鑰管理系統」。如果只有影片加密，一旦使用者拿到了鑰匙並解密，他就擁有了檔案，可以無限次播放或拷貝給別人。而 DRM 比較像是一位「保全」，它不僅管你有沒有鑰匙，還管你進屋後能待多久、能不能拍照、能不能把東西帶走。確保只有獲得授權的使用者，在受信任的環境下，才能取得那把解開影片的鑰匙。&lt;/p&gt;
&lt;p&gt;DRM 的運作包含三個核心機制：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CDM（Content Decryption Module）：這是瀏覽器內置的一個硬體或軟體沙盒，負責真正的解密與渲染。由於它是個「黑盒子」，JavaScript 無法觸碰其內容，這就是為什麼截圖會變黑 —— 影像是在 CDM 內部渲染的，完全繞過了瀏覽器的渲染層。&lt;/li&gt;
&lt;li&gt;License：加密檔案本身是死的，但「許可證」是活的。當你點擊播放時，CDM 會向伺服器請求一張許可證，上面寫滿了限制規則：這把鑰匙只能用 24 小時、你只能看 1080p 的畫質、或者你現在的 IP 位置不符合版權範圍。只有當你符合清單上所有規則，CDM 才會為你點頭開鎖。&lt;/li&gt;
&lt;li&gt;Hardware Enforcement：現代 DRM 不只靠軟體，還直接與你的硬體（CPU 和顯示晶片）合作。例如透過 HDCP 協議，如果 DRM 發現你接了一個不合格的投影機，硬體層級會直接切斷訊號。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;這種從瀏覽器到伺服器，再到硬體內部的「封閉信任鏈」，就是為什麼你即便設法拿到了鑰匙，也無法反抗這些限制的原因。&lt;/p&gt;
&lt;p&gt;了解了這三道安全防線後，身為前端工程師的我們還必須認識三個組件：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;EME（Encrypted Media Extensions）：它是播放器與 CDM 之間的「橋樑」。作為 W3C 標準的 API，它負責將密鑰請求傳給伺服器，再把拿到的許可證傳回給 CDM。&lt;/li&gt;
&lt;li&gt;MSE（Media Source Extensions）：負責將下載下來的加密影片片段（Segments）源源不絕地餵入播放器中。&lt;/li&gt;
&lt;li&gt;PSSH（Protection System Specific Header）：這是埋在影片流中的資訊，包含了重要的「金鑰 ID（Key ID）」。沒有 PSSH，CDM 不知道需要哪把金鑰，也無法生成正確的 Challenge。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;3008&quot; height=&quot;1408&quot; src=&quot;https://blog.amowu.com/_astro/image1.Dt-t9yhr_pr7LQ.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image1.Dt-t9yhr_Z2opzvY.webp 640w, https://blog.amowu.com/_astro/image1.Dt-t9yhr_Z1nG9EK.webp 750w, https://blog.amowu.com/_astro/image1.Dt-t9yhr_200WOl.webp 828w, https://blog.amowu.com/_astro/image1.Dt-t9yhr_Z5srqO.webp 1080w, https://blog.amowu.com/_astro/image1.Dt-t9yhr_1PCbe9.webp 1280w, https://blog.amowu.com/_astro/image1.Dt-t9yhr_1Tr0NX.webp 1668w, https://blog.amowu.com/_astro/image1.Dt-t9yhr_1RrOmo.webp 2048w, https://blog.amowu.com/_astro/image1.Dt-t9yhr_ZoxdJX.webp 2560w, https://blog.amowu.com/_astro/image1.Dt-t9yhr_pr7LQ.webp 3008w&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;三大主流陣營googleapplemicrosoft-的角力&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#三大主流陣營googleapplemicrosoft-的角力&quot;&gt;三大主流陣營：Google、Apple、Microsoft 的角力&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;在實作 DRM 時，開發者最頭痛的就是跨平台兼容性，因為不同的瀏覽器與設備背後分別由三大巨頭把持：&lt;/p&gt;
&lt;div class=&quot;table-wrap&quot;&gt;




























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;DRM 方案&lt;/th&gt;&lt;th&gt;幕後大佬&lt;/th&gt;&lt;th&gt;主要支持環境&lt;/th&gt;&lt;th&gt;技術特點&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Widevine&lt;/td&gt;&lt;td&gt;Google&lt;/td&gt;&lt;td&gt;Chrome, Android, ChromeOS&lt;/td&gt;&lt;td&gt;應用最廣，分為 L1 (硬體級) 到 L3 (軟體級)。&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;FairPlay&lt;/td&gt;&lt;td&gt;Apple&lt;/td&gt;&lt;td&gt;Safari, iOS, macOS&lt;/td&gt;&lt;td&gt;高度垂直整合，幾乎純硬體級別，安全性極高。&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;PlayReady&lt;/td&gt;&lt;td&gt;Microsoft&lt;/td&gt;&lt;td&gt;Edge, Xbox, Windows&lt;/td&gt;&lt;td&gt;歷史悠久，在智慧電視（Smart TV）滲透率極高。&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;特別值得一提的是 Apple 的 FairPlay。由於 Apple 掌控了從晶片（M/A 系列）、作業系統到瀏覽器的完整生態，FairPlay 將解密工作徹底交給硬體層的「Secure Enclave（安全隔離區）」。這種封閉哲學使其安全性極高，但也讓它顯得相當「不合群」，不支援任何非 Apple 設備，甚至在開發調試上也是出了名的麻煩。&lt;/p&gt;
&lt;h2 id=&quot;解析畫質之謎為什麼我的裝置只能看-1080p&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#解析畫質之謎為什麼我的裝置只能看-1080p&quot;&gt;解析「畫質之謎」：為什麼我的裝置只能看 1080p？&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;你可能發現過，明明買了 Netflix 最高級的會員，但在一些手機或某些電腦瀏覽器上，畫質卻被限制在低畫質。這與 Widevine 的「強健度（Robustness）」等級有關。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Widevine L1：解密與解碼完全發生在硬體受信任執行環境（TEE）中。片商對此高度信任，因此願意開放 4K 與 HDR。&lt;/li&gt;
&lt;li&gt;Widevine L3：解密是在軟體層級完成的。對於駭客來說，軟體層較容易透過逆向工程破解，因此片商通常只給予低畫質以降低風險。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;// 1. DRM 基本配置&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;drmConfig&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; [{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;initDataTypes: [&lt;/span&gt;&lt;span&gt;&apos;cenc&apos;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;videoCapabilities: [{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;contentType: &lt;/span&gt;&lt;span&gt;&apos;video/mp4; codecs=&quot;avc1.42E01E&quot;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;// 如果你強行要求 HW_SECURE_ALL（L1）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;// 但使用者設備僅支援 L3，播放器會直接報錯黑屏。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;// 專業的做法是偵測到失敗後，&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;// 自動 Fallback 要求 SW_SECURE_DECODE（L3），&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;// 至少讓用戶能看到低畫質的畫面。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;robustness: &lt;/span&gt;&lt;span&gt;&apos;SW_SECURE_DECODE&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}];&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;video&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; document.&lt;/span&gt;&lt;span&gt;querySelector&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;video&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;// 2. 監聽 &apos;encrypted&apos; 事件：這是獲取 PSSH 的標準入口&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;video.&lt;/span&gt;&lt;span&gt;addEventListener&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;encrypted&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;event&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;--- 偵測到加密內容 ---&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;初始化數據類型:&apos;&lt;/span&gt;&lt;span&gt;, event.initDataType); &lt;/span&gt;&lt;span&gt;// 通常是 &apos;cenc&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;// 這就是你需要的 PSSH Data！&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pssh&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; event.initData;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;try&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;// 3. 請求 CDM 存取權限&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;access&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; navigator.&lt;/span&gt;&lt;span&gt;requestMediaKeySystemAccess&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;com.widevine.alpha&apos;&lt;/span&gt;&lt;span&gt;, drmConfig);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;keys&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; access.&lt;/span&gt;&lt;span&gt;createMediaKeys&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; video.&lt;/span&gt;&lt;span&gt;setMediaKeys&lt;/span&gt;&lt;span&gt;(keys);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;// 4. 建立解密會話&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;session&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; keys.&lt;/span&gt;&lt;span&gt;createMediaKeySession&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;// 5. 處理 License 請求&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;session.&lt;/span&gt;&lt;span&gt;addEventListener&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;message&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;msgEvent&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;收到 Challenge，正在向 License Server 請求金鑰...&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;response&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fetch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;https://your-license-server.com/get-key&apos;&lt;/span&gt;&lt;span&gt;, {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;method: &lt;/span&gt;&lt;span&gt;&apos;POST&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;body: msgEvent.message&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;license&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; response.&lt;/span&gt;&lt;span&gt;arrayBuffer&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;// 6. 將拿到的鑰匙塞入 CDM&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; session.&lt;/span&gt;&lt;span&gt;update&lt;/span&gt;&lt;span&gt;(license);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;DRM 解密成功，影片開始播放！&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;// 7. 告知 CDM 根據這段 PSSH 生成 Challenge&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; session.&lt;/span&gt;&lt;span&gt;generateRequest&lt;/span&gt;&lt;span&gt;(event.initDataType, pssh);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;} &lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt; (error) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;console.&lt;/span&gt;&lt;span&gt;error&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;DRM 流程錯誤:&apos;&lt;/span&gt;&lt;span&gt;, error);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;這裡有一個「Chrome 悖論」：即使你有一台價值十萬的頂規 PC，在桌面版 Chrome 上通常也只能看到 1080p 甚至更低。這是因為 Windows/macOS 的硬體解碼環境太雜，Chrome 往往只能運行 L3 軟體保護。這也是為什麼在 Mac 上想看 4K Netflix，你必須使用 Safari （Google 近年已在 Windows 平台上導入 Microsoft PlayReady 支援，試圖打破 4K 瓶頸）。&lt;/p&gt;
&lt;h2 id=&quot;開發者的噩夢fairplay-那極其人工的申請流程&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#開發者的噩夢fairplay-那極其人工的申請流程&quot;&gt;開發者的噩夢：FairPlay 那極其「人工」的申請流程&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;實作 FairPlay 的過程通常比 Widevine 痛苦得多，因為它的流程充滿了「Apple 式」的嚴謹與人工審核：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;申請與憑證：必須具備 Apple 開發者組織帳號，並申請 FPS Deployment Package。&lt;/li&gt;
&lt;li&gt;Application Certificate：這是 FairPlay 的獨門要求。在發送 License 請求前，播放器必須先下載這個二進制憑證，否則 CDM 連密鑰請求都生不出來。&lt;/li&gt;
&lt;li&gt;換取 ASK：在 Apple 後台生成證書時，會產生一串 ASK（Application Secret Key）。&lt;/li&gt;
&lt;li&gt;警告：ASK 在網頁生成時「只會出現一次」，沒存下來就得全部重來。&lt;/li&gt;
&lt;li&gt;整合：將證書、私鑰與 ASK 交付給播放器或後端。&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;// FairPlay 專屬：獲取 Application Certificate&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fetchAppCert&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;certUrl&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;response&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fetch&lt;/span&gt;&lt;span&gt;(certUrl);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;arrayBuffer&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; response.&lt;/span&gt;&lt;span&gt;arrayBuffer&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Uint8Array&lt;/span&gt;&lt;span&gt;(arrayBuffer);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;setupFairPlay&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;video&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;certUrl&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;appCert&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fetchAppCert&lt;/span&gt;&lt;span&gt;(certUrl);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;video.&lt;/span&gt;&lt;span&gt;addEventListener&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;encrypted&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;event&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;// 1. 取得 FairPlay 的 Key System&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;access&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; navigator.&lt;/span&gt;&lt;span&gt;requestMediaKeySystemAccess&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;com.apple.fps&apos;&lt;/span&gt;&lt;span&gt;, [{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;initDataTypes: [&lt;/span&gt;&lt;span&gt;&apos;skd&apos;&lt;/span&gt;&lt;span&gt;], &lt;/span&gt;&lt;span&gt;// FairPlay 常用 skd 或 sinf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;videoCapabilities: [{ contentType: &lt;/span&gt;&lt;span&gt;&apos;video/mp4; codecs=&quot;avc1.42E01E&quot;&apos;&lt;/span&gt;&lt;span&gt; }]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}]);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;keys&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; access.&lt;/span&gt;&lt;span&gt;createMediaKeys&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;// 2. 【關鍵點】必須先設定伺服器憑證&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; keys.&lt;/span&gt;&lt;span&gt;setServerCertificate&lt;/span&gt;&lt;span&gt;(appCert);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; video.&lt;/span&gt;&lt;span&gt;setMediaKeys&lt;/span&gt;&lt;span&gt;(keys);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;session&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; keys.&lt;/span&gt;&lt;span&gt;createMediaKeySession&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;session.&lt;/span&gt;&lt;span&gt;addEventListener&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;message&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;msgEvent&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;// 3. 發送 SPC (Server Playback Context) 給 Apple License Server&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;response&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fetch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;your-fairplay-license-url&apos;&lt;/span&gt;&lt;span&gt;, {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;method: &lt;/span&gt;&lt;span&gt;&apos;POST&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;body: msgEvent.message&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ckc&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; response.&lt;/span&gt;&lt;span&gt;arrayBuffer&lt;/span&gt;&lt;span&gt;(); &lt;/span&gt;&lt;span&gt;// 回傳的是 CKC&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; session.&lt;/span&gt;&lt;span&gt;update&lt;/span&gt;&lt;span&gt;(ckc);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;// 4. FairPlay 的 initData 通常需要從 skd:// 轉換&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;initData&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; event.initData;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; session.&lt;/span&gt;&lt;span&gt;generateRequest&lt;/span&gt;&lt;span&gt;(event.initDataType, initData);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;如果在專案上線前兩天你才發現沒申請 FairPlay，那是絕對來不及的。因為 Apple 的審核人員可能正在放假，這不是寫程式能解決的問題。此外，如果你用了一條不合格的 HDMI 線，FairPlay 會直接丟給你一個模糊的 &lt;code&gt;MediaError&lt;/code&gt;，讓你調試到懷疑人生。&lt;/p&gt;
&lt;h2 id=&quot;既然有-drm為什麼盜版平台還是有-4k-資源&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#既然有-drm為什麼盜版平台還是有-4k-資源&quot;&gt;既然有 DRM，為什麼盜版平台還是有 4K 資源？&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;儘管 DRM 很強，但它防的是「普通用戶」，而非「專業組織」。盜版資源主要透過三大漏洞流出：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Widevine L3 漏洞：駭客透過逆向工程提取軟體層密鑰，直接從伺服器下載原始流並解密。這稱為 WEB-DL（最高境界，畫質無損且與原始檔案一致）。&lt;/li&gt;
&lt;li&gt;HDCP 剝離（Analog Hole）：利用特殊擷取卡偽裝成合法顯示器，在 HDMI 傳輸中剝離 HDCP 保護。這稱為 WEB-Rip（經過二次壓縮，細節略損）。&lt;/li&gt;
&lt;li&gt;CDM 零日漏洞：專業組織發現瀏覽器解密模組的未公開漏洞，模擬合法 L1 設備索取 4K 密鑰。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;為了提高安全性，Widevine 現在也引入了 Service Certificate（類似 FairPlay 的預先認證），讓 License 請求從第一步開始就是加密的，確保隱私不被截獲。&lt;/p&gt;
&lt;p&gt;DRM 就像家裡的門鎖，它防不了帶專業工具的盜賊，但它能確保路過的行人無法隨意進來搬東西。&lt;/p&gt;
&lt;h2 id=&quot;省時省力的秘密jw-player-studio-drm-託管方案&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#省時省力的秘密jw-player-studio-drm-託管方案&quot;&gt;省時省力的秘密：JW Player Studio DRM 託管方案&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;為了不讓開發者深陷在 EME 實作的泥沼中，DRM as a Service 應運而生。以 JW Player Studio DRM 為例，它實現了高度封裝：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CMAF 統一打包：透過 CMAF（Common Media Application Format）技術，同一份影片檔案可以同時支援 Widevine、PlayReady 和 FairPlay，這為平台節省了約 66% 的存儲成本。&lt;/li&gt;
&lt;li&gt;前端極簡化：播放器 SDK 會自動偵測環境。在 Safari 啟動 FairPlay，在 Chrome 啟動 Widevine，在 Windows 則啟用 PlayReady。&lt;/li&gt;
&lt;li&gt;擴充性：若需要自備 License Server，開發者仍可透過 &lt;code&gt;licenseRequestFilter&lt;/code&gt; 攔截請求，加入自定義的 Token 或 Header。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;jwplayer&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;video&quot;&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;setup&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;playlist: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;file: &lt;/span&gt;&lt;span&gt;&quot;video.mpd&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;drm: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;widevine: { url: &lt;/span&gt;&lt;span&gt;&quot;...&quot;&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;playready: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;url: &lt;/span&gt;&lt;span&gt;&quot;...&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;          &lt;/span&gt;&lt;span&gt;licenseRequestFilter&lt;/span&gt;&lt;span&gt;: (&lt;/span&gt;&lt;span&gt;req&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt; } &lt;/span&gt;&lt;span&gt;// 自定義請求&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;file: &lt;/span&gt;&lt;span&gt;&quot;video.m3u8&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;drm: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;fairplay: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;url: &lt;/span&gt;&lt;span&gt;&quot;...&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;certificateUrl: &lt;/span&gt;&lt;span&gt;&quot;...&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;// App 憑證&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;          &lt;/span&gt;&lt;span&gt;processSpcurl&lt;/span&gt;&lt;span&gt;: (&lt;/span&gt;&lt;span&gt;spc&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt; }, &lt;/span&gt;&lt;span&gt;// 處理 skd 轉換&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;          &lt;/span&gt;&lt;span&gt;licenseRequestFilter&lt;/span&gt;&lt;span&gt;: (&lt;/span&gt;&lt;span&gt;req&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;如果你跟我所在的團隊一樣，原本就已經採用 JW Player 作為整套影片託管的解決方案，那麼直接使用它們的 Studio DRM 會是最順水推舟的選擇（JW Player 的 Delivery API 會直接給你完整的 playlist，連上面的程式碼都不用寫）。&lt;/p&gt;
&lt;h2 id=&quot;並非全站皆兵什麼情境才適合用-drm&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#並非全站皆兵什麼情境才適合用-drm&quot;&gt;並非全站皆兵：什麼情境才適合用 DRM？&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;DRM 的握手（Handshake）會消耗大量的伺服器運算成本，因此並非全站適用。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;適合 DRM：好萊塢電影、高價線上課程、企業機密培訓。這些內容通常有合約強制要求。&lt;/li&gt;
&lt;li&gt;不適合 DRM：行銷廣告、新聞、UGC（使用者上傳內容）。這些內容追求傳播量，DRM 反而會增加播放延遲並導致舊設備無法開啟。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;YouTube 就是典型案例：一般影片不加密以利傳播，但電影租借則會強制啟動 Widevine 保護。&lt;/p&gt;
&lt;h2 id=&quot;正在主流化ai-隱形浮水印與-ab-變體技術&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#正在主流化ai-隱形浮水印與-ab-變體技術&quot;&gt;正在主流化：AI 隱形浮水印與 A/B 變體技術&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;無論是 WEB-DL 還是 WEB-Rip 檔案流出時，AI 隱形浮水印（Forensic Watermarking）是最後的防線。它甚至能防禦 DRM 無法解決的「類比漏洞（如手機翻拍）」。&lt;/p&gt;
&lt;p&gt;其核心是 「雲端 A/B 切換模式」：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;原理：轉碼時針對同一段 2–6 秒的片段生成 A、B 兩個微小差異版本。&lt;/li&gt;
&lt;li&gt;組合：CDN 根據使用者 ID 給予專屬序列（如 A-B-A-A-B…）。&lt;/li&gt;
&lt;li&gt;追蹤：AI 具備強大的 「抗畸變（Anti-distortion）」 能力。即使盜版影片被手機翻拍、嚴重壓縮或裁切，AI 依然能從像素中的微小擾動回推這段 A/B 序列，精準鎖定洩漏者的 User ID。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;2606&quot; height=&quot;1600&quot; src=&quot;https://blog.amowu.com/_astro/image2.C14i4pPT_Z1FNowo.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image2.C14i4pPT_Z3V0OF.webp 640w, https://blog.amowu.com/_astro/image2.C14i4pPT_VuRRx.webp 750w, https://blog.amowu.com/_astro/image2.C14i4pPT_VQPd8.webp 828w, https://blog.amowu.com/_astro/image2.C14i4pPT_1GRQym.webp 1080w, https://blog.amowu.com/_astro/image2.C14i4pPT_Z2pRY79.webp 1280w, https://blog.amowu.com/_astro/image2.C14i4pPT_Z1HhczG.webp 1668w, https://blog.amowu.com/_astro/image2.C14i4pPT_Z1FQ9fz.webp 2048w, https://blog.amowu.com/_astro/image2.C14i4pPT_Z9vjsD.webp 2560w, https://blog.amowu.com/_astro/image2.C14i4pPT_Z1FNowo.webp 2606w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;這套技術就像是給影片植入了「數位 DNA」。以前盜版者可以說：「這是別人在網路上傳的，不關我的事。」現在透過這套序列，平台可以非常有底氣地說：「這就是從你的帳號、在 14:05 分、用某型號電視看的時候流出的。」&lt;/p&gt;
&lt;p&gt;傳統若要為每位使用者嵌入唯一浮水印，必須進行「使用者數量 x 影片數量」的獨立轉檔，這成本是完全不切實際的天文數字。相較之下，A/B 變體技術無論背後的使用者規模多大，永遠只需要預先輸出 A、B 兩份版本（影片數量 x 2）。&lt;/p&gt;
&lt;p&gt;面對「合謀攻擊（Collusion Attack）」（駭客對比不同使用者影片來抹除浮水印），AI 嵌入的特徵是非線性的且分佈在不同頻域，使破解成本高到不符合經濟效益。&lt;/p&gt;
&lt;h2 id=&quot;未來趨勢webassembly-drm&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#未來趨勢webassembly-drm&quot;&gt;未來趨勢：WebAssembly DRM&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;WebAssembly DRM 正在興起。它讓服務商可以部署自定義的解密演算法，實現跨瀏覽器的一致性，不再受限於 Google、Microsoft 或 Apple 的 CDM，實現「軟體定義安全」的靈活性與自主權。&lt;/p&gt;
&lt;p&gt;展望未來，主流的安全架構也許將是混合式的：DRM（攔截非法存取）+ AI 隱形浮水印（追蹤洩漏來源）+ Wasm（統一安全邏輯）。&lt;/p&gt;
&lt;h2 id=&quot;一場無止境的經濟攻防戰&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#一場無止境的經濟攻防戰&quot;&gt;一場無止境的經濟攻防戰&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;從黑屏截圖到 AI 隱形浮水印，我們看到串流技術的演進始終圍繞著一個核心：安全性不在於「絕對不可破解」，而在於「不斷增加破解成本」（當然，前提是產品開發成本不會先讓老闆崩潰，畢竟安全性也是有邊際效益的）。&lt;/p&gt;
&lt;p&gt;在 AI 浮水印能精準追蹤到每個人的 2026 年，傳統的「盜版分享」是否終將消亡？抑或會演變成另一種更高層次的技術對抗？這場影像防盜的攻防拉鋸戰，顯然還沒有終點。&lt;/p&gt;</content:encoded><category>Tech</category></item><item><title>Pact Contract Testing</title><link>https://blog.amowu.com/posts/2024-07-14-pact-contract-testing/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2024-07-14-pact-contract-testing/</guid><description>初探 Pact &amp; PactFlow</description><pubDate>Sun, 14 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;不知道大家有沒有聽過「契約測試」這個冷門的概念，它適合兩種對象：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;一個後端（API server）＋多個 client 端（web＋mobile）&lt;/li&gt;
&lt;li&gt;多個 server 端（例如 microservices）&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;這篇文章會介紹 Pact 這個契約測試工具，以及 PactFlow 這個服務。&lt;/p&gt;
&lt;h2 id=&quot;前言&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#前言&quot;&gt;前言&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;大概是在 2017 年左右，當時我正在尋找前端 mock server 相關資料的時候，第一次聽到「契約測試」這個概念，當時並沒有深入去瞭解它。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.amowu.com/posts/2017-08-26-mock-server/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Mock Server＆契約測試&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;後來公司開始有了 iOS &amp;amp; Android 工程師，這時候就出現一些問題。因為後端的 API 要同時服務於 web 前端跟 mobile app，有時候兩邊要的東西是不一樣的，可能就很容易產生衝突。在尋找解決方案的時候，又再次聽到契約測試的名字。&lt;/p&gt;
&lt;p&gt;即便是今天，我搜尋了一下 contract testing，還是沒多少人在介紹，可見這個東西是相對冷門的。於是我就決定來研究一下它是什麼，並於 2023 年在公司的前端聚會上分享，這篇文章就是整理自當時簡報的內容。&lt;/p&gt;
&lt;p&gt;以下是大綱：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;什麼是 Contract Testing？&lt;/li&gt;
&lt;li&gt;Pact Contract Testing 如何運作&lt;/li&gt;
&lt;li&gt;5 分鐘指南&lt;/li&gt;
&lt;li&gt;總結&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;什麼是-contracttesting&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#什麼是-contracttesting&quot;&gt;什麼是 Contract Testing？&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Content testing 的中文通常翻譯成「契約測試」，也有一些地方譯作「合約測試」或「合同測試」。&lt;/p&gt;
&lt;p&gt;這是一種整合測試（Integration testing）技術，通過對每個應用（application）進行獨立檢查，確保其「發送」或「接收」的「訊息」符合記錄在「契約」中的共識。&lt;/p&gt;
&lt;p&gt;例如，對於通過 HTTP 進行通信的應用，這些訊息將是 HTTP 請求（request）和回應（response）；而對於使用隊列（queues）的應用，訊息則是放入隊列的消息（message）。&lt;/p&gt;
&lt;p&gt;在實踐中，&lt;strong&gt;實施契約測試的常見方式（也是 Pact 的方式）是檢查所有對「測試替身（test doubles）」發送和接收的結果，是否與對真實應用發送和接收的結果一致。&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;何時該使用契約測試&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#何時該使用契約測試&quot;&gt;何時該使用契約測試？&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;契約測試適用於任何需要通信的兩個以上的應用，最簡單的例子是 API server 與 web 前端之間的通信。&lt;/p&gt;
&lt;p&gt;雖然單個客戶端對單個服務端的情況較為常見，但在多個服務環境下，契約測試能發揮最大的作用。例如現今流行的微服務（Microservices）架構中，使用契約測試能幫助開發人員更好地管理版本。&lt;/p&gt;
&lt;h3 id=&quot;契約測試會用到的術語&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#契約測試會用到的術語&quot;&gt;契約測試會用到的術語&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;契約通常由**消費者端（Consumer）&lt;strong&gt;和&lt;/strong&gt;提供者端（Provider）**直接簽訂。Consumer 端可以想像成需要獲取某些資料的客戶端（client），而 Provider 端則是提供資料的伺服器（server）。&lt;/p&gt;
&lt;p&gt;那為何不直接使用 client 和 server 呢？因為在微服務架構中，通信可能是伺服器對伺服器之間的通信，這時使用 client 和 server 並不合適，因此更傾向於使用 Consumer 和 Provider 這兩個術語。&lt;/p&gt;
&lt;h3 id=&quot;contract-testingpatterns&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#contract-testingpatterns&quot;&gt;Contract Testing Patterns&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;契約測試主要分為兩種模式（pattern）：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;以消費者為驅動的契約測試（Consumer-Driven Contract Testing）&lt;/li&gt;
&lt;li&gt;以提供者為驅動的契約測試（Provider-Driven Contract Testing）&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;今天要介紹的 Pact 是一種 Code-First 的「消費者驅動契約測試」工具。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;什麼是 Code-First 呢？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;契約是在 Consumer 執行測試的階段自動產生，這就是所謂的 Code-First Consumer-Driven。&lt;/p&gt;
&lt;p&gt;這種模式的優點是只有 Consumer 實際使用的通信部分才會被測試。因此，如果 Provider 對當前 Consumer 未使用的部分進行修改，測試也不會受到影響。&lt;/p&gt;
&lt;p&gt;其他類似於契約測試的概念還包括 Schema Testing 和 OpenAPI specification（OAS），不過這些今天都不會提到，有興趣的人可以看這篇&lt;a href=&quot;https://pactflow.io/blog/contract-testing-using-json-schemas-and-open-api-part-1/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;官方文章&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id=&quot;pact-contract-testing如何運作&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#pact-contract-testing如何運作&quot;&gt;Pact Contract Testing 如何運作&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;在說明 Pact 契約測試如何運作之前，我們先來看看傳統測試會有哪些問題。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://pact.io/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Microservices and API testing framework&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;整合測試的問題&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#整合測試的問題&quot;&gt;整合測試的問題&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;整合測試（Integration testing）雖然能提升信心，但也會引入依賴關係。因為 Consumer 和 Provider 需要一起運作，這會使除錯變得緩慢。例如，使用 Cypress 進行 E2E 測試時，可能需要執行完整的回歸測試（Regression testing）才能發現問題，這會延遲修正問題的時間。&lt;/p&gt;
&lt;p&gt;此外，整合測試較容易失敗。例如，前端或後端的一些小改動可能會導致整個測試失敗，迫使我們重新編寫測試，增加了維護的負擔。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;451&quot; height=&quot;324&quot; src=&quot;https://blog.amowu.com/_astro/image1.Bw_p2EMt_1b73DO.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image1.Bw_p2EMt_1b73DO.webp 451w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.amowu.com/posts/2022-06-29-cypress-on-rails/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Hahow for Business 如何跑 E2E 測試？&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;獨立測試的問題&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#獨立測試的問題&quot;&gt;獨立測試的問題&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;那為什麼不獨立的測試呢？獨立測試將 Consumer 和 Provider 分開測試，這樣能提供獨立運作的優點，反饋速度快且測試穩定，維護也較容易。然而，獨立測試相比整合測試，對發佈（release）的信心較低，可能會在上線後才發現真實世界中的問題。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;455&quot; height=&quot;326&quot; src=&quot;https://blog.amowu.com/_astro/image2.DW8zrQpi_Z1mlN.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image2.DW8zrQpi_Z1mlN.webp 455w&quot; /&gt;&lt;/p&gt;
&lt;h3 id=&quot;pact-契約測試&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#pact-契約測試&quot;&gt;Pact 契約測試&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;那 Pact 是如何運作的呢？我們先來看 Consumer 端。在測試期間，Consumer 會對模擬 Provider 的 mock server 發送 request，並記錄預期的 response。這些互動（interactions）會自動生成一份契約（稱作 pact）。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;454&quot; height=&quot;325&quot; src=&quot;https://blog.amowu.com/_astro/image3.DIL1E1qC_Z1KiDde.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image3.DIL1E1qC_Z1KiDde.webp 454w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Provider 端則會拿到這份生成的契約，對其進行回放（replays），檢查 Provider 實際產生的 response 是否符合 Consumer 的預期。如果兩者匹配，我們就能確信它們在真實應用中的行為也是符合預期的。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;455&quot; height=&quot;326&quot; src=&quot;https://blog.amowu.com/_astro/image4.Cd9jvDDQ_Z1fOkqL.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image4.Cd9jvDDQ_Z1fOkqL.webp 455w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;這就是 Pact 契約測試的大致運作流程。這種方法同時保有獨立運作的優點，反饋快速且測試穩定，維護起來也容易。此外，Pact 契約測試也能夠提升我們的發佈信心，雖然不如整合測試那麼強，但比獨立測試更有信心。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;459&quot; height=&quot;326&quot; src=&quot;https://blog.amowu.com/_astro/image5.DKD2wQ6p_ZEPsyN.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image5.DKD2wQ6p_ZEPsyN.webp 459w&quot; /&gt;&lt;/p&gt;
&lt;h3 id=&quot;pactflow&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#pactflow&quot;&gt;PactFlow&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;使用 Pact 契約測試後，就會需要管理這些契約（pacts），否則每次都要等 Consumer 生成契約再交給 Provider 測試，這會很麻煩。因此，需要一個中介角色，Pact 將之稱作 &lt;strong&gt;Pact Broker&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;具體操作是自動化將契約存放到某個地方，Provider 再從中取回契約進行測試。這塊除了可以自行實作，也可以使用 Pact 提供的開源 Pact Broker。此外 Pact 還提供了一個完全托管的 Pact Broker 服務，叫做 PactFlow，後面會介紹。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Pact Contract Testing&quot; width=&quot;639&quot; height=&quot;473&quot; src=&quot;https://blog.amowu.com/_astro/image6.Cdjz4N-f_Z1yS0wh.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image6.Cdjz4N-f_Z1yS0wh.webp 639w&quot; /&gt;
&lt;em&gt;Pact Contract Testing&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;5-分鐘指南&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#5-分鐘指南&quot;&gt;5 分鐘指南&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;接下來，我將介紹官方提供的&lt;a href=&quot;https://docs.pact.io/5-minute-getting-started-guide&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;五分鐘指南&lt;/a&gt;。&lt;/p&gt;
&lt;h3 id=&quot;官方五分鐘指南demo&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#官方五分鐘指南demo&quot;&gt;官方五分鐘指南Demo&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;官方五分鐘指南有提供一個&lt;a href=&quot;https://github.com/pact-foundation/pact-5-minute-getting-started-guide&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;範例 repository&lt;/a&gt;，有興趣的朋友可以自行 clone 下來操作。&lt;/p&gt;
&lt;h3 id=&quot;目錄結構介紹&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#目錄結構介紹&quot;&gt;目錄結構介紹&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Clone 下來後，目錄結構大致如下：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;pact-5-minute-getting-started-guide/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;├── consumer/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;├── pacts/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;├── provider/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;├── scripts/ci/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;├── index.html&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;├── index.js&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;├── package.json&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;├── pact.js&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;├── publish.sh&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;├── runConsumerTest.js&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;├── runProviderTest.js&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;└── runTest.js&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;主要的資料夾包括：&lt;strong&gt;consumer&lt;/strong&gt;、&lt;strong&gt;provider&lt;/strong&gt; 和用來存放自動產生契約的 &lt;strong&gt;pacts&lt;/strong&gt; 資料夾。為了方便理解，官方將 Consumer 和 Provider 都放在一起，這樣演示比較方便。&lt;/p&gt;
&lt;p&gt;我們先來看一下 package.json 文件，這個範例的前端和後端都是使用 JavaScript 語言：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame has-title&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;package.json&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;dependencies&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;express&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;^4.18.2&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;測試工具的部分，前端和後端都是使用 mocha 和 chai，以及 Pact 提供的測試 library：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame has-title&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;package.json&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;devDependencies&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;@pact-foundation/pact&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;12.1.0&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;mocha&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;^10.2.0&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;chai&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;^4.3.7&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3 id=&quot;測試流程&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#測試流程&quot;&gt;測試流程&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;首先，我們會執行 Consumer 端的測試，指令是 &lt;code&gt;npm run test:consumer&lt;/code&gt;，接著執行 Provider 端的測試，指令是 &lt;code&gt;npm run test:provider&lt;/code&gt;。這兩個測試都是在本地進行的：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame has-title&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;package.json&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;scripts&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;test:consumer&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;node runConsumerTest.js&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;test:provider&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;node runProviderTest.js&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;test&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;npm run test:consumer &amp;amp;&amp;amp; npm run test:provider&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;演示完這部分之後，我還會示範如何將契約發佈到 PactFlow 執行測試，主要指令是 &lt;code&gt;npm run test:broker&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame has-title&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;package.json&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;scripts&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;pact:publish&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;./publish.sh&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;test:broker&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;npm run test:consumer &amp;amp;&amp;amp; npm run pact:publish &amp;amp;&amp;amp; npm run test:provider&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3 id=&quot;consumer-測試&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#consumer-測試&quot;&gt;Consumer 測試&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;我們先來看 Consumer 端的測試。這個範例主要是測試一個訂單 API，前端會呼叫 RESTful API 並轉換成自己的 model 結構：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame has-title&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;consumer/order.js&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Order&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;constructor&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;items&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.id &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; id;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.items &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; items;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;total&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.items.&lt;/span&gt;&lt;span&gt;reduce&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;acc&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;v&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;acc &lt;/span&gt;&lt;span&gt;+=&lt;/span&gt;&lt;span&gt; v.quantity &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; v.value;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; acc;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;toString&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;`Order ${&lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;}, Total: ${&lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;total&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;}`&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;module&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;exports&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Order,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;// consumer/orderClient.js&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;request&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;superagent&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;Order&lt;/span&gt;&lt;span&gt; } &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;./order&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;hostname&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;127.0.0.1&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fetchOrders&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; request.&lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;`http://${&lt;/span&gt;&lt;span&gt;hostname&lt;/span&gt;&lt;span&gt;}:${&lt;/span&gt;&lt;span&gt;process&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;env&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;API_PORT&lt;/span&gt;&lt;span&gt;}/orders`&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;res&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; res.body.&lt;/span&gt;&lt;span&gt;reduce&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;acc&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;o&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;acc.&lt;/span&gt;&lt;span&gt;push&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Order&lt;/span&gt;&lt;span&gt;(o.id, o.items));&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; acc;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}, []);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;err&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(err)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;throw&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Error&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;`Error from response: ${&lt;/span&gt;&lt;span&gt;err&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;}`&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;module&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;exports&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;fetchOrders,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;我們會對這個 model 進行單元測試，測試會模擬一個 HTTP GET 請求來獲取訂單，並將返回的 response 轉換為前端的訂單 model：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame has-title&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;consumer/consumer.spec.js&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;// 設定我們的測試框架&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;chai&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;chai&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;expect&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;chai&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;expect&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;chaiAsPromised&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;chai-as-promised&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;chai&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;use&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;chaiAsPromised&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;// 我們需要 Pact 才能在測試中使用它&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;provider&lt;/span&gt;&lt;span&gt; } &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;../pact&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;eachLike&lt;/span&gt;&lt;span&gt; } &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;@pact-foundation/pact&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;MatchersV3&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;// 導入我們的待測邏輯（orderClient）和訂單模型&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;Order&lt;/span&gt;&lt;span&gt; } &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;./order&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;fetchOrders&lt;/span&gt;&lt;span&gt; } &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;./orderClient&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;// 這是我們開始寫測試的地方&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;describe&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;Pact with Order API&quot;&lt;/span&gt;&lt;span&gt;, () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;describe&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;given there are orders&quot;&lt;/span&gt;&lt;span&gt;, () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;itemProperties&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;burger&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;quantity&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;orderProperties&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;items&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;eachLike&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;itemProperties&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;describe&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;when a call to the API is made&quot;&lt;/span&gt;&lt;span&gt;, () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;before&lt;/span&gt;&lt;span&gt;(() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;provider&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;          &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;given&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;there are orders&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;          &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;uponReceiving&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;a request for orders&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;          &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;withRequest&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;method&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;GET&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;path&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;/orders&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;          &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;willRespondWith&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;eachLike&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;orderProperties&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;status&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;200&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;headers&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;              &lt;/span&gt;&lt;span&gt;&quot;Content-Type&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;application/json; charset=utf-8&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;it&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;will receive the list of current orders&quot;&lt;/span&gt;&lt;span&gt;, () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;provider&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;executeTest&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;mockserver&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;          &lt;/span&gt;&lt;span&gt;// Mock server 是在一個隨機可用的 port 上啓動的，&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;          &lt;/span&gt;&lt;span&gt;// 因此我們設置了 API_PORT，這樣 HTTP 客戶端就能動態找到 endpoint 了。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;          &lt;/span&gt;&lt;span&gt;process&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;env&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;API_PORT&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mockserver&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;port&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;          &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;expect&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;fetchOrders&lt;/span&gt;&lt;span&gt;())&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;to&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;eventually&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;have&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;deep&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;members&lt;/span&gt;&lt;span&gt;([&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Order&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;orderProperties&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;, [&lt;/span&gt;&lt;span&gt;itemProperties&lt;/span&gt;&lt;span&gt;]),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;其中 &lt;code&gt;require(&quot;../pact&quot;)&lt;/code&gt; 會先 setup 測試環境，這些設置包括 Consumer 和 Provider 的名稱以及契約的存放位置（本地的 pacts 資料夾）：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame has-title&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;pact.js&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;path&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;path&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;consumerName&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;GettingStartedOrderWeb&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;providerName&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;GettingStartedOrderApi&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pactFile&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; path.&lt;/span&gt;&lt;span&gt;resolve&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;`./pacts/${&lt;/span&gt;&lt;span&gt;consumerName&lt;/span&gt;&lt;span&gt;}-${&lt;/span&gt;&lt;span&gt;providerName&lt;/span&gt;&lt;span&gt;}.json`&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;module&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;exports&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;pactFile,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;consumerName,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;providerName,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;然後是 Provider mock server 的設置：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame has-title&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;pact.js&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pact&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;@pact-foundation/pact&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Pact&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; pact.PactV3;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;path&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;path&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;process&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;process&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;provider&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Pact&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;log: path.&lt;/span&gt;&lt;span&gt;resolve&lt;/span&gt;&lt;span&gt;(process.&lt;/span&gt;&lt;span&gt;cwd&lt;/span&gt;&lt;span&gt;(), &lt;/span&gt;&lt;span&gt;&quot;logs&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;pact.log&quot;&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;dir: path.&lt;/span&gt;&lt;span&gt;resolve&lt;/span&gt;&lt;span&gt;(process.&lt;/span&gt;&lt;span&gt;cwd&lt;/span&gt;&lt;span&gt;(), &lt;/span&gt;&lt;span&gt;&quot;pacts&quot;&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;logLevel: &lt;/span&gt;&lt;span&gt;&quot;info&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;host: &lt;/span&gt;&lt;span&gt;&quot;127.0.0.1&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;consumer: consumerName,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;provider: providerName,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;host: &lt;/span&gt;&lt;span&gt;&quot;127.0.0.1&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;// 用於在出現錯誤時 kill 剩餘的 mock server instances&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;process.&lt;/span&gt;&lt;span&gt;on&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;SIGINT&quot;&lt;/span&gt;&lt;span&gt;, () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;pact.&lt;/span&gt;&lt;span&gt;removeAllServers&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;module&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;exports&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;provider,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;接著，我們運行 Consumer 測試指令 &lt;code&gt;npm run test:consumer&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;➜&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;npm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;test:consumer&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; test:consumer&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; node runConsumerTest.js&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;Pact&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;with&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Order&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;API&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;given&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;there&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;are&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;orders&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;when&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;call&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;to&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;the&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;API&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;is&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;made&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;✔&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;will&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;receive&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;the&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;list&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;of&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;current&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;orders&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;passing&lt;/span&gt;&lt;span&gt; (14ms)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;consumer&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;test&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;passed!&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Open&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;the&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pact&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;file&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;.../pact-5-minute-getting-started-guide/pacts/GettingStartedOrderWeb-GettingStartedOrderApi.json&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;測試通過後，pacts 資料夾會生成一個契約文件，記錄 Consumer 預期的 request 和 response：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame has-title&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;pacts/GettingStartedOrderWeb-GettingStartedOrderApi.json&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;consumer&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;name&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;GettingStartedOrderWeb&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;interactions&quot;&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;&quot;request&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;&quot;method&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;GET&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;&quot;path&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;/orders&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;&quot;response&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;&quot;body&quot;&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;&quot;id&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;&quot;items&quot;&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;                &lt;/span&gt;&lt;span&gt;&quot;name&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;burger&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;                &lt;/span&gt;&lt;span&gt;&quot;quantity&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;                &lt;/span&gt;&lt;span&gt;&quot;value&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;100&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;provider&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;name&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;GettingStartedOrderApi&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3 id=&quot;provider-測試&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#provider-測試&quot;&gt;Provider 測試&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Provider 端的範例使用 Express.js 搭建一個簡單的 API server，提供 &lt;code&gt;GET /orders&lt;/code&gt; 的端點，返回訂單數據：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame has-title&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;provider/provider.js&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;express&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;express&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;server&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;express&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;// &quot;寫死&quot; 的示範資料&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; dataStore &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;./data/orders.js&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;server.&lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;/orders&quot;&lt;/span&gt;&lt;span&gt;, (&lt;/span&gt;&lt;span&gt;_&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;res&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;res.&lt;/span&gt;&lt;span&gt;json&lt;/span&gt;&lt;span&gt;(dataStore);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;module&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;exports&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;server,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;dataStore,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;// provider/data/orders.js&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;module&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;exports&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;id: &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;items: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;name: &lt;/span&gt;&lt;span&gt;&quot;burger&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;quantity: &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;value: &lt;/span&gt;&lt;span&gt;20&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;name: &lt;/span&gt;&lt;span&gt;&quot;coke&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;quantity: &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;value: &lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;];&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;在 Provider 測試中，我們同樣會引入 Pact 的設置文件。這些設置包括契約文件的位置，以及一些測試環境的變數。&lt;/p&gt;
&lt;p&gt;Provider 測試會模擬 Consumer 的請求，檢查實際的 response 是否符合契約的預期：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame has-title&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;provider/provider.spec.js&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Verifier&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;@pact-foundation/pact&quot;&lt;/span&gt;&lt;span&gt;).Verifier;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;chai&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;chai&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;chaiAsPromised&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;chai-as-promised&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;server&lt;/span&gt;&lt;span&gt; } &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;./provider.js&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;providerName&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;pactFile&lt;/span&gt;&lt;span&gt; } &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;../pact.js&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;chai.&lt;/span&gt;&lt;span&gt;use&lt;/span&gt;&lt;span&gt;(chaiAsPromised);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; port;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; opts;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; app;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;hostname&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;127.0.0.1&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;// 驗證 Provider 是否滿足所有 Consumer 的期望&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;describe&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;Pact Verification&quot;&lt;/span&gt;&lt;span&gt;, () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;before&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;port &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;3000&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;opts &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;// 我們需要知道 Provider 的名稱&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;provider: providerName,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;// 我們需要確定 Provider 運行的位置&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;providerBaseUrl: &lt;/span&gt;&lt;span&gt;`http://${&lt;/span&gt;&lt;span&gt;hostname&lt;/span&gt;&lt;span&gt;}:${&lt;/span&gt;&lt;span&gt;port&lt;/span&gt;&lt;span&gt;}`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;// 為了方便起見，我們提供了執行 npm run test:consumer 時，&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;// 產生的示例契約的路徑。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;opts &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;opts,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;pactUrls: [pactFile]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;app &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; server.&lt;/span&gt;&lt;span&gt;listen&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;3000&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;127.0.0.1&apos;&lt;/span&gt;&lt;span&gt;, () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;`Provider service listening on http://localhost:${&lt;/span&gt;&lt;span&gt;port&lt;/span&gt;&lt;span&gt;}`&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;after&lt;/span&gt;&lt;span&gt;(() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (app) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;app.&lt;/span&gt;&lt;span&gt;close&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;it&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;should validate the expectations of Order Web&quot;&lt;/span&gt;&lt;span&gt;, () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(opts)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Verifier&lt;/span&gt;&lt;span&gt;(opts)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;verifyProvider&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;output&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;Pact Verification Complete!&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(output);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;console.&lt;/span&gt;&lt;span&gt;error&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;Pact verification failed :(&quot;&lt;/span&gt;&lt;span&gt;, e);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;接著，我們運行 Provider 測試指令 &lt;code&gt;npm run test:provider&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;➜&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;npm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;test:provider&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; test:provider&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; node runProviderTest.js&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;Pact&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Verification&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Provider&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;service&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;listening&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;on&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;http://localhost:3000&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;provider:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;GettingStartedOrderApi&apos;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;providerBaseUrl:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;http://127.0.0.1:3000&apos;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;pactUrls:&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&apos;.../pact-5-minute-getting-started-guide/pacts/GettingStartedOrderWeb-GettingStartedOrderApi.json&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Verifying&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pact&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;between&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;GettingStartedOrderWeb&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;and&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;GettingStartedOrderApi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;request&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;for&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;orders&lt;/span&gt;&lt;span&gt; (0s &lt;/span&gt;&lt;span&gt;loading,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;441ms&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;verification&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;     &lt;/span&gt;&lt;span&gt;Given&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;there&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;are&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;orders&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;returns&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;response&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;which&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;has&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;status&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;code&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;200&lt;/span&gt;&lt;span&gt; (OK)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;includes&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;headers&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;&quot;Content-Type&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;with&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;application/json; charset=utf-8&quot;&lt;/span&gt;&lt;span&gt; (OK)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;has&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;matching&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt; (OK)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Pact&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Verification&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Complete!&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;finished:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;✔&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;should&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;validate&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;the&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;expectations&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;of&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Order&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Web&lt;/span&gt;&lt;span&gt; (603ms)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;passing&lt;/span&gt;&lt;span&gt; (607ms)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;provider&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;test&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;passed!&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Open&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;the&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pact&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;file&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;.../pact-5-minute-getting-started-guide/pacts/GettingStartedOrderWeb-GettingStartedOrderApi.json&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;以上就是 Pact 契約測試的本地範例運作原理。&lt;/p&gt;
&lt;h3 id=&quot;pactflow-1&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#pactflow-1&quot;&gt;PactFlow&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;最後，我們來介紹如何將契約發佈到 Pact Broker 進行管理。&lt;/p&gt;
&lt;p&gt;官方提供了一個名為 &lt;a href=&quot;https://pactflow.io/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;PactFlow&lt;/a&gt; 的全託管 Broker 服務，有免費方案可以使用。&lt;/p&gt;
&lt;p&gt;我們需要註冊 PactFlow 以取得 API Token 和 PactFlow base URL。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;1436&quot; height=&quot;817&quot; src=&quot;https://blog.amowu.com/_astro/image7.B3zv3_D-_Z1m7mPM.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image7.B3zv3_D-_29lib9.webp 640w, https://blog.amowu.com/_astro/image7.B3zv3_D-_nxWLl.webp 750w, https://blog.amowu.com/_astro/image7.B3zv3_D-_Z17Sbsa.webp 828w, https://blog.amowu.com/_astro/image7.B3zv3_D-_2sHT8B.webp 1080w, https://blog.amowu.com/_astro/image7.B3zv3_D-_Z2x3VQi.webp 1280w, https://blog.amowu.com/_astro/image7.B3zv3_D-_Z1m7mPM.webp 1436w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;回到 Provider 測試，我們重新配置了相關變數，這樣 Provider 才有辦法從 PactFlow 獲取契約：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame has-title&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;provider/provider.spec.js&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;describe&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;Pact Verification&quot;&lt;/span&gt;&lt;span&gt;, () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;before&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;// 如果我們有 Broker，那麼還有一些相關的配置&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (process.env.&lt;/span&gt;&lt;span&gt;PACT_BROKER_BASE_URL&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;opts &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;opts,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;// 我們需要知道我們的 Broker 在哪裡&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;pactBrokerUrl: process.env.&lt;/span&gt;&lt;span&gt;PACT_BROKER_BASE_URL&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;// 我們需要有關我們正在驗證的 Provider 版本的詳細信息，&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;// 以便我們稍後可以識別它。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;providerVersion: process.env.&lt;/span&gt;&lt;span&gt;GIT_COMMIT&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;providerVersionBranch: process.env.&lt;/span&gt;&lt;span&gt;GIT_BRANCH&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;// 只有在 CI 的情況下，我們才需要自動發布契約&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;publishVerificationResult: &lt;/span&gt;&lt;span&gt;!!&lt;/span&gt;&lt;span&gt;process.env.&lt;/span&gt;&lt;span&gt;CI&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;??&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;// 我們需要設置 Broker 身份驗證選項&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (process.env.&lt;/span&gt;&lt;span&gt;PACT_BROKER_TOKEN&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;opts &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;          &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;opts,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;pactBrokerToken: process.env.&lt;/span&gt;&lt;span&gt;PACT_BROKER_TOKEN&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;配置好之後，我們準備執行 &lt;code&gt;run test:broker&lt;/code&gt; 指令。&lt;/p&gt;
&lt;p&gt;在執行之前，我們先來看一下這個指令，前面介紹指令時有提到，&lt;code&gt;npm run test:broker&lt;/code&gt; 會執行三個步驟的指令：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;npm run test:consumer&lt;/code&gt; - 執行 Consumer 測試，產生契約&lt;/li&gt;
&lt;li&gt;&lt;code&gt;npm run pact:publish&lt;/code&gt; - 將契約發佈到 PactFlow 上（script 細節本文就不介紹了）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;npm run test:provider&lt;/code&gt; - Provider 從 PactFlow 獲取契約，執行 Provider 測試&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;理解指令的作用之後，我們開始執行 &lt;code&gt;run test:broker&lt;/code&gt; 指令：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;➜&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;PACT_BROKER_BASE_URL=&amp;lt;YOUR_PACT_FLOW_BASE_URL&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;PACT_BROKER_TOKEN=&amp;lt;YOUR_PACT_FLOW_API_TOKEN&amp;gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# 如果在 CI 執行的話，通常會自帶 GIT_COMMIT 和 GIT_BRANCH，可以不用指定&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;GIT_COMMIT&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rev-parse&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;HEAD&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;GIT_BRANCH=$(git &lt;/span&gt;&lt;span&gt;rev-parse&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--abbrev-ref&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;HEAD&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;npm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;test:broker&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;這裡就不貼出冗長的輸出結果了，如果順利的話，你就會看到這三個步驟的執行過程並且通過測試。&lt;/p&gt;
&lt;p&gt;測試結束之後，就可以在 PactFlow 上看到發布的契約文件：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;1436&quot; height=&quot;823&quot; src=&quot;https://blog.amowu.com/_astro/image8.CwDGPAHw_Z20opGI.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image8.CwDGPAHw_1CjduL.webp 640w, https://blog.amowu.com/_astro/image8.CwDGPAHw_1GuoBF.webp 750w, https://blog.amowu.com/_astro/image8.CwDGPAHw_Q7x82.webp 828w, https://blog.amowu.com/_astro/image8.CwDGPAHw_ZD3qWF.webp 1080w, https://blog.amowu.com/_astro/image8.CwDGPAHw_1SQ96H.webp 1280w, https://blog.amowu.com/_astro/image8.CwDGPAHw_Z20opGI.webp 1436w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;之後你就可以把這套機制丟到 CI 上，自動化契約測試的流程了，未來如果 Consumer 或 Provider 有一方的更改違反了契約，CI 測試就會失敗。&lt;/p&gt;
&lt;p&gt;以上就是官方&lt;a href=&quot;https://docs.pact.io/5-minute-getting-started-guide&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;五分鐘指南&lt;/a&gt;的基本原理，當然真實環境一定沒有像範例這麼單純，例如 Provider 端通常不會跟 Consumer 一起跑 CI，這時候可能就會需要用到 Pact 的 &lt;a href=&quot;https://docs.pact.io/pact_broker/webhooks?utm_source=pact&amp;amp;utm_medium=client_library&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;webhooks&lt;/a&gt; 之類的，有興趣的朋友可以自行深入探索。&lt;/p&gt;
&lt;h2 id=&quot;總結&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#總結&quot;&gt;總結&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;我們來回顧一下 Pact Contract Testing 的概念：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;整合測試（Integration testing）雖然在發布時的信心表現較好，但它引入了依賴關係，反饋速度慢，測試不穩定等問題，增加了維護難度。&lt;/li&gt;
&lt;li&gt;獨立測試（Unit testing）雖然解決了整合測試的一些問題，但難以模擬真實環境，因此發布的信心不足。&lt;/li&gt;
&lt;li&gt;Pact 契約測試引入中間契約的概念，記錄了 Consumer 對 Provider mock server 預期的 request 和 response。Provider 則根據契約回放，確保真實環境的互動與 Consumer 的預期匹配。&lt;/li&gt;
&lt;li&gt;契約需要有地方管理，Pact 稱作 Broker。你可以自己實作一個，也可以使用 Pact 開源的 Pact Broker，或使用 Pact 提供的 Broker 服務 PactFlow。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;參考&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#參考&quot;&gt;參考&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://pact.io/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://pact.io/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.pact.io/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;General Pact documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://pactflow.io/how-pact-works/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;How Pact contract testing works&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.pact.io/pact_broker/overview&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;The Pact Broker overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.pactflow.io/docs/bi-directional-contract-testing&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Bi-Directional Contract Testing Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.pactflow.io/docs/workshops&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;PactFlow University&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>Tech</category></item><item><title>Automate browser testing with tools</title><link>https://blog.amowu.com/posts/2024-06-18-automate-browser-testing-with-tools/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2024-06-18-automate-browser-testing-with-tools/</guid><description>隨著科技的迅速發展，網站和應用程式的複雜性日益增加，對品質和穩定性的要求也變得更加迫切。在這篇文章中，我們將探討自動化測試的重要性、如何在組織中實施自動化測試，以及 Chrome 提供的測試工具和策略。</description><pubDate>Tue, 18 Jun 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;隨著科技的迅速發展，網站和應用程式的複雜性日益增加，對品質和穩定性的要求也變得更加迫切。在這篇文章中，我們將探討自動化測試的重要性、如何在組織中實施自動化測試，以及 Chrome 提供的測試工具和策略。&lt;/p&gt;
&lt;h2 id=&quot;前言&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#前言&quot;&gt;前言&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;最近有許多跟前端相關的 conference，例如 &lt;a href=&quot;https://conf.react.dev/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;React Conf 2024&lt;/a&gt;、&lt;a href=&quot;https://io.google/2024/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Google I/O 2024&lt;/a&gt; 以及 &lt;a href=&quot;https://react.paris/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;React Paris 24&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;雖然每段議程都不長，但要全部追完也是很累人，所以公司同事就揪大家一起共筆。&lt;/p&gt;
&lt;p&gt;我認領到的議程有這三場：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://conf.react.dev/talks/8&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Real-time server components&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://io.google/2024/explore/f6a77c6d-e1fe-434b-92ca-879f8d153672/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Automate browser testing with tools and best practices from Chrome&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://io.google/2024/explore/8ae18b72-028e-4722-9a05-4a480048e629/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Multi-page application View Transitions are here&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;這篇是第二篇，介紹《Automate browser testing with tools and best practices from Chrome》，第一篇可以點擊下方連結前往。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.amowu.com/posts/2024-06-08-real-time-server-components/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Real-time Server Components&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;自動化測試的重要性及其優勢&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#自動化測試的重要性及其優勢&quot;&gt;自動化測試的重要性及其優勢&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;確保品質&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&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 alt=&quot;觀察全球受歡迎的電子商務網站，平均來說，使用者需要完成至少 16 次的互動才能順利下訂單。&quot; width=&quot;1751&quot; height=&quot;653&quot; src=&quot;https://blog.amowu.com/_astro/image1.CcH5WE0F_ZgFqKL.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image1.CcH5WE0F_ZnenLT.webp 640w, https://blog.amowu.com/_astro/image1.CcH5WE0F_Rji1B.webp 750w, https://blog.amowu.com/_astro/image1.CcH5WE0F_Z285Vbz.webp 828w, https://blog.amowu.com/_astro/image1.CcH5WE0F_1k3SQi.webp 1080w, https://blog.amowu.com/_astro/image1.CcH5WE0F_ZTw26K.webp 1280w, https://blog.amowu.com/_astro/image1.CcH5WE0F_bUhLs.webp 1668w, https://blog.amowu.com/_astro/image1.CcH5WE0F_ZgFqKL.webp 1751w&quot; /&gt;
&lt;em&gt;觀察全球受歡迎的電子商務網站，平均來說，使用者需要完成至少 16 次的互動才能順利下訂單。&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;每一次互動都是潛在的失敗點。跑版，按鈕無法點擊、API 沒有正確回應等…。而這還只是網站中數百個互動情境的其中之一。&quot; width=&quot;1804&quot; height=&quot;464&quot; src=&quot;https://blog.amowu.com/_astro/image2.CuVsWpTH_1JIIgA.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image2.CuVsWpTH_NTzrV.webp 640w, https://blog.amowu.com/_astro/image2.CuVsWpTH_1niJq7.webp 750w, https://blog.amowu.com/_astro/image2.CuVsWpTH_1dsWQr.webp 828w, https://blog.amowu.com/_astro/image2.CuVsWpTH_2v3TlH.webp 1080w, https://blog.amowu.com/_astro/image2.CuVsWpTH_Z12OuXu.webp 1280w, https://blog.amowu.com/_astro/image2.CuVsWpTH_ZxeT3U.webp 1668w, https://blog.amowu.com/_astro/image2.CuVsWpTH_1JIIgA.webp 1804w&quot; /&gt;
&lt;em&gt;每一次互動都是潛在的失敗點。跑版，按鈕無法點擊、API 沒有正確回應等…。而這還只是網站中數百個互動情境的其中之一。&lt;/em&gt;&lt;/p&gt;
&lt;h3 id=&quot;提高開發效率&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#提高開發效率&quot;&gt;提高開發效率&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;自動化測試在每次 commit、Pull Request 和 release 時自動運行，可以減少了手動測試的資源和時間消耗，使開發團隊能更專注於新功能的開發和現有功能的優化。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;1619&quot; height=&quot;354&quot; src=&quot;https://blog.amowu.com/_astro/image3.Cq6EIvPW_Xry2Q.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image3.Cq6EIvPW_D9dbU.webp 640w, https://blog.amowu.com/_astro/image3.Cq6EIvPW_Zud4N2.webp 750w, https://blog.amowu.com/_astro/image3.Cq6EIvPW_119zkR.webp 828w, https://blog.amowu.com/_astro/image3.Cq6EIvPW_85Msy.webp 1080w, https://blog.amowu.com/_astro/image3.Cq6EIvPW_1Paj9A.webp 1280w, https://blog.amowu.com/_astro/image3.Cq6EIvPW_Xry2Q.webp 1619w&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;如何在組織中實施自動化測試&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#如何在組織中實施自動化測試&quot;&gt;如何在組織中實施自動化測試&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;統一心態&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#統一心態&quot;&gt;統一心態&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;在實施自動化測試的過程中，團隊內部需要達成共識：品質即功能（Quality as a feature）。這意味著將品質視為一種投資，而不是負擔。滿意的客戶會帶來繁榮的業務，一旦這種心態被接受，測試（Tests）自然成為產品開發工作流程的一部分。&lt;/p&gt;
&lt;h3 id=&quot;測試流程的整合&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#測試流程的整合&quot;&gt;測試流程的整合&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;將自動化測試納入每個功能完成的定義中（DoD），並在開發流程的每個關鍵步驟中運行測試。例如，每次 commit 程式碼、每次 Pull Request 以及每次完整 release 或環境變更後，都應進行測試。此外，確保測試相關的衡量標準對組織中的每個人都是可見和可訪問的，例如，每月發佈次數、bug reports、測試覆蓋率，以便追踪進度、影響並更好地了解產品的健康和品質。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;1737&quot; height=&quot;367&quot; src=&quot;https://blog.amowu.com/_astro/image4.CP5DvvMo_ZuWeln.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image4.CP5DvvMo_1KddWK.webp 640w, https://blog.amowu.com/_astro/image4.CP5DvvMo_Z1SbRiL.webp 750w, https://blog.amowu.com/_astro/image4.CP5DvvMo_ZmOd9R.webp 828w, https://blog.amowu.com/_astro/image4.CP5DvvMo_Zhni4r.webp 1080w, https://blog.amowu.com/_astro/image4.CP5DvvMo_Z1zbvEW.webp 1280w, https://blog.amowu.com/_astro/image4.CP5DvvMo_vxpld.webp 1668w, https://blog.amowu.com/_astro/image4.CP5DvvMo_ZuWeln.webp 1737w&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;chrome-測試工具及策略&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#chrome-測試工具及策略&quot;&gt;Chrome 測試工具及策略&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;chrome-fortesting&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#chrome-fortesting&quot;&gt;Chrome for Testing&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;新推出的 Chrome for Testing 是專為自動化測試設計的特製版本，它不會自動更新，但包括所有 Chrome Stable 版本的功能，可以用於複製特定版本的測試環境。這對於需要在特定版本的 Chrome 上進行測試的情況特別有用。&lt;/p&gt;
&lt;div class=&quot;table-wrap&quot;&gt;












































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Channel&lt;/th&gt;&lt;th&gt;&lt;img alt=&quot;&quot; width=&quot;132&quot; height=&quot;133&quot; src=&quot;https://blog.amowu.com/_astro/chrome-for-testing.hZW4_sjL_jAAfH.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/chrome-for-testing.hZW4_sjL_jAAfH.webp 132w&quot; /&gt; Chrome for Testing&lt;/th&gt;&lt;th&gt;&lt;img alt=&quot;&quot; width=&quot;71&quot; height=&quot;71&quot; src=&quot;https://blog.amowu.com/_astro/chrome-stable.SYuj17G0_2baxv2.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/chrome-stable.SYuj17G0_2baxv2.webp 71w&quot; /&gt; Chrome Stable&lt;/th&gt;&lt;th&gt;&lt;img alt=&quot;&quot; width=&quot;69&quot; height=&quot;70&quot; src=&quot;https://blog.amowu.com/_astro/chrome-beta.DbB3KfYF_ZrcxTU.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/chrome-beta.DbB3KfYF_ZrcxTU.webp 69w&quot; /&gt; Chrome Beta&lt;/th&gt;&lt;th&gt;&lt;img alt=&quot;&quot; width=&quot;69&quot; height=&quot;71&quot; src=&quot;https://blog.amowu.com/_astro/chrome-dev.c-8Y2c7I_2mrkt1.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/chrome-dev.c-8Y2c7I_2mrkt1.webp 69w&quot; /&gt; Chrome Dev&lt;/th&gt;&lt;th&gt;&lt;img alt=&quot;&quot; width=&quot;72&quot; height=&quot;72&quot; src=&quot;https://blog.amowu.com/_astro/chrome-canary.CNM9Nt4Q_2aGK86.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/chrome-canary.CNM9Nt4Q_2aGK86.webp 72w&quot; /&gt; Chrome Canary&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;特點&lt;/td&gt;&lt;td&gt;特製版本，不自動更新，包含所有 Chrome Stable 功能&lt;/td&gt;&lt;td&gt;針對大眾使用者，功能穩定可靠&lt;/td&gt;&lt;td&gt;即將成為 Stable 版本的測試版，功能相對穩定 ｜ 最新功能和修復，每週更新一次&lt;/td&gt;&lt;td&gt;最實驗性的版本，每天更新，包含所有最新功能和修復&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;更新頻率&lt;/td&gt;&lt;td&gt;不定期&lt;/td&gt;&lt;td&gt;每月&lt;/td&gt;&lt;td&gt;每四至六週&lt;/td&gt;&lt;td&gt;每週&lt;/td&gt;&lt;td&gt;每天&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;適用對象&lt;/td&gt;&lt;td&gt;測試工程師、開發人員&lt;/td&gt;&lt;td&gt;所有人&lt;/td&gt;&lt;td&gt;UX/UI 團隊、開發團隊&lt;/td&gt;&lt;td&gt;工程團隊、開發人員&lt;/td&gt;&lt;td&gt;開發者、測試人員&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;主要用途&lt;/td&gt;&lt;td&gt;自動化瀏覽器測試&lt;/td&gt;&lt;td&gt;日常瀏覽使用、確保穩定性&lt;/td&gt;&lt;td&gt;提前預見即將發佈的功能和變化&lt;/td&gt;&lt;td&gt;測試最新功能和 API，提前適應變化&lt;/td&gt;&lt;td&gt;是用最新的 Chrome 功能和 API，進行實驗性測試&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
&lt;h3 id=&quot;新-headless模式&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#新-headless模式&quot;&gt;新 Headless 模式&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;過去 Chrome 的舊 headless 模式（無頭模式）是單獨維護的瀏覽器版本，容易發生結果不一致的問題。新 headless 模式不再是單獨維護的瀏覽器，而是與 Chrome Stable 使用相同的程式碼，支援所有 Chrome Stable 功能。這使得測試 Extensions 和使用 WebGPU（AI 相關應用）的網站變得更加容易。&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;// for Chrome Extensions&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; puppeteer &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;puppeteer&apos;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;EXTENSION_PATH&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;~/dino/workspace/my-super-cool-extension-src&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;EXTENSION_ID&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;jkomgjfbbjocikdmilgaehbfpllalmia&apos;&lt;/span&gt;&lt;span&gt; ;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;browser&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; puppeteer.&lt;/span&gt;&lt;span&gt;launch&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;headless: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;// Defaults to new headless mode from Puppeteer 22+&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;args: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&apos;--disable-extensions-except=${EXTENSION_PATH}&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&apos;--load-extension=${EXTENSION_PATH}&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;page&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; browser.&lt;/span&gt;&lt;span&gt;newPage&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; page &lt;/span&gt;&lt;span&gt;goto&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;chrome-extension://${EXTENSION_ID}/page.htm&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;// for Chrome Extensions&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; puppeteer &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;puppeteer&apos;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;EXTENSION_PATH&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;~/dino/workspace/my-super-cool-extension-src&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;EXTENSION_ID&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;jkomgjfbbjocikdmilgaehbfpllalmia&apos;&lt;/span&gt;&lt;span&gt; ;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;browser&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; puppeteer.&lt;/span&gt;&lt;span&gt;launch&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;headless: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;// Defaults to new headless mode from Puppeteer 22+&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;args: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&apos;--disable-extensions-except=${EXTENSION_PATH}&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&apos;--load-extension=${EXTENSION_PATH}&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const page &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; browser.&lt;/span&gt;&lt;span&gt;newPage&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; page &lt;/span&gt;&lt;span&gt;goto&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;chrome-extension://${EXTENSION_ID}/page.htm&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;// for WebGPU&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const browser &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; puppeteer.&lt;/span&gt;&lt;span&gt;launch&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;// Defaults to new headless mode from Puppeteer 22+&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;headless: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;args: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&apos;--no-sandbox&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&apos;--headless=new&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&apos;--use-angle=vulkan&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&apos;--enable-features=Vulkan&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&apos;--disable-vulkan-surface&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&apos;--enable-unsafe-webgpu&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3 id=&quot;跨瀏覽器測試&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#跨瀏覽器測試&quot;&gt;跨瀏覽器測試&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;為了確保網站在所有瀏覽器中都能良好運行，Google 與主要瀏覽器和測試工具供應商合作開發了 WebDriver BiDi 協議。這個協議允許使用相同的命令自動化所有瀏覽器，不論其引擎如何。目前，部分自動化工具如 Selenium、WebDriver I/O 和 Puppeteer 已部分支持 WebDriver BiDi。Firefox、Chrome 和 Edge 都已經或計劃部分支持 WebDriver BiDi。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;您仍可以使用現有的 WebDriver 標準來自動化所有瀏覽器。&quot; width=&quot;1714&quot; height=&quot;843&quot; src=&quot;https://blog.amowu.com/_astro/image5.zfLICKyv_jD8r4.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image5.zfLICKyv_ccHjC.webp 640w, https://blog.amowu.com/_astro/image5.zfLICKyv_Z1w273W.webp 750w, https://blog.amowu.com/_astro/image5.zfLICKyv_1n7mUH.webp 828w, https://blog.amowu.com/_astro/image5.zfLICKyv_q2xNN.webp 1080w, https://blog.amowu.com/_astro/image5.zfLICKyv_2sM4sn.webp 1280w, https://blog.amowu.com/_astro/image5.zfLICKyv_Z1mGQ1s.webp 1668w, https://blog.amowu.com/_astro/image5.zfLICKyv_jD8r4.webp 1714w&quot; /&gt;
&lt;em&gt;您仍可以使用現有的 WebDriver 標準來自動化所有瀏覽器。&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;總結&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#總結&quot;&gt;總結&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;自動化測試是一種確保產品品質的重要手段，應成為每個開發團隊的核心部分。通過充分利用 Chrome 提供的測試工具（Chrome for Testing、新 headless 模式）和策略（WebDriver BiDi），我們可以確保產品在各個瀏覽器中的一致性和可靠性。品質即功能，自動化測試能使我們更自信地面對未來的挑戰，並提供更優質的用戶體驗。&lt;/p&gt;</content:encoded><category>Tech</category></item><item><title>人工智慧的極限</title><link>https://blog.amowu.com/posts/2024-06-10-limits-of-artificial-intelligence/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2024-06-10-limits-of-artificial-intelligence/</guid><description>本文將探討哪些問題是 AI 可以解決的，哪些是它無法觸及的。希望通過這篇文章，讀者能夠對計算的本質與 AI 的限制有更清晰的認識，理性看待這項技術的發展。</description><pubDate>Mon, 10 Jun 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;本文將探討哪些問題是 AI 可以解決的，哪些是它無法觸及的。希望通過這篇文章，讀者能夠對計算的本質與 AI 的限制有更清晰的認識，理性看待這項技術的發展。&lt;/p&gt;
&lt;h2 id=&quot;前言&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#前言&quot;&gt;前言&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;在這個數位化迅速發展的時代，AI 逐漸成為我們生活中的一部分。從智能助理到自動駕駛汽車，AI 技術的應用範圍日益擴大。大數據和深度學習技術的飛速進步，使得 AI 在語音識別、圖像處理等領域表現得愈發出色。近期，生成式 AI 例如 ChatGPT 和 DALL-E，更是展現了 AI 在創意和生成內容方面的巨大潛力。&lt;/p&gt;
&lt;p&gt;然而，隨著這些技術的進步，許多人也開始擔心：AI 會不會超越人類的能力，甚至取而代之？&lt;/p&gt;
&lt;p&gt;最近讀了吳軍的《計算之魂》一書之後，我深受啟發，決定節錄書中的精華內容，撰寫成這篇文章。&lt;/p&gt;
&lt;h2 id=&quot;什麼是計算機&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#什麼是計算機&quot;&gt;什麼是計算機&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;計算機（電腦）的歷史可以追溯到幾千年前，從中國的算盤到現代的電子計算機。儘管現代電子計算機在 1946 年才被發明，但計算的概念早已存在。計算機的核心構成包括&lt;strong&gt;計算單元&lt;/strong&gt;、&lt;strong&gt;存儲單元&lt;/strong&gt;和&lt;strong&gt;控制指令序列&lt;/strong&gt;（&lt;a href=&quot;https://en.wikipedia.org/wiki/Von_Neumann_architecture&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;馮諾伊曼架構&lt;/a&gt;）。沒有指令序列，計算機就不完整。&lt;/p&gt;
&lt;p&gt;中國的算盤雖然簡單，卻蘊含了基本的計算思想。算盤的設計基於珠算口訣，使用中國算盤，人所提供的不過是機械動能，而非頭腦中的運算能力，算盤是在口訣指令的控制下完成機械運動的，而機械運動能得到計算結果，這與現代計算機的指令控制有異曲同工之妙。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;中國算盤（圖右）之所以被視為計算機，是因為它依靠珠算口訣控制操作，而古羅馬算盤（圖左）只是輔助計算工具，沒有指令序列的控制功能。&quot; width=&quot;677&quot; height=&quot;203&quot; src=&quot;https://blog.amowu.com/_astro/image1.C7626o-f_fA8i8.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image1.C7626o-f_Z1DUVKf.webp 640w, https://blog.amowu.com/_astro/image1.C7626o-f_fA8i8.webp 677w&quot; /&gt;
&lt;em&gt;中國算盤（圖右）之所以被視為計算機，是因為它依靠珠算口訣控制操作，而古羅馬算盤（圖左）只是輔助計算工具，沒有指令序列的控制功能。&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;布林代數和開關電路&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#布林代數和開關電路&quot;&gt;布林代數和開關電路&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;進入工業時代後，人們開始設計機械計算機來自動執行計算任務。&lt;a href=&quot;https://en.wikipedia.org/wiki/Blaise_Pascal&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;布萊茲·帕斯卡&lt;/a&gt;和&lt;a href=&quot;https://en.wikipedia.org/wiki/Gottfried_Wilhelm_Leibniz&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;哥特弗利德·萊布尼茲&lt;/a&gt;先後發明了能進行加減運算的機械計算機，而&lt;a href=&quot;https://en.wikipedia.org/wiki/Charles_Babbage&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;查爾斯·巴貝奇&lt;/a&gt;則設計了更複雜的差分機，儘管未能成功製造出來，但其思想深遠地影響了後來的計算機發展。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/George_Boole&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;喬治·布爾&lt;/a&gt;發明了&lt;a href=&quot;https://en.wikipedia.org/wiki/Boolean_algebra_%28structure%29&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;布林代數&lt;/a&gt;統一算術與邏輯，為現代邏輯電路奠定了基礎。&lt;a href=&quot;https://en.wikipedia.org/wiki/Claude_Shannon&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;克勞德·夏農&lt;/a&gt;（&lt;a href=&quot;https://en.wikipedia.org/wiki/Information_theory&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;信息論&lt;/a&gt;之父）則是將布林代數應用於電氣工程，設計出基於開關電路的邏輯運算，這一突破性發明使現代數位計算機成為可能。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Konrad_Zuse&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;康拉德·楚澤&lt;/a&gt;通過簡單模組的複製，成功研制出了世界上第一台可編程計算機 &lt;a href=&quot;https://en.wikipedia.org/wiki/Z1_%28computer%29&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Z1&lt;/a&gt;。這台計算機利用布林代數和邏輯電路，實現了複雜計算的模組化，使得計算機的設計和製造進入了一個新階段。&lt;/p&gt;
&lt;h2 id=&quot;數學與邏輯的局限&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#數學與邏輯的局限&quot;&gt;數學與邏輯的局限&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;在討論 AI 的極限時，必須首先理解數學和邏輯的邊界。數學家&lt;a href=&quot;https://en.wikipedia.org/wiki/David_Hilbert&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;大衛·希爾伯特&lt;/a&gt;提出了三個重要的問題：數學是否完備（complete）、一致（consistent）和可判定（decisive）。所謂完備性，就是說對於任意一個命題，要麼可以證明它是對的，要麼可以證明它是錯的；一致性指的是一個命題不能既是真的又是假的；可判定性則是能否判斷一個具體問題是否有答案。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Kurt_G%C3%B6del&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;庫爾特·哥德爾&lt;/a&gt;通過其&lt;a href=&quot;https://en.wikipedia.org/wiki/G%C3%B6del%27s_incompleteness_theorems&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;哥德爾不完備定理&lt;/a&gt;證明，數學不可能同時是完備和一致的。這意味著有些問題是無法通過數學方法證明其對錯的。1970 年，蘇聯數學家&lt;a href=&quot;https://en.wikipedia.org/wiki/Yuri_Matiyasevich&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;尤里·馬季亞謝維奇&lt;/a&gt;解決了&lt;a href=&quot;https://en.wikipedia.org/wiki/Hilbert%27s_tenth_problem&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;希爾伯特第十問題&lt;/a&gt;，證明了對於絕大多數&lt;a href=&quot;https://en.wikipedia.org/wiki/Diophantine_equation&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;不定方程&lt;/a&gt;，我們既不能證明它們有整數解，也無法證明它們無解。這說明，&lt;strong&gt;有些問題即使是最先進的 AI 也無法解決，因為它們根本不是數學問題&lt;/strong&gt;。&lt;/p&gt;
&lt;h2 id=&quot;圖靈機的理論邊界&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#圖靈機的理論邊界&quot;&gt;圖靈機的理論邊界&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;在 20 世紀 30 年代中期，&lt;a href=&quot;https://en.wikipedia.org/wiki/Alan_Turing&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;艾倫·圖靈&lt;/a&gt;開始思考三個非常根本的問題：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;數學問題是否都有明確的答案？&lt;/li&gt;
&lt;li&gt;如果有明確的答案，是否可以通過有限步的計算得到？&lt;/li&gt;
&lt;li&gt;是否能有一種假想的機器，通過機械運動解決這些數學問題？&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;圖靈提出的&lt;a href=&quot;https://en.wikipedia.org/wiki/Turing_machine&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;圖靈機&lt;/a&gt;模型定義了哪些問題是可計算的（可以在有限步內完成計算），哪些問題是不可計算的（無法在有限步內完成計算）。&lt;/p&gt;
&lt;p&gt;圖靈機的概念不僅幫助定義了計算的邊界，還引入了幾個關鍵的計算元素：紙帶（相當於現代的存儲地址）、讀寫頭、規則表（即今天的指令集）和寄存器（即計算機狀態）。這些元素構成了現代計算機的基本原理，使得計算過程與機械運動相對應。圖靈機將複雜的計算過程簡化為一系列基本操作，這一思路奠定了數字計算的基礎。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;圖靈機將複雜的計算過程簡化為一系列基本操作，這一思路奠定了數字計算的基礎。&quot; width=&quot;685&quot; height=&quot;447&quot; src=&quot;https://blog.amowu.com/_astro/image2.CbbMh341_ZLAvJx.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image2.CbbMh341_ZO6CwX.webp 640w, https://blog.amowu.com/_astro/image2.CbbMh341_ZLAvJx.webp 685w&quot; /&gt;
&lt;em&gt;圖靈機將複雜的計算過程簡化為一系列基本操作，這一思路奠定了數字計算的基礎。&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;圖靈在思考計算機相關問題時，會回到問題的本源，拋開具體技術，從計算的本質來尋找計算機的極限。&lt;strong&gt;現代計算機，包括最先進的 AI 系統，都沒有超越圖靈機定義的可計算問題範疇。&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;人工智慧的極限&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#人工智慧的極限&quot;&gt;人工智慧的極限&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;在 2016 年，當 Google 的 AlphaGo 擊敗了李世石之後，許多人開始擔心世界上所有的事情是否都能由計算機比人類做得更好。然而，這種擔心多少有點杞人憂天，因為無論是什麼樣的計算機，實際上只能解決世界上一小部分問題。&lt;/p&gt;
&lt;p&gt;我們在前面提到，世界上的很多問題並不是數學問題，這一點已經由&lt;a href=&quot;https://en.wikipedia.org/wiki/David_Hilbert&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;希爾伯特&lt;/a&gt;和&lt;a href=&quot;https://en.wikipedia.org/wiki/Kurt_G%C3%B6del&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;哥德爾&lt;/a&gt;等人證明了。如果我們畫一個大圈代表所有問題（下圖集合 S₁）的話，那麼所有的數學問題（集合 S₂）只是這個大圈中的一個小圈而已。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Yuri_Matiyasevich&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;馬季亞謝維奇&lt;/a&gt;解決了&lt;a href=&quot;https://en.wikipedia.org/wiki/Hilbert%27s_tenth_problem&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;希爾伯特第十問題&lt;/a&gt;，這一發現使得我們清楚地知道，有些數學問題無法判定答案存在與否，這意味著我們無法用邏輯方法推導出答案。因此，我們可以將數學問題中的一小部分看作是可判定（是否有答案）的問題（集合 S₃），而真正有答案的問題（集合 S₄）的數量則更少。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;在數學領域，只有一部分問題我們能夠判斷是否存在答案，大部分問題是無法判斷答案是否存在的。&quot; width=&quot;387&quot; height=&quot;225&quot; src=&quot;https://blog.amowu.com/_astro/image3.Dwnf97T3_Z1h0xpP.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image3.Dwnf97T3_Z1h0xpP.webp 387w&quot; /&gt;
&lt;em&gt;在數學領域，只有一部分問題我們能夠判斷是否存在答案，大部分問題是無法判斷答案是否存在的。&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;在有答案的問題中，有一些是可以通過圖靈機解決的問題（下圖集合 S₅），即我們前面提到的可計算問題。現實生活中的計算機能解決的問題（即工程可解問題，集合 S₆）是可計算問題的一個小子集。&lt;/p&gt;
&lt;p&gt;AI 的實際應用主要集中在工程可解問題上，這些問題可以通過現有的算法和計算能力在合理的時間內解決。然而，很多問題的計算複雜度高於可接受的範圍，即使計算機速度提升一萬倍，也無法顯著改善這些問題的解決能力。例如，某些加密算法的破解時間即使在量子計算機的幫助下也需要極其漫長的時間。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;人工智慧可解問題只是有答案的問題中很少的一部分&quot; width=&quot;405&quot; height=&quot;215&quot; src=&quot;https://blog.amowu.com/_astro/image4.BuuH4oUL_Z1iU22r.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image4.BuuH4oUL_Z1iU22r.webp 405w&quot; /&gt;
&lt;em&gt;人工智慧可解問題只是有答案的問題中很少的一部分&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;今天的人工智慧主要是基於大數據的深度學習。我們可以把人工智慧系統理解為由特定程序（控制指令序列）控制的、能夠解決某一類問題的計算機。例如，語音和圖像識別、無人駕駛、自動翻譯、下圍棋或象棋等，這些問題並未超出圖靈機可計算問題的範疇。&lt;/p&gt;
&lt;p&gt;人工智慧之所以顯得很聰明，是因為很多問題過去人們沒有找到轉變為數學問題的橋梁，而現在找到了。這意味著集合 S₆ 沿著某個維度擴大了一些範疇，但無論怎麼擴展，它都不可能超出可計算問題的范疇。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;利用 AI 技術已經解決的問題（大圓內的小圓和橢圓）只是工程可解問題中的一小部分&quot; width=&quot;361&quot; height=&quot;183&quot; src=&quot;https://blog.amowu.com/_astro/image5.r47niBag_184Si0.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image5.r47niBag_184Si0.webp 361w&quot; /&gt;
&lt;em&gt;利用 AI 技術已經解決的問題（大圓內的小圓和橢圓）只是工程可解問題中的一小部分&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;很多人只看到局部的進步，就誤認為 AI 技術已經解決大部分問題了&quot; width=&quot;379&quot; height=&quot;222&quot; src=&quot;https://blog.amowu.com/_astro/image6.CzDpQ9DQ_1aRg8Y.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image6.CzDpQ9DQ_1aRg8Y.webp 379w&quot; /&gt;
&lt;em&gt;很多人只看到局部的進步，就誤認為 AI 技術已經解決大部分問題了&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;結論&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#結論&quot;&gt;結論&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;迄今為止，計算機相關理論和技術的發展都沒有超越圖靈機的范疇。也就是說，80 多年前圖靈為計算機所能解決的問題劃的那條線，至今還沒有被逾越。這說明理論對工程的影響是多麼深遠。在學術界，試圖越過圖靈劃的紅線其實沒有必要。今天，世界上需要用計算機、用人工智慧來解決的問題實在太多，因此將注意力放在利用它們解決現有的問題上，而不是杞人憂天或異想天開，更有意義。&lt;/p&gt;
&lt;p&gt;未來是否會有能夠解決非數學問題的機器？或許會有，但這不是我們今天所談論的計算機。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;以上內容節錄自吳軍《計算之魂》這本書的第一章，省略了很多細節。如果你對這些內容感興趣，強烈建議購買這本書進行詳細閱讀。希望這篇文章能幫助你更深入地了解計算的本質。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;800&quot; height=&quot;800&quot; src=&quot;https://blog.amowu.com/_astro/image7.CPmOfY3K_Z6A7WJ.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image7.CPmOfY3K_DsQhA.webp 640w, https://blog.amowu.com/_astro/image7.CPmOfY3K_UPsFg.webp 750w, https://blog.amowu.com/_astro/image7.CPmOfY3K_Z6A7WJ.webp 800w&quot; /&gt;&lt;/p&gt;</content:encoded><category>Tech</category></item><item><title>Real-time Server Components</title><link>https://blog.amowu.com/posts/2024-06-08-real-time-server-components/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2024-06-08-real-time-server-components/</guid><description>打破無狀態伺服器的限制：利用 Durable Objects 和 React Server Components 構建即時多人應用</description><pubDate>Sat, 08 Jun 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;打破無狀態伺服器的限制：利用 Durable Objects 和 React Server Components 構建即時多人應用&lt;/p&gt;
&lt;h2 id=&quot;前言&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#前言&quot;&gt;前言&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;最近有許多跟前端相關的 conference，例如 &lt;a href=&quot;https://conf.react.dev/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;React Conf 2024&lt;/a&gt;、&lt;a href=&quot;https://io.google/2024/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Google I/O 2024&lt;/a&gt; 以及 &lt;a href=&quot;https://react.paris/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;React Paris 24&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;雖然每段議程都不長，但要全部追完也是很累人，所以公司同事就揪大家一起共筆。&lt;/p&gt;
&lt;p&gt;我認領到的議程有這三場：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://conf.react.dev/talks/8&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Real-time server components&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://io.google/2024/explore/f6a77c6d-e1fe-434b-92ca-879f8d153672/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Automate browser testing with tools and best practices from Chrome&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://io.google/2024/explore/8ae18b72-028e-4722-9a05-4a480048e629/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Multi-page application View Transitions are here&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;剛好可以順便來嘗試最近剛訂閱的 &lt;a href=&quot;https://openai.com/index/chatgpt-plus/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;ChatGPT Plus&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;這篇先介紹 Real-time server components，其它之後有機會再陸續更新上來。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/live/T8TZQ6k4SLE?t=24068&amp;amp;si=ThfeHok-WKdlzt3B&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;React Conf 2024 Day 1&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;背景介紹&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#背景介紹&quot;&gt;背景介紹&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;講者 &lt;a href=&quot;https://x.com/threepointone&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Sunil Pai&lt;/a&gt; 曾在 React 核心團隊工作，後來創辦了 &lt;a href=&quot;https://www.partykit.io&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;PartyKit&lt;/a&gt; 這間公司，專注於構建即時多人應用程式。&lt;/p&gt;
&lt;p&gt;最近 PartyKit 被 Cloudflare 收購，本人重返大公司工作。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;842&quot; height=&quot;380&quot; src=&quot;https://blog.amowu.com/_astro/image1.ByiSYzBO_K8UyA.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image1.ByiSYzBO_Zj7re1.webp 640w, https://blog.amowu.com/_astro/image1.ByiSYzBO_2qNWaw.webp 750w, https://blog.amowu.com/_astro/image1.ByiSYzBO_Z1NSSnG.webp 828w, https://blog.amowu.com/_astro/image1.ByiSYzBO_K8UyA.webp 842w&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;無狀態伺服器架構的局限&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#無狀態伺服器架構的局限&quot;&gt;無狀態伺服器架構的局限&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;無伺服器模型（Serverless）雖然有許多優點，但存在狀態管理問題。每次請求都會重置狀態，導致需要依賴外部資料庫或 key-value storage 來管理狀態。&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; counter &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;onRequest&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;req&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Request&lt;/span&gt;&lt;span&gt;) ‹&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Response&lt;/span&gt;&lt;span&gt;(counter&lt;/span&gt;&lt;span&gt;++&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;（每次請求回應的 &lt;code&gt;counter&lt;/code&gt; 都會是 0）&lt;/p&gt;
&lt;h2 id=&quot;durable-objects的解決方案&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#durable-objects的解決方案&quot;&gt;Durable Objects 的解決方案&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Cloudflare 的 &lt;a href=&quot;https://developers.cloudflare.com/durable-objects/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Durable Objects&lt;/a&gt;（持久化物件？）提供了狀態化服務（stateful services）的新方法。通過給定的 ID，可以建立一個保持活躍的 process，這個 process 可以處理請求並保持狀態。&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;// for every /chat/:id...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Server&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;// store messages in memory&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;messages &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; [];&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;async &lt;/span&gt;&lt;span&gt;onRequest&lt;/span&gt;&lt;span&gt;(req: &lt;/span&gt;&lt;span&gt;Request&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (req.method &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;POST&apos;&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.messages.&lt;/span&gt;&lt;span&gt;push&lt;/span&gt;&lt;span&gt;(await req.&lt;/span&gt;&lt;span&gt;text&lt;/span&gt;&lt;span&gt;());&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; Response.&lt;/span&gt;&lt;span&gt;json&lt;/span&gt;&lt;span&gt;({ status: &lt;/span&gt;&lt;span&gt;&apos;ok&apos;&lt;/span&gt;&lt;span&gt; });&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (req.method &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;GET&apos;&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; Response.&lt;/span&gt;&lt;span&gt;json&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.messages);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;這個模型使得構建像聊天室這樣的即時應用變得簡單，開發者可以使用類似於 JavaScript 的變數來維持狀態。&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Server&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;websockets&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;WebSocket&lt;/span&gt;&lt;span&gt;[] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; []; &lt;/span&gt;&lt;span&gt;// all websockets in this room&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;messages&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;[] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; [];&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;onConnect&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;ws&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;WebSocket&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.websockets.&lt;/span&gt;&lt;span&gt;push&lt;/span&gt;&lt;span&gt;(ws);   &lt;/span&gt;&lt;span&gt;// add websocket to array&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ws.&lt;/span&gt;&lt;span&gt;send&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;JSON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;stringify&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;type: &lt;/span&gt;&lt;span&gt;&apos;message&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;data: &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.messages,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;onMessage&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;ws&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;WebSocket&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;message&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.messages.&lt;/span&gt;&lt;span&gt;push&lt;/span&gt;&lt;span&gt;(message);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.websockets.&lt;/span&gt;&lt;span&gt;forEach&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;w&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;w.&lt;/span&gt;&lt;span&gt;send&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;JSON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;stringify&lt;/span&gt;&lt;span&gt;({ &lt;/span&gt;&lt;span&gt;// &amp;lt;----- broadcast all incoming messages to all connected clients&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;type: &lt;/span&gt;&lt;span&gt;&apos;message&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;data: message,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2 id=&quot;react-server-components-的挑戰&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#react-server-components-的挑戰&quot;&gt;React Server Components 的挑戰&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;目前在 &lt;a href=&quot;https://react.dev/reference/rsc/server-components&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;React Server Components&lt;/a&gt; 中不能使用 &lt;code&gt;useState&lt;/code&gt; 和 &lt;code&gt;Context&lt;/code&gt;。這對於需要在伺服器端維持狀態的應用來說是一個限制。&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;// server&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;App&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;counter&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;setCounter&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;useState&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&amp;gt;Counter&amp;lt;/&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;counter&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;onClick&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;          &lt;/span&gt;&lt;span&gt;&apos;use server&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;          &lt;/span&gt;&lt;span&gt;setCounter&lt;/span&gt;&lt;span&gt;(counter &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Increment&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;// browser&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;;(&lt;/span&gt;&lt;span&gt;&apos;use client&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt;({ &lt;/span&gt;&lt;span&gt;onClick&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;children&lt;/span&gt;&lt;span&gt; }) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &amp;lt;&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;onClick&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;&lt;span&gt;onClick&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;children&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2 id=&quot;react-party&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#react-party&quot;&gt;React Party&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;講者介紹了一個名為 React Party 的新 library（coming soon），旨在解決上述挑戰。這個 library 允許在伺服器端使用狀態和上下文，同時暴露類似於 React 的 hooks。&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;useState,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;useReducer,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;createContext,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;useContext,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ReactServer,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;} &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;react-party&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;MyServer&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ReactServer&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;render&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &amp;lt;&lt;/span&gt;&lt;span&gt;App&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;這個 library 適合構建即時應用，如聊天室、協作工具、遊戲伺服器等，並且可以無縫整合到現有的框架如 Next.js 和 Remix 中。&lt;/p&gt;
&lt;h2 id=&quot;react-party的優點&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#react-party的優點&quot;&gt;React Party 的優點&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;在所有的 React components 中使用 state 和 context，無論是 server 還是 client&lt;/li&gt;
&lt;li&gt;通過 WebSockets 推送 React Server Components&lt;/li&gt;
&lt;li&gt;部署在 Global Edge Network（全球&lt;a href=&quot;https://www.ruanyifeng.com/blog/2024/03/edge-platform.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;邊緣&lt;/a&gt;網路）以提供更低延遲的體驗&lt;/li&gt;
&lt;li&gt;…&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;總結&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#總結&quot;&gt;總結&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;隨著前端和後端技術的不斷進步，即時應用的開發變得越來越簡單和高效。Sunil Pai 和他的團隊通過引入 Cloudflare Durable Objects 和即將推出的 React Party，展示了如何在伺服器端高效管理狀態並進行即時更新。&lt;/p&gt;
&lt;p&gt;Durable Objects 為無狀態伺服器架構帶來了新的希望，讓開發者能夠更輕鬆地構建高效能、低延遲的應用。同時，React Party 也將改變 React Server Components 的使用方式，使得開發者可以在伺服器端利用狀態和上下文來創建體驗更佳的應用。&lt;/p&gt;</content:encoded><category>Tech</category></item><item><title>使用 Heptabase 學習複雜的程式架構</title><link>https://blog.amowu.com/posts/2024-05-18-heptabase/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2024-05-18-heptabase/</guid><description>這是我在公司的前端聚會上分享的主題，介紹 Heptabase 這個筆記工具，以及我如何使用它來完成「Hahow for Business 企業上傳串 JW Player」的研究過程。大綱如下：</description><pubDate>Sat, 18 May 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;這是我在公司的前端聚會上分享的主題，介紹 Heptabase 這個筆記工具，以及我如何使用它來完成「Hahow for Business 企業上傳串 JW Player」的研究過程。大綱如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;前言&lt;/li&gt;
&lt;li&gt;Heptabase 簡介&lt;/li&gt;
&lt;li&gt;如何使用 Heptabase&lt;/li&gt;
&lt;li&gt;我如何使用 Heptabase&lt;/li&gt;
&lt;li&gt;總結&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;前言&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#前言&quot;&gt;前言&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;這篇文章要介紹的是 &lt;a href=&quot;https://heptabase.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Heptabase&lt;/a&gt; 這個筆記工具，以下我將簡稱它為 Hepta。&lt;/p&gt;
&lt;p&gt;雖然這個主題和前端工程師可能沒太大關係，但是大家在學習的過程中，或多或少需要做一些筆記。所以我想分享一下我最近使用的這款筆記工具。&lt;/p&gt;
&lt;p&gt;最近在負責將 &lt;a href=&quot;https://business.hahow.in/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Hahow for Business&lt;/a&gt; 的 VOD 從 AWS 換到 &lt;a href=&quot;https://jwplayer.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;JW Player&lt;/a&gt;。Hahow for Business 的影片分為「訂閱制」和「企業上傳」兩種。前者是由 Hahow 提供選購的課程；後者是由企業內部自行上傳的課程。雖然兩者都是 VOD，但它們背後使用的套件和架構是完全不同的。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;2092&quot; height=&quot;1178&quot; src=&quot;https://blog.amowu.com/_astro/image1.D8rRiEU3_ZmVanL.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image1.D8rRiEU3_Z1cuxRd.webp 640w, https://blog.amowu.com/_astro/image1.D8rRiEU3_1v4PDr.webp 750w, https://blog.amowu.com/_astro/image1.D8rRiEU3_16k3MH.webp 828w, https://blog.amowu.com/_astro/image1.D8rRiEU3_Z1PwmC0.webp 1080w, https://blog.amowu.com/_astro/image1.D8rRiEU3_2fE9fx.webp 1280w, https://blog.amowu.com/_astro/image1.D8rRiEU3_1bFtPa.webp 1668w, https://blog.amowu.com/_astro/image1.D8rRiEU3_Z1PXqzN.webp 2048w, https://blog.amowu.com/_astro/image1.D8rRiEU3_ZmVanL.webp 2092w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;訂閱制比較簡單（背後是 Ruby on Rails 內建的 &lt;a href=&quot;https://edgeguides.rubyonrails.org/active_storage_overview.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Active Storage&lt;/a&gt;），目前已經串完 JW Player 並上線運行一陣子了。最近開始處理「企業上傳」的部分，因為「企業上傳」的架構比「訂閱制」複雜得多（背後是第三方的套件 &lt;a href=&quot;https://github.com/carrierwaveuploader/carrierwave&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;CarrierWave&lt;/a&gt;），加上我沒有這部分的背景知識，所以很多地方我完全不知道它是如何運作的。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;2090&quot; height=&quot;1176&quot; src=&quot;https://blog.amowu.com/_astro/image2.BWmpplvH_Fc2bI.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image2.BWmpplvH_vovs7.webp 640w, https://blog.amowu.com/_astro/image2.BWmpplvH_Z1QddPa.webp 750w, https://blog.amowu.com/_astro/image2.BWmpplvH_Z2fX0FT.webp 828w, https://blog.amowu.com/_astro/image2.BWmpplvH_BsnvO.webp 1080w, https://blog.amowu.com/_astro/image2.BWmpplvH_Z14oVv3.webp 1280w, https://blog.amowu.com/_astro/image2.BWmpplvH_Z1qvSOW.webp 1668w, https://blog.amowu.com/_astro/image2.BWmpplvH_Z5Pnws.webp 2048w, https://blog.amowu.com/_astro/image2.BWmpplvH_Fc2bI.webp 2090w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;一開始預到的問題是，我會一個一個檔案打開來研究程式碼，但是時間久了，我的 Chrome 和 VS Code 就會累積一堆 tabs。這樣不只是尋找檔案會很麻煩，可能休個假回來，就會想不起來之前研究到哪裡，變成又要從頭開始。所以我認為我需要一個工具來幫助我學習。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;2092&quot; height=&quot;1180&quot; src=&quot;https://blog.amowu.com/_astro/image3.CfgOo1qv_Z84TIi.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image3.CfgOo1qv_gCLGf.webp 640w, https://blog.amowu.com/_astro/image3.CfgOo1qv_Z25XWB2.webp 750w, https://blog.amowu.com/_astro/image3.CfgOo1qv_Z2uIJrL.webp 828w, https://blog.amowu.com/_astro/image3.CfgOo1qv_Z1N5dtb.webp 1080w, https://blog.amowu.com/_astro/image3.CfgOo1qv_2i6iom.webp 1280w, https://blog.amowu.com/_astro/image3.CfgOo1qv_1qwJuD.webp 1668w, https://blog.amowu.com/_astro/image3.CfgOo1qv_Z16Ezlv.webp 2048w, https://blog.amowu.com/_astro/image3.CfgOo1qv_Z84TIi.webp 2092w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;我是一個很喜歡折騰筆記工具的人，無論是最早的 Evernote 和 OneNote，或是後起之秀 Bear 和 Notion，以及近幾年因為**雙向連結（Bi-Directional Links）**火起來的 Rome Research 和 Obsidian，我都用過一段時間，可能買過的筆記軟體比我寫過的筆記還多（笑）。我也經常私訊我們產品負責人，希望 Hahow 可以做個 PKM 功能，不過每次都被笑笑打發掉（笑）。&lt;/p&gt;
&lt;p&gt;說到雙向鏈接，想順便推薦一下《How to Take Smart Notes》這本書，近幾年在筆記圈相當火，它在介紹一種叫做「卡片盒筆記法」的方法論。這是一位德國學者叫做魯曼（Luhmann）發明的，他靠著這套方法在很多領域貢獻了許多論文。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;2088&quot; height=&quot;1178&quot; src=&quot;https://blog.amowu.com/_astro/image4.CtMxcRHi_ZpYnhx.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image4.CtMxcRHi_1KW75g.webp 640w, https://blog.amowu.com/_astro/image4.CtMxcRHi_ZAECd1.webp 750w, https://blog.amowu.com/_astro/image4.CtMxcRHi_Z10pp3K.webp 828w, https://blog.amowu.com/_astro/image4.CtMxcRHi_Z1a39D5.webp 1080w, https://blog.amowu.com/_astro/image4.CtMxcRHi_Z293Lzt.webp 1280w, https://blog.amowu.com/_astro/image4.CtMxcRHi_24yNkJ.webp 1668w, https://blog.amowu.com/_astro/image4.CtMxcRHi_ZsCvvp.webp 2048w, https://blog.amowu.com/_astro/image4.CtMxcRHi_ZpYnhx.webp 2088w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;台灣出版社也有推出中譯本，有興趣的人可以買來讀一讀。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;仔細看推薦人竟然有 Hahow 的創辦人阿諾，以及接下來要介紹的 Heptabase 共同創辦人詹雨安&quot; width=&quot;2090&quot; height=&quot;1174&quot; src=&quot;https://blog.amowu.com/_astro/image5.CwybaljF_HuJ76.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image5.CwybaljF_2wjrec.webp 640w, https://blog.amowu.com/_astro/image5.CwybaljF_Z1kJacQ.webp 750w, https://blog.amowu.com/_astro/image5.CwybaljF_Z1JtW3A.webp 828w, https://blog.amowu.com/_astro/image5.CwybaljF_1lCMwF.webp 1080w, https://blog.amowu.com/_astro/image5.CwybaljF_ad44C.webp 1280w, https://blog.amowu.com/_astro/image5.CwybaljF_Z1odbTz.webp 1668w, https://blog.amowu.com/_astro/image5.CwybaljF_Z3wFB5.webp 2048w, https://blog.amowu.com/_astro/image5.CwybaljF_HuJ76.webp 2090w&quot; /&gt;
&lt;em&gt;仔細看推薦人竟然有 Hahow 的創辦人阿諾，以及接下來要介紹的 Heptabase 共同創辦人詹雨安&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;如果不想看書的話，張永錫老師在 Hahow 有開一門《&lt;a href=&quot;https://hahow.in/courses/623156dc32edfc0007190299?utm_source=share&amp;amp;utm_medium=link&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;卡片筆記術：高效內容產出的秘密&lt;/a&gt;》的課程，有興趣的朋友可以去支持一下。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;2088&quot; height=&quot;1172&quot; src=&quot;https://blog.amowu.com/_astro/image6.DHCL4YoT_h18XE.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image6.DHCL4YoT_5LJbx.webp 640w, https://blog.amowu.com/_astro/image6.DHCL4YoT_Z1iLz0a.webp 750w, https://blog.amowu.com/_astro/image6.DHCL4YoT_Z1HwlPT.webp 828w, https://blog.amowu.com/_astro/image6.DHCL4YoT_1DxtSx.webp 1080w, https://blog.amowu.com/_astro/image6.DHCL4YoT_s7Kqu.webp 1280w, https://blog.amowu.com/_astro/image6.DHCL4YoT_Z16iuxH.webp 1668w, https://blog.amowu.com/_astro/image6.DHCL4YoT_VeHPg.webp 2048w, https://blog.amowu.com/_astro/image6.DHCL4YoT_h18XE.webp 2088w&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;heptabase-簡介&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#heptabase-簡介&quot;&gt;Heptabase 簡介&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;雖然 Hepta 也是基於「卡片盒筆記法」構建的，但我不會太著墨這塊，這篇主要還是分享一下 Hepta 這個工具的使用。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;1043&quot; height=&quot;587&quot; src=&quot;https://blog.amowu.com/_astro/image7.C0asBa1L_28PK9K.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image7.C0asBa1L_Z2oFvl6.webp 640w, https://blog.amowu.com/_astro/image7.C0asBa1L_Z1Ajvcx.webp 750w, https://blog.amowu.com/_astro/image7.C0asBa1L_8rWQb.webp 828w, https://blog.amowu.com/_astro/image7.C0asBa1L_28PK9K.webp 1043w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Hepta 是由幾位台灣人共同創辦的，上圖中間這一位就是前面提到的詹雨安（Alan），他認為現有的筆記工具都有各自的問題，不符合他的需求，所以就自己創業開發了 Heptabase。很厲害的是，他們獲得了矽谷知名的創業加速器 Y Combinator（YC）的投資，目前成立了約兩年左右。&lt;/p&gt;
&lt;p&gt;Alan（&lt;a href=&quot;https://medium.com/u/7f54ec92397a&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;詹雨安 Alan Chan&lt;/a&gt;）在 &lt;a href=&quot;https://alanchan1209.medium.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Medium&lt;/a&gt; 上也寫了很多跟創業還有筆記相關的文章，而且都有提供中英文版本，推薦一讀。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;1230&quot; height=&quot;771&quot; src=&quot;https://blog.amowu.com/_astro/image8.D5l_IhGD_2iJFfl.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image8.D5l_IhGD_Z2rwyHC.webp 640w, https://blog.amowu.com/_astro/image8.D5l_IhGD_1wsJhh.webp 750w, https://blog.amowu.com/_astro/image8.D5l_IhGD_1vVflD.webp 828w, https://blog.amowu.com/_astro/image8.D5l_IhGD_Z2eQ7Bw.webp 1080w, https://blog.amowu.com/_astro/image8.D5l_IhGD_2iJFfl.webp 1230w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Hepta 在 Landing Page 上的標語，寫的是「理解複雜的主題，Heptabase 幫你直觀地理解你的學習、研究和專案」，Hepta 並沒有自稱是一個筆記或知識管理工具，而是更像一個思考的工具。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Hepta 注重的是過程，而不是結果。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;人類大腦更容易理解以視覺呈現的方式，Hepta 只有白板和卡片這兩個核心元素，你可以用任何習慣的呈現方式來組織你的卡片筆記。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;1230&quot; height=&quot;772&quot; src=&quot;https://blog.amowu.com/_astro/image9.o7seanM0_Z1QouMa.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image9.o7seanM0_cmYh8.webp 640w, https://blog.amowu.com/_astro/image9.o7seanM0_ZSNPwT.webp 750w, https://blog.amowu.com/_astro/image9.o7seanM0_UCb3a.webp 828w, https://blog.amowu.com/_astro/image9.o7seanM0_Z1jN9P6.webp 1080w, https://blog.amowu.com/_astro/image9.o7seanM0_Z1QouMa.webp 1230w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Hepta 可以把任何筆記段落直接拖拉到白板變成一張筆記卡片，而且原本的筆記段落也會變成一個雙向連結。這樣一來，除了可以把知識粒度變小之外，還可以在任何地方重複使用這張筆記卡片。&lt;/p&gt;
&lt;h2 id=&quot;如何使用-heptabase&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#如何使用-heptabase&quot;&gt;如何使用 Heptabase&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;769&quot; height=&quot;222&quot; src=&quot;https://blog.amowu.com/_astro/image10.DCgfsB7b_ZLV0Xs.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image10.DCgfsB7b_Z1lrpzW.webp 640w, https://blog.amowu.com/_astro/image10.DCgfsB7b_2M6Ve.webp 750w, https://blog.amowu.com/_astro/image10.DCgfsB7b_ZLV0Xs.webp 769w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;上圖是知名的 IPO Model，IPO 就是 Input、Process 和 Output，很多領域都可以套用這個模型，知識管理也可以套用這個模型。我們做筆記的過程就是吸收各種 Input，然後在筆記工具上進行 Process，最後產出各種 Output。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;767&quot; height=&quot;471&quot; src=&quot;https://blog.amowu.com/_astro/image11.BqP3cY_W_Z7ugF8.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image11.BqP3cY_W_XHrRX.webp 640w, https://blog.amowu.com/_astro/image11.BqP3cY_W_Z1EsK64.webp 750w, https://blog.amowu.com/_astro/image11.BqP3cY_W_Z7ugF8.webp 767w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;以我自己來講，我的 Input 通常來源是 Google 上搜尋到的文章、我訂閱的 RSS、Newsletter，或是在 Twitter 或 YouTube 上看到的內容，其它還有像是一些電子書和 PDF 的論文，還會最近一直使用的 ChatGPT 回答的結果之類的。我的 Output 可能就是希望它會變成我工作上的技術文件、分享會上用到的簡報，或是一篇部落格文章。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;760&quot; height=&quot;470&quot; src=&quot;https://blog.amowu.com/_astro/image12.CTTMYnyu_Z1BM4jP.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image12.CTTMYnyu_ZDjTXu.webp 640w, https://blog.amowu.com/_astro/image12.CTTMYnyu_1MG0Qp.webp 750w, https://blog.amowu.com/_astro/image12.CTTMYnyu_Z1BM4jP.webp 760w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;各種筆記軟體在做的就是中間這一塊，每一家筆記軟體的設計哲學可能都不一樣，以 Alan 在&lt;a href=&quot;https://medium.com/heptabase/the-best-way-to-acquire-knowledge-from-readings-abf9357814d1&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;文章&lt;/a&gt;中建議的步驟為例：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;第一步會先將閱讀的過程中看到的東西，所有重要的段落&lt;strong&gt;記錄&lt;/strong&gt;下來&lt;/li&gt;
&lt;li&gt;第二步是把這些記錄下來的段落&lt;strong&gt;拆解&lt;/strong&gt;成更小的概念&lt;/li&gt;
&lt;li&gt;第三步就是畫出這些概念之間的&lt;strong&gt;關聯性&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;然後最後一步就是把這些相似的概念&lt;strong&gt;群組&lt;/strong&gt;起來&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;第一步將閱讀的過程中看到的所有重要段落記錄下來&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#第一步將閱讀的過程中看到的所有重要段落記錄下來&quot;&gt;第一步：將閱讀的過程中看到的所有重要段落記錄下來&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;1107&quot; height=&quot;611&quot; src=&quot;https://blog.amowu.com/_astro/image13.CMYLhhaS_gIIOb.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image13.CMYLhhaS_ZGHJ05.webp 640w, https://blog.amowu.com/_astro/image13.CMYLhhaS_vpMmN.webp 750w, https://blog.amowu.com/_astro/image13.CMYLhhaS_Z1nKQBH.webp 828w, https://blog.amowu.com/_astro/image13.CMYLhhaS_ktiwq.webp 1080w, https://blog.amowu.com/_astro/image13.CMYLhhaS_gIIOb.webp 1107w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;第一步中，Alan 建議把在閱讀過程中看到的重要段落記錄下來。這個過程的實作方式可能會根據你使用的工具而有所不同，不管你採用哪種方式做筆記，只要確保最終會產出一張「重點卡片」，裡頭包含你在學習過程中所紀錄的重要段落即可。&lt;/p&gt;
&lt;h2 id=&quot;第二步將這些紀錄下來的重要段落拆解成顆粒度更小的概念&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#第二步將這些紀錄下來的重要段落拆解成顆粒度更小的概念&quot;&gt;第二步：將這些紀錄下來的重要段落拆解成顆粒度更小的概念&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;1397&quot; height=&quot;949&quot; src=&quot;https://blog.amowu.com/_astro/image14.DKh7nC1F_13lr0h.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image14.DKh7nC1F_2dQyhx.webp 640w, https://blog.amowu.com/_astro/image14.DKh7nC1F_Z1wcRrE.webp 750w, https://blog.amowu.com/_astro/image14.DKh7nC1F_Z1Ktof7.webp 828w, https://blog.amowu.com/_astro/image14.DKh7nC1F_5FMnt.webp 1080w, https://blog.amowu.com/_astro/image14.DKh7nC1F_ZXWd5q.webp 1280w, https://blog.amowu.com/_astro/image14.DKh7nC1F_13lr0h.webp 1397w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;接著你可以建立一個白板，並透過 Hepta 右上角的 Import Panel 將卡片從 Card Library 拉進這個白板裡頭。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;1398&quot; height=&quot;949&quot; src=&quot;https://blog.amowu.com/_astro/image15.DwcLA0Op_Z1rIChD.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image15.DwcLA0Op_27wQin.webp 640w, https://blog.amowu.com/_astro/image15.DwcLA0Op_Z85Hi3.webp 750w, https://blog.amowu.com/_astro/image15.DwcLA0Op_Zmme5v.webp 828w, https://blog.amowu.com/_astro/image15.DwcLA0Op_1WR5Ck.webp 1080w, https://blog.amowu.com/_astro/image15.DwcLA0Op_1nFFIf.webp 1280w, https://blog.amowu.com/_astro/image15.DwcLA0Op_Z1rIChD.webp 1398w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;當卡片拉進白板之後，Alan 會把它點開到右側欄，讓他更方便地瀏覽裡面的內容。Alan 通常會先快速掃過一遍這張卡片的所有內容，然後將裡面所有重要的概念識別出來。當他決定要把一個概念萃取出來時，&lt;strong&gt;他會把與這個概念有關的區塊選取起來一口氣拖曳到白板上變成一張新的「概念卡片」。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;1398&quot; height=&quot;952&quot; src=&quot;https://blog.amowu.com/_astro/image16.ByZKu8tD_Z27N2Es.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image16.ByZKu8tD_LKtjt.webp 640w, https://blog.amowu.com/_astro/image16.ByZKu8tD_ZuNEan.webp 750w, https://blog.amowu.com/_astro/image16.ByZKu8tD_Z1H8B4p.webp 828w, https://blog.amowu.com/_astro/image16.ByZKu8tD_15nyIQ.webp 1080w, https://blog.amowu.com/_astro/image16.ByZKu8tD_HBglq.webp 1280w, https://blog.amowu.com/_astro/image16.ByZKu8tD_Z27N2Es.webp 1398w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;只有建立卡片還不夠，**他還會將這張卡片的核心概念用一句話總結，並以這句話當做是卡片標題，**以確保他一眼就能知道這張卡片在講什麼。接下來，Alan 會將這張「概念卡片」裡頭的內容重新組織，讓它的結構更符合自己的直覺；他也會看一下原本的「重點卡片」裡頭還有沒有其它與這張「概念卡片」相關的區塊，如果有的話，就把它們也拖進這張「概念卡片」裡頭。&lt;/p&gt;
&lt;h2 id=&quot;第三步畫出這些概念之間的關聯性&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#第三步畫出這些概念之間的關聯性&quot;&gt;第三步：畫出這些概念之間的關聯性&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;1399&quot; height=&quot;953&quot; src=&quot;https://blog.amowu.com/_astro/image17.SSeks44-_VEFXd.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image17.SSeks44-_Z1qEzJF.webp 640w, https://blog.amowu.com/_astro/image17.SSeks44-_2lWpzp.webp 750w, https://blog.amowu.com/_astro/image17.SSeks44-_19CsFn.webp 828w, https://blog.amowu.com/_astro/image17.SSeks44-_ZUoTD6.webp 1080w, https://blog.amowu.com/_astro/image17.SSeks44-_Z202U70.webp 1280w, https://blog.amowu.com/_astro/image17.SSeks44-_VEFXd.webp 1399w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;當 Alan 從重點卡片中萃取出愈來愈多「概念卡片」之後，&lt;strong&gt;他就會開始在這些「概念卡片」之間畫箭頭，或是把內容相近的卡片放在一起&lt;/strong&gt;。如果他發現有兩張「概念卡片」的內容在討論相同的想法，他會將它們合併；如果他發現一張「概念卡片」裡頭包含的資訊量太大，他會把它拆解成多張更小的「概念卡片」來保持卡片的粒度。&lt;/p&gt;
&lt;h2 id=&quot;第四步將相似的概念群組起來&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#第四步將相似的概念群組起來&quot;&gt;第四步：將相似的概念群組起來&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;1397&quot; height=&quot;951&quot; src=&quot;https://blog.amowu.com/_astro/image18.Bk2dzx2S_UrfoH.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image18.Bk2dzx2S_J93Ag.webp 640w, https://blog.amowu.com/_astro/image18.Bk2dzx2S_Zxq4SA.webp 750w, https://blog.amowu.com/_astro/image18.Bk2dzx2S_Z1JK1MC.webp 828w, https://blog.amowu.com/_astro/image18.Bk2dzx2S_ZeCuIK.webp 1080w, https://blog.amowu.com/_astro/image18.Bk2dzx2S_Z1jgvcE.webp 1280w, https://blog.amowu.com/_astro/image18.Bk2dzx2S_UrfoH.webp 1397w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;將「重點卡片」的內容都轉成「概念卡片」之後，Alan 會關掉右側欄，開始專心把這些「概念卡片」之間的關聯性和群組關係建立起來。當他發現有多張「概念卡片」都跟某個子題有關時，他會將它們用 Section 包起來，並給這個 Section 一個名字。在為 Section 取名時會跟為「概念卡片」下標題時一樣謹慎，因為這些名稱將會是未來回顧這個白板時第一眼看到的東西。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;1398&quot; height=&quot;952&quot; src=&quot;https://blog.amowu.com/_astro/image19.COss3Ee3_Zb6w4t.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image19.COss3Ee3_vzfYH.webp 640w, https://blog.amowu.com/_astro/image19.COss3Ee3_ZKYRu9.webp 750w, https://blog.amowu.com/_astro/image19.COss3Ee3_Z1XjOob.webp 828w, https://blog.amowu.com/_astro/image19.COss3Ee3_Z2373u6.webp 1080w, https://blog.amowu.com/_astro/image19.COss3Ee3_Z2pSlRw.webp 1280w, https://blog.amowu.com/_astro/image19.COss3Ee3_Zb6w4t.webp 1398w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;完成白板的排版之後，Alan 會把原本的「重點卡片」開在白板的右側欄，並將所有「概念卡片」和 Section 的連結重新貼回這張卡片上。&lt;/p&gt;
&lt;p&gt;當你產出最終的白板後，吸收知識的階段就結束了。在這個過程中，真正有價值的並不是你最終產出的這個白板，而是你在執行第二步到第四步的過程中&lt;strong&gt;建立知識架構、給每張概念卡片和 Section 下標題所投入的思考過程&lt;/strong&gt;。深度的理解和洞察往往源自於將知識分解、重組、用自己的話語描述的這個過程。&lt;strong&gt;只有在走過這個過程後，這些知識才會真正內化成自己的知識&lt;/strong&gt;。&lt;/p&gt;
&lt;h2 id=&quot;我如何使用-heptabase&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#我如何使用-heptabase&quot;&gt;我如何使用 Heptabase&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;2400&quot; height=&quot;1667&quot; src=&quot;https://blog.amowu.com/_astro/image20.C6_ByASW_Zv4JeG.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image20.C6_ByASW_ZnFAT2.webp 640w, https://blog.amowu.com/_astro/image20.C6_ByASW_ZWS8Yo.webp 750w, https://blog.amowu.com/_astro/image20.C6_ByASW_Z9rNRd.webp 828w, https://blog.amowu.com/_astro/image20.C6_ByASW_Z6Aihu.webp 1080w, https://blog.amowu.com/_astro/image20.C6_ByASW_ZqmV0l.webp 1280w, https://blog.amowu.com/_astro/image20.C6_ByASW_Z19WiAT.webp 1668w, https://blog.amowu.com/_astro/image20.C6_ByASW_Z1WVgSE.webp 2048w, https://blog.amowu.com/_astro/image20.C6_ByASW_Zv4JeG.webp 2400w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;回到學習「Hahow for Business 企業上傳串 JW Player」的部分，我首先在 Hepta 建立了一個名為「Hahow for Business 企業上傳架構」的白板。&lt;/p&gt;
&lt;p&gt;這裡我的使用方式跟 Alan 建議的方法不太一樣，因為我是在舊的架構上新增新功能，所以首先要釐清舊的架構，然後才能開始做新的功能。&lt;/p&gt;
&lt;p&gt;我會先根據 VOD 的三個步驟：&lt;strong&gt;上傳&lt;/strong&gt;、&lt;strong&gt;轉檔&lt;/strong&gt;和&lt;strong&gt;交付&lt;/strong&gt;，分別使用三種不同顏色來拆分卡片，這樣我可以比較容易一眼看出想尋找的卡片。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;1493&quot; height=&quot;1136&quot; src=&quot;https://blog.amowu.com/_astro/image21.eDP99oHl_ZP1jI8.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image21.eDP99oHl_11IUr3.webp 640w, https://blog.amowu.com/_astro/image21.eDP99oHl_tMhFJ.webp 750w, https://blog.amowu.com/_astro/image21.eDP99oHl_ZYwJn4.webp 828w, https://blog.amowu.com/_astro/image21.eDP99oHl_Z1KHIyw.webp 1080w, https://blog.amowu.com/_astro/image21.eDP99oHl_WT3nB.webp 1280w, https://blog.amowu.com/_astro/image21.eDP99oHl_ZP1jI8.webp 1493w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;接著，我可能會從使用者的角度來切入。如果使用者想要跑完整個 VOD 的流程，首先，他必須進行上傳操作；其次，他需要觀看串流。&lt;/p&gt;
&lt;p&gt;因此，使用者只會有這兩個進入點。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;1492&quot; height=&quot;1140&quot; src=&quot;https://blog.amowu.com/_astro/image22.DnA75_o0_Zw4YLs.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image22.DnA75_o0_1B82is.webp 640w, https://blog.amowu.com/_astro/image22.DnA75_o0_14box9.webp 750w, https://blog.amowu.com/_astro/image22.DnA75_o0_hI4yO.webp 828w, https://blog.amowu.com/_astro/image22.DnA75_o0_2oUD7n.webp 1080w, https://blog.amowu.com/_astro/image22.DnA75_o0_1ukAKz.webp 1280w, https://blog.amowu.com/_astro/image22.DnA75_o0_Zw4YLs.webp 1492w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;以上傳的過程為例，一開始會先接觸到前端一個名為 &lt;code&gt;VideoLessonFormApp&lt;/code&gt; 的檔案。我會將相關的卡片都「黏」在一起，代表它們屬於同一檔案。但是同一個檔案底下，可能會有負責不同商業邏輯的程式碼，所以這裡就會用到前面提的顏色功能，例如：黃色代表上傳，橘色代表轉檔，藍色則是預計接下來要在這個檔案新增的程式碼。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;1493&quot; height=&quot;1141&quot; src=&quot;https://blog.amowu.com/_astro/image23.2iFaIV9z_123ons.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image23.2iFaIV9z_26n94w.webp 640w, https://blog.amowu.com/_astro/image23.2iFaIV9z_1yqvjd.webp 750w, https://blog.amowu.com/_astro/image23.2iFaIV9z_LXbkS.webp 828w, https://blog.amowu.com/_astro/image23.2iFaIV9z_2w0DzB.webp 1080w, https://blog.amowu.com/_astro/image23.2iFaIV9z_1BpBdN.webp 1280w, https://blog.amowu.com/_astro/image23.2iFaIV9z_123ons.webp 1493w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;建立卡片的過程中，我只會貼上關鍵的程式碼，避免太繁雜。Hepta 可以支援程式碼區塊的格式，也支援語法高亮，對工程師來說非常方便。另外，Hepta 也支援一些像是數學符號如 LaTeX 格式，支援的格式種類相當多，包括影片和音訊檔案等，只要使用類似於 Notion 的操作，打一個「斜線」就能插入各種不同的元素。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;1492&quot; height=&quot;1140&quot; src=&quot;https://blog.amowu.com/_astro/image24.C8QxVnZf_ZL5Hvk.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image24.C8QxVnZf_Z20u7ko.webp 640w, https://blog.amowu.com/_astro/image24.C8QxVnZf_2wKnIe.webp 750w, https://blog.amowu.com/_astro/image24.C8QxVnZf_1Ki3JT.webp 828w, https://blog.amowu.com/_astro/image24.C8QxVnZf_29TUnv.webp 1080w, https://blog.amowu.com/_astro/image24.C8QxVnZf_1fjS1H.webp 1280w, https://blog.amowu.com/_astro/image24.C8QxVnZf_ZL5Hvk.webp 1492w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;在學習的過程中，我也會使用畫線和 Section 功能將它們組織起來。目前我的分組方式是分為「前端」和「後端」兩個部分，線條之間的「標註」也可以幫助我理解它們之間的關係。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;1491&quot; height=&quot;1140&quot; src=&quot;https://blog.amowu.com/_astro/image25.CvduIHob_1T2Wbi.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image25.CvduIHob_1FTDIl.webp 640w, https://blog.amowu.com/_astro/image25.CvduIHob_18X0X2.webp 750w, https://blog.amowu.com/_astro/image25.CvduIHob_muFYH.webp 828w, https://blog.amowu.com/_astro/image25.CvduIHob_1bTOWS.webp 1080w, https://blog.amowu.com/_astro/image25.CvduIHob_hjMB5.webp 1280w, https://blog.amowu.com/_astro/image25.CvduIHob_1T2Wbi.webp 1491w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;1493&quot; height=&quot;1141&quot; src=&quot;https://blog.amowu.com/_astro/image26.BPCKTygH_Z1Pwb9k.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image26.BPCKTygH_Z1ApXka.webp 640w, https://blog.amowu.com/_astro/image26.BPCKTygH_Z28mB5t.webp 750w, https://blog.amowu.com/_astro/image26.BPCKTygH_2amcK8.webp 828w, https://blog.amowu.com/_astro/image26.BPCKTygH_ZlyUWb.webp 1080w, https://blog.amowu.com/_astro/image26.BPCKTygH_Z1g9XiY.webp 1280w, https://blog.amowu.com/_astro/image26.BPCKTygH_Z1Pwb9k.webp 1493w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;理清了所有關係之後，我就會開始進入預計實作的部分。這時我已經理解架構背後的運作原理，如果我要處理「上傳」JW Player 的部分，我就會找到上傳的「前端」＆「後端」的相關卡片，然後在上面新增一張藍色的卡片，直接參考關鍵程式碼的部分，一步一步在這張藍色卡片先寫上 pseudo-code。最後實際要在 VS Code 上寫程式時，我只需要打開白板，專注看藍色的卡片，一一實作相關的 pseudo-code 就可以完成了，不再需要打開一堆分頁。&lt;/p&gt;
&lt;p&gt;最後，Hepta 也可以分享白板，不過因為這裡面是公司的程式碼，不方便分享上來，有興趣的朋友可以去試用官方分享的案例。&lt;/p&gt;
&lt;h2 id=&quot;one-morething&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#one-morething&quot;&gt;One more thing&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;790&quot; height=&quot;502&quot; src=&quot;https://blog.amowu.com/_astro/image27.CKvnLsns_1FL1Un.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image27.CKvnLsns_28inHv.webp 640w, https://blog.amowu.com/_astro/image27.CKvnLsns_Z1x2LDS.webp 750w, https://blog.amowu.com/_astro/image27.CKvnLsns_1FL1Un.webp 790w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;分享完 Hepta 之後，我想再分享一個額外的內容。剛剛在前面有提到，我的輸入來源很多，每個來源對應的閱讀工具可能都不一樣，比如說 Google 到的文章，我會丟到 Instapaper 來稍後閱；RSS 資訊，我會用 Feedly 來閱讀，但是最近發現了一個很方便的工具，叫做 Readwise Reader。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;1045&quot; height=&quot;586&quot; src=&quot;https://blog.amowu.com/_astro/image28.BO9M0AtL_Z1Tl1as.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image28.BO9M0AtL_ZfIlXx.webp 640w, https://blog.amowu.com/_astro/image28.BO9M0AtL_Z1EhFaf.webp 750w, https://blog.amowu.com/_astro/image28.BO9M0AtL_1vHNEb.webp 828w, https://blog.amowu.com/_astro/image28.BO9M0AtL_Z1Tl1as.webp 1045w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Readwise 原本只有提供文章的 Highlight 的服務，透過瀏覽器擴充外掛，可以在網站上 Highlight 文章重點並集中管理。最近他們推出了 Readwise Reader，可以整合多種來源，除了稍後閱讀之外，還支援 RSS 訂閱，上傳 ePub 電子書，還提供隨機信箱讓你訂閱 Newsletter！並且這些內容都可以在 Readwise Reader 上 Highlight。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;1752&quot; height=&quot;1107&quot; src=&quot;https://blog.amowu.com/_astro/image29.DijmD-wN_298fgV.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image29.DijmD-wN_Z2qsmSR.webp 640w, https://blog.amowu.com/_astro/image29.DijmD-wN_Z1pLfSI.webp 750w, https://blog.amowu.com/_astro/image29.DijmD-wN_J6wlp.webp 828w, https://blog.amowu.com/_astro/image29.DijmD-wN_1CM4KX.webp 1080w, https://blog.amowu.com/_astro/image29.DijmD-wN_Z2fUlWl.webp 1280w, https://blog.amowu.com/_astro/image29.DijmD-wN_Z1gRQrh.webp 1668w, https://blog.amowu.com/_astro/image29.DijmD-wN_298fgV.webp 1752w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;甚至 PDF 檔案除了文字可以 Highlight 之外，還可以 Snapshot 文件上的某個區塊，這對於筆記 AWS 架構圖這類 PDF 形式的文件來說非常方便。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;1752&quot; height=&quot;1107&quot; src=&quot;https://blog.amowu.com/_astro/image30.Dfp1KHbj_Z2867Nj.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image30.Dfp1KHbj_Z1fPhqp.webp 640w, https://blog.amowu.com/_astro/image30.Dfp1KHbj_Zf9aqg.webp 750w, https://blog.amowu.com/_astro/image30.Dfp1KHbj_1TIBNR.webp 828w, https://blog.amowu.com/_astro/image30.Dfp1KHbj_2qJPuE.webp 1080w, https://blog.amowu.com/_astro/image30.Dfp1KHbj_Z1rWAdE.webp 1280w, https://blog.amowu.com/_astro/image30.Dfp1KHbj_ZsU5HA.webp 1668w, https://blog.amowu.com/_astro/image30.Dfp1KHbj_Z2867Nj.webp 1752w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;其中我認為很厲害的是，Readwise Reader 還可以列出 YouTube 影片的逐字稿，並且也可以 Highlight 重點。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;回到 Hepta，最近也推出了一個 Highlight 功能，但不是重造輪子，而是與 Readwise 整合，只要你有設定 Readwise 的 access token，就可以將 Readwise 的 highlights 直接拉到白板上當卡片。這樣我就可以在白板上整理我所有看過的內容（Input），並添加箭頭和 Section 來整理思路，這對於寫作和輸出（Output）相當有幫助。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;800&quot; height=&quot;498&quot; src=&quot;https://blog.amowu.com/_astro/image31.CKMyyxcs_ZT8Cu4.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image31.CKMyyxcs_1vgfHq.webp 640w, https://blog.amowu.com/_astro/image31.CKMyyxcs_1n7PYX.webp 750w, https://blog.amowu.com/_astro/image31.CKMyyxcs_ZT8Cu4.webp 800w&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;總結&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#總結&quot;&gt;總結&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;總結一下 Hepta 的特點，相比目前其它筆記工具，Hepta 更加專注於學習的過程，不論是零散的想法還是學習的重點，搭配 Readwise 的 highlights，都能統一匯集到這個白板上，然後以自己習慣的方式組織思考，最後在進行寫作和輸出時，可以很方便地參考之前已經內化過的結構，達到快速產出的目的。&lt;/p&gt;
&lt;p&gt;最後幫忙工商一下，就是 Heptabase 現在還在 beta 階段，他們每天的迭代速度很快，最近已經打算要發佈 1.0 版了，如果在 1.0 發佈之前訂閱的話，以後都會是永久早鳥價。如果有興趣的話，也可以去 &lt;a href=&quot;https://www.producthunt.com/products/heptabase&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Product Hunt&lt;/a&gt; 幫他們 upvote 一下，支持土生土長的台灣新創團隊。&lt;/p&gt;</content:encoded><category>Tech</category></item><item><title>如何跑 TBD flow？</title><link>https://blog.amowu.com/posts/2022-07-27-tbd-flow/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2022-07-27-tbd-flow/</guid><description>TBD 是一種版本控制的分支模型（source-control branching model），為了避免傳統 Git workflow 帶來的合併地獄，TBD 規定開發人員只能在被稱作「Trunk」的單一 branch 中提交程式碼，並且限制建立其它會長期存在的…</description><pubDate>Wed, 27 Jul 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;TBD 是一種版本控制的分支模型（source-control branching model），為了避免傳統 Git workflow 帶來的合併地獄，TBD 規定開發人員只能在被稱作「Trunk」的單一 branch 中提交程式碼，並且限制建立其它會長期存在的 branches。&lt;/p&gt;
&lt;h2 id=&quot;前言&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#前言&quot;&gt;前言&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;大家的公司或團隊，都是怎麼跑 Git workflow 呢？在執行上有沒有遇到什麼問題呢？這篇文章想要來聊聊我們團隊遇到的問題，以及解決的方式。&lt;/p&gt;
&lt;h2 id=&quot;想解決的問題&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#想解決的問題&quot;&gt;想解決的問題&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;在說明遇到的問題之前，先來看下面這張圖，這是我們團隊目前正在跑的 Git workflow：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;440&quot; src=&quot;https://blog.amowu.com/_astro/image1.Da-hlnuH_1dLchI.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image1.Da-hlnuH_r1Etq.webp 640w, https://blog.amowu.com/_astro/image1.Da-hlnuH_1dLchI.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;一個 sprint 是 15 天（三週）&lt;/li&gt;
&lt;li&gt;有三支需要 maintain 的 branches：&lt;code&gt;develop&lt;/code&gt;、&lt;code&gt;alpha&lt;/code&gt; 和 &lt;code&gt;master&lt;/code&gt;，分別對應上線的 server 環境 &lt;code&gt;staging&lt;/code&gt;、&lt;code&gt;QA&lt;/code&gt; 和 &lt;code&gt;production&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Day 1～Day 10 為&lt;strong&gt;開發期&lt;/strong&gt;，全部的 feature 都是從 &lt;code&gt;develop&lt;/code&gt; 分支、對 &lt;code&gt;develop&lt;/code&gt; 送 PR，code review 沒問題之後 merge 進 &lt;code&gt;develop&lt;/code&gt;，接著 CI 就會自動 deploy 到 &lt;code&gt;staging&lt;/code&gt; server&lt;/li&gt;
&lt;li&gt;Day 10 開始進入&lt;strong&gt;測試期&lt;/strong&gt;，我們的 GitHub 會自動開一個 merge &lt;code&gt;develop&lt;/code&gt; to &lt;code&gt;alpha&lt;/code&gt; 的 release PR，每個 sprint 安排一位 release manager 負責 review，沒問題的話，就 merge 進 &lt;code&gt;alpha&lt;/code&gt;，接著 CI 自動 deploy 到 &lt;code&gt;QA&lt;/code&gt; server，QA 開始測試&lt;/li&gt;
&lt;li&gt;測試期間，工程師仍然可以繼續對 &lt;code&gt;develop&lt;/code&gt; 送 feature 相關的 code，流程同開發期&lt;/li&gt;
&lt;li&gt;測試期間，如果 QA 測出問題，工程師修正的 code 仍然是從 &lt;code&gt;develop&lt;/code&gt; 分支、對 &lt;code&gt;develop&lt;/code&gt; 送 PR，接著 release manager 會 &lt;code&gt;git cherry-pick&lt;/code&gt; 相關 commits 到一個新的 release PR，沒問題之後，merge 並且 deploy 到 &lt;code&gt;QA&lt;/code&gt; server 讓 QA 驗證&lt;/li&gt;
&lt;li&gt;Day 15，GitHub 會自動開一個 merge &lt;code&gt;alpha&lt;/code&gt; to &lt;code&gt;master&lt;/code&gt; 的 release PR，如果 QA 覺得沒問題，release manager 就會 merge 進 &lt;code&gt;master&lt;/code&gt;，CI 自動 deploy 到 &lt;code&gt;production&lt;/code&gt; server，完成這次 sprint 的產品交付（例如上圖的 &lt;code&gt;1.1.0&lt;/code&gt; 和 &lt;code&gt;1.2.0&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;如果是緊急的問題，需要馬上修復（&lt;strong&gt;Hotfix&lt;/strong&gt;），無論是開發期或是測試期，工程師一樣還是從 &lt;code&gt;develop&lt;/code&gt; 分支、對 &lt;code&gt;develop&lt;/code&gt; 送 PR，merge 之後，release manager 一樣走上面流程 release 到 &lt;code&gt;QA&lt;/code&gt; server 給 QA 驗證，沒問題之後再開一個 merge 到 &lt;code&gt;master&lt;/code&gt; 的 release PR，&lt;code&gt;git cherry-pick&lt;/code&gt; 相關 commits，merge 之後 deploy 到 &lt;code&gt;production&lt;/code&gt; server，完成緊急修復（例如上圖的 &lt;code&gt;1.1.1&lt;/code&gt;）&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;問題-1conflicts&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#問題-1conflicts&quot;&gt;問題 1：Conflicts&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;在 Day 10 和 Day 15 這兩天，GitHub 開出的 release PR 常常會遇到 conflicts，而且這些 conflicts 通常因為時間久遠、commits 量大，導致 release manager 很難解 conflicts，容易發生解錯的風險。&lt;/p&gt;
&lt;h2 id=&quot;問題-2out-ofdate&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#問題-2out-ofdate&quot;&gt;問題 2：Out of date&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;為了確保 codebase 一致，我們的 GitHub &lt;code&gt;alpha&lt;/code&gt; 和 &lt;code&gt;master&lt;/code&gt; branch 有打開 &lt;code&gt;[Require branches to be up to date before merging](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/defining-the-mergeability-of-pull-requests/about-protected-branches)&lt;/code&gt; 的檢查。但是在測試期間，因為大家會頻繁的對 &lt;code&gt;alpha&lt;/code&gt; 送修正的 PR，導致 PR 很容易出現 &lt;code&gt;out-of-date&lt;/code&gt; 的情況，必須 &lt;code&gt;git rebase&lt;/code&gt; or &lt;code&gt;git merge&lt;/code&gt; 之後，再重頭跑一遍 CI。&lt;/p&gt;
&lt;h2 id=&quot;問題-3hotfix&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#問題-3hotfix&quot;&gt;問題 3：Hotfix&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;如果是 Hotfix 流程，一共需要開 3 次 PR（對象是 &lt;code&gt;develop&lt;/code&gt;、&lt;code&gt;alpha&lt;/code&gt; 和 &lt;code&gt;master&lt;/code&gt;）、跑 3 遍 CI、等 3 次 PR approve，但是它們的 change list 其實是一樣的。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;為了解決這三個問題，我參考了許多 TBD 相關文章，嘗試重新設計我們的 Git workflow。&lt;/p&gt;
&lt;h2 id=&quot;什麼是-tbd&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#什麼是-tbd&quot;&gt;什麼是 TBD？&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;TBD 是 &lt;strong&gt;Trunk Based Development&lt;/strong&gt; 的縮寫。是一種 Git 管理模型 or 策略，Google 和 Facebook 都有在使用。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://trunkbaseddevelopment.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Trunk Based Development&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;（中文 &lt;a href=&quot;https://tw.trunkbaseddevelopment.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://tw.trunkbaseddevelopment.com/&lt;/a&gt;）&lt;/p&gt;
&lt;p&gt;其它類似的模型還有：&lt;a href=&quot;https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Git flow&lt;/a&gt;、&lt;a href=&quot;https://docs.github.com/en/get-started/quickstart/github-flow&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;GitHub flow&lt;/a&gt; 以及 &lt;a href=&quot;https://docs.gitlab.com/ee/topics/gitlab_flow.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;GitLab flow&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id=&quot;改動的部分&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#改動的部分&quot;&gt;改動的部分&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;下圖是參考 TBD 調整之後的 workflow：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;412&quot; src=&quot;https://blog.amowu.com/_astro/image2.rUYxCsjo_Zbw7TM.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image2.rUYxCsjo_Zgleib.webp 640w, https://blog.amowu.com/_astro/image2.rUYxCsjo_Zbw7TM.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;將原先的三支 branches（&lt;code&gt;develop&lt;/code&gt;、&lt;code&gt;alpha&lt;/code&gt; 和 &lt;code&gt;master&lt;/code&gt;）&lt;strong&gt;縮減成一支 trunk branch（&lt;/strong&gt;&lt;code&gt;master&lt;/code&gt;&lt;strong&gt;）&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;無論是開發期、測試期或是 Hotfix，工程師只能從 &lt;code&gt;master&lt;/code&gt; 分支、對 &lt;code&gt;master&lt;/code&gt; 送 PR（這部分跟原本的差不多，只是從 &lt;code&gt;develop&lt;/code&gt; 變成 &lt;code&gt;master&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;取消 Day 10 和 Day 15 自動開 release PR 的機制，改成 Day 10 自動從 &lt;code&gt;master&lt;/code&gt; 開一支 &lt;strong&gt;release branch&lt;/strong&gt;（branch 的 prefix name 需為 &lt;code&gt;release/&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;Release branch 會觸發 CI 執行如下圖的 jobs：&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt=&quot;CircleCI 示意圖&quot; width=&quot;700&quot; height=&quot;24&quot; src=&quot;https://blog.amowu.com/_astro/image3.DdfeWV0Y_2dFc8A.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image3.DdfeWV0Y_2rSFVw.webp 640w, https://blog.amowu.com/_astro/image3.DdfeWV0Y_2dFc8A.webp 700w&quot; /&gt;
&lt;em&gt;CircleCI 示意圖&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;我們將原本 CI 的 &lt;code&gt;release alpha&lt;/code&gt; 和 &lt;code&gt;release prod&lt;/code&gt; 兩個 workflow 合二為一，release branch 會直接 deploy &lt;code&gt;QA&lt;/code&gt; server，但是因為後面接著 &lt;code&gt;approve-deploy-prod&lt;/code&gt;（CircleCI 提供的 &lt;a href=&quot;https://circleci.com/blog/manual-job-approval-and-scheduled-workflow-runs/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;approval&lt;/a&gt; 功能），所以不會馬上 deploy 到 &lt;code&gt;production&lt;/code&gt; server&lt;/li&gt;
&lt;li&gt;測試期間的修正 commits 會由 release manager &lt;code&gt;git cherry-pick&lt;/code&gt; 到 release branch，然後一樣觸發上述 CI，deploy 到 &lt;code&gt;QA&lt;/code&gt; server&lt;/li&gt;
&lt;li&gt;Day 15 如果 QA 沒問題，release manager 直接點擊 CI 的 &lt;code&gt;approve-deploy-prod&lt;/code&gt;，deploy 到 &lt;code&gt;production&lt;/code&gt; server（例如圖中的 &lt;code&gt;1.1.0&lt;/code&gt; 和 &lt;code&gt;1.2.0&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;如果要 hotfix，改成直接從 latest tag（例如 &lt;code&gt;1.1.0&lt;/code&gt;）開 release branch，之後的流程同上（例如圖中的 &lt;code&gt;1.1.1&lt;/code&gt;）&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;好處-1&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#好處-1&quot;&gt;好處 1&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;因為已經沒有 &lt;code&gt;develop&lt;/code&gt; 和 &lt;code&gt;alpha&lt;/code&gt; 需要維護了，所以就不會為了 sync branch 而產生不必要的 conflicts 了，可以省下大家解 conflicts 的時間。&lt;/p&gt;
&lt;h2 id=&quot;好處-2&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#好處-2&quot;&gt;好處 2&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;因為已經改用 release branch 取代 release PR，所以也不會有 PR out of date 的問題了，除了省下時間、也省下重跑 CI 的成本。&lt;/p&gt;
&lt;h2 id=&quot;好處-3&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#好處-3&quot;&gt;好處 3&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Hotfix 流程變成只需要開 1 次 PR（原本 3 次）、跑 2 遍 CI（原本 3 遍）、等 1 次 PR approve（原本 3 次），省時＋省力＋省錢。&lt;/p&gt;
&lt;h2 id=&quot;結語&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#結語&quot;&gt;結語&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;當然，新的流程勢必也存在新的問題，例如可能需要更仰賴 &lt;a href=&quot;https://trunkbaseddevelopment.com/feature-flags/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Feature flags&lt;/a&gt; 的機制等，但是經過與團隊的交流討論，我覺得大部分疑慮都是可以解決的，如果這個 flow 總體帶來的效益大過這些疑慮的話，我認為還是有一試的價值。&lt;/p&gt;
&lt;h2 id=&quot;參考資料&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#參考資料&quot;&gt;參考資料&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://trunkbaseddevelopment.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Trunk Based Development&lt;/a&gt;（&lt;a href=&quot;https://tw.trunkbaseddevelopment.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;中文&lt;/a&gt;）&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://nedwu13.blogspot.com/2014/01/tbd-what-is-trunk-based-development.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;筆記：TBD是三小? — -What is Trunk Based Development?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.atlassian.com/continuous-delivery/continuous-integration/trunk-based-development&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Trunk-based Development | Atlassian&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://cloud.google.com/architecture/devops/devops-tech-trunk-based-development&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;DevOps tech: Trunk-based development | Google Cloud&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.itread01.com/hkyfeii.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;版本分支管理標準- Trunk Based Development 主幹開發模型&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://juejin.cn/post/6977942781209608200&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;『前端进阶』 — — 代码管理方案之Trunk-based Flow&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://juejin.cn/post/6891583607262068750&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;git 开发流程 gitflow githubflow gitlabflow TrunkBase AoneFlow 以及持续集成&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://juejin.cn/post/6875874533228838925&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;字节研发设施下的 Git 工作流&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://juejin.cn/post/6844904203115036685&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Git 分支管理策略总结&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://juejin.cn/post/6875874533228838925&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;如何在微服务团队中高效使用 Git 管理代码？&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://juejin.cn/post/6844903464477130765&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Google 是如何开发 Web 框架的&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>Tech</category></item><item><title>為什麼會有拖延症？</title><link>https://blog.amowu.com/posts/2022-07-25-procrastiatio-and-parkinsons-law/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2022-07-25-procrastiatio-and-parkinsons-law/</guid><description>人類的自然規律：不是做多少事、就需要多少資源。而是有多少資源、人類就一定會把這個資源用到耗盡為止。</description><pubDate>Mon, 25 Jul 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;人類的自然規律：不是做多少事、就需要多少資源。而是有多少資源、人類就一定會把這個資源用到耗盡為止。&lt;/p&gt;
&lt;h2 id=&quot;前言&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#前言&quot;&gt;前言&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;最近團隊交付產品的時間，常常有不如預期的情況發生。&lt;/p&gt;
&lt;p&gt;通常想解決這個問題，可能會想到以下幾種方式：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;增加人力&lt;/li&gt;
&lt;li&gt;增加時間&lt;/li&gt;
&lt;li&gt;其它（例如：改善流程、自我成長、加班）&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;第一種「增加人力」在《&lt;a href=&quot;https://zh.wikipedia.org/zh-tw/%E4%BA%BA%E6%9C%88%E7%A5%9E%E8%AF%9D&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;人月神話&lt;/a&gt;》 一書中，已經告訴我們為什麼沒有用了。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://x.com/JustSomeDev/status/1546827210618814464?ref_src=twsrc%5Etfw%7Ctwcamp%5Etweetembed%7Ctwterm%5E1546827210618814464%7Ctwgr%5Ea732597fff51ab464a98bc7701e5f38315c02e08%7Ctwcon%5Es1_c10&amp;amp;ref_url=https%3A%2F%2Fcdn.embedly.com%2Fwidgets%2Fmedia.html%3Ftype%3Dtext2Fhtmlkey%3Da19fcc184b9711e1b4764040d3dc5c07schema%3Dtwitterurl%3Dhttps3A%2F%2Ftwitter.com%2FJustSomeDev%2Fstatus%2F15468272106188144643Fref_src3Dtwsrc255Etfw257Ctwcamp255Etweetembed257Ctwterm255E1546827210618814464257Ctwgr255E564e91ab5b1b194c891619dddd9112d15ed0fab5257Ctwcon255Es1_c1026ref_url3Dhttps253A252F252Fblog.amowu.com252Fprocrastiatio-and-parkinsons-law252Fimage%3D&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://x.com/JustSomeDev/status/1546827210618814464?ref_src=twsrc%5Etfw%7Ctwcamp%5Etweetembed%7Ctwterm%5E1546827210618814464%7Ctwgr%5Ea732597fff51ab464a98bc7701e5f38315c02e08%7Ctwcon%5Es1_c10&amp;amp;ref_url=https%3A%2F%2Fcdn.embedly.com%2Fwidgets%2Fmedia.html%3Ftype%3Dtext2Fhtmlkey%3Da19fcc184b9711e1b4764040d3dc5c07schema%3Dtwitterurl%3Dhttps3A%2F%2Ftwitter.com%2FJustSomeDev%2Fstatus%2F15468272106188144643Fref_src3Dtwsrc255Etfw257Ctwcamp255Etweetembed257Ctwterm255E1546827210618814464257Ctwgr255E564e91ab5b1b194c891619dddd9112d15ed0fab5257Ctwcon255Es1_c1026ref_url3Dhttps253A252F252Fblog.amowu.com252Fprocrastiatio-and-parkinsons-law252Fimage%3D&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;第二種「增加時間」，我們曾經嘗試過把 Scrum 的 Sprint 週期從兩個禮拜增加到三個禮拜，但是結果看起來並不理想。&lt;/p&gt;
&lt;p&gt;這篇文章我想要探討，為什麼我覺得第二種「增加時間」的方法也是沒有用的。&lt;/p&gt;
&lt;h2 id=&quot;為什麼會有拖延症&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#為什麼會有拖延症&quot;&gt;為什麼會有拖延症？&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;最近在《&lt;a href=&quot;https://www.igetget.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;罗辑思维&lt;/a&gt;》聽到一期《&lt;a href=&quot;https://d.dedao.cn/DN1DUiWhTCF9xsMJ&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;為什麼會有拖延症？&lt;/a&gt;》令我印象深刻，摘錄分享出來。&lt;/p&gt;
&lt;p&gt;為什麼會有拖延症？其實不是因為我們自我管理不嚴格，而是因為只要時間資源在那裡，你就會傾向於把它用盡。&lt;/p&gt;
&lt;p&gt;1955 年，英國政治學家帕金森，提出了一個「帕金森定理」：一個組織，總是會趨向於層級越來越多，規模越來越膨脹，人越來越忙，但組織效率越來越低下。&lt;/p&gt;
&lt;p&gt;為什麼會變這樣呢？是因為組織腐敗嗎？不是，員工自己也覺得工作很辛苦、每天忙得要死。那是為什麼呢？&lt;/p&gt;
&lt;p&gt;因為人類組織的自然規律：&lt;strong&gt;不是做多少事、就需要多少資源。而是有多少資源、組織就一定會膨脹到那個程度，直到把這個資源耗盡為止。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;至於事情有沒有多做、做得好不好，反倒不是那麼重要了。反過來，把資源砍掉，也未必會影響做事的成效。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Parkinson’s law&quot; width=&quot;700&quot; height=&quot;320&quot; src=&quot;https://blog.amowu.com/_astro/image1.D_GEHQbB_Z2hVvpR.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image1.D_GEHQbB_8pgqS.webp 640w, https://blog.amowu.com/_astro/image1.D_GEHQbB_Z2hVvpR.webp 700w&quot; /&gt;
&lt;em&gt;Parkinson’s law&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;這個過程中，不僅資源會被用上，而且還會衍生出新的事務，迫使組織又進一步渴望新的資源，進入惡性循環。&lt;/p&gt;
&lt;p&gt;舉個例子，一間公司原本很小，只有兩個人，分工清楚。&lt;/p&gt;
&lt;p&gt;這個時候，公司招聘了一批新人，那麼有些工作就可以交給他們了，但是原本的兩人有變輕鬆了嗎？並沒有，因為多了一個人，至少多出來了好幾件事：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;工作的分配問題&lt;/strong&gt;：要想清楚哪些工作可以交出去、什麼時候交、交到什麼程度？&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;人際關係問題&lt;/strong&gt;：要考慮大家的心情和可能發生的矛盾，工作量和工作成果的分配要儘可能公平合理&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;品質問題&lt;/strong&gt;：新人做事不放心，要去 review 他們的工作成果，有需要改善的地方，可能得查缺補漏甚至推倒重來&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;組織發展問題&lt;/strong&gt;：公司要對他們負責，所以要花時間、找資源去培訓他們、提升他們&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;評價標準問題&lt;/strong&gt;：人多了，就牽涉到排名問題，為了給將來的升職加薪留個依據，公司就要制定 KPI 之類的玩意兒&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;這裡面的每一件事你都不能說沒必要，但是每一件事都只是因為多了這麼個人、這麼個資源，因事生事，已經和最初的工作目標沒什麼關係了。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;回到拖延症這個話題。這個規律也適用於「時間」這種資源：並不是因為需要多少用多少，而是有多少你就會用光多少。&lt;/p&gt;
&lt;p&gt;你可能會懷疑，做一件事需要的時間不是有限的嗎？從鎖一顆螺絲到造一台汽車，不僅需要的時間是有限的，而且還可以持續優化，怎麼會需要無限的時間資源呢？&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;這是因為人類工作要處理的對象，從「物品」變成了「事情」。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;以前，無論是農業時代還是工業時代，我們的工作對象主要是物品：種一畝地的田、流水線上加工一個零件。因為物品的邊界是清晰的，所以需要的工作時間就是有限的。我們努力的方向，就是通過各種工具和創新，節省時間資源，獲得更高的效率。&lt;/p&gt;
&lt;p&gt;但是現在我們的工作對象越來越像是「事情」。比如寫一篇企劃搞、做藝術創作、開發一個程式功能、準備一場行銷活動。只要是「事情」，背後包含的複雜度就太高了。而且只要是一件「事情」，就可以膨脹成任何規模的工作。&lt;/p&gt;
&lt;p&gt;只要有時間，你就可以無窮無盡地圍繞著這個決定做準備，沒有一件事是毫無必要的，也沒有一件事是確定有必要的。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Human Evolution&quot; width=&quot;700&quot; height=&quot;239&quot; src=&quot;https://blog.amowu.com/_astro/image2.C48zQ9UX_Z2s6fxE.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image2.C48zQ9UX_Zxdv8i.webp 640w, https://blog.amowu.com/_astro/image2.C48zQ9UX_Z2s6fxE.webp 700w&quot; /&gt;
&lt;em&gt;Human Evolution&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;這也可以解釋 &lt;a href=&quot;https://zh.m.wikipedia.org/zh-hant/996%E5%B7%A5%E4%BD%9C%E5%88%B6&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;996 工作制&lt;/a&gt;這件事。不是因為 IT 公司都是慣老闆，也不是因為 IT 公司的工作量就比別人多，而是因為 IT 公司面對的工作對象，不是清晰的「物品」，而是一件件說不清、道不明的「事情」，它的工作對時間資源的索取就是無止境的。所以 996 本質上不是說需要這麼多時間，而是現在一個 IT 從業人員能榨取的工作時間的極限就這麼多了，那些搞 996 工作制的公司，只是把它用盡了而已。&lt;/p&gt;
&lt;h2 id=&quot;結語&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#結語&quot;&gt;結語&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;如果「加人」與「加時間」都無法解決產品交付 delay 問題的話，可能就要從「其它」方式下手了。&lt;/p&gt;
&lt;p&gt;撇開「自我成長」和「加班」這類不可控因素，我認為團隊應該要分配資源在「改善流程」上面。&lt;/p&gt;
&lt;p&gt;比如我接下來預計要寫的幾篇文章：《Trunk Based Development 》、《Single Text of Truth 》以及《Design to Code》。這些都是跟交付功能無關的工作，卻是可以改善團隊交付速度的解決方案。&lt;/p&gt;
&lt;p&gt;否則如果只是把「&lt;a href=&quot;https://zh.wikipedia.org/zh-tw/%E7%80%91%E5%B8%83%E6%A8%A1%E5%9E%8B&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;瀑布&lt;/a&gt;」換成「&lt;a href=&quot;https://zh.wikipedia.org/zh-tw/%E6%95%8F%E6%8D%B7%E8%BD%AF%E4%BB%B6%E5%BC%80%E5%8F%91&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;敏捷&lt;/a&gt;」的話，本質上還是在第二種資源「時間」上打轉而已，最終可能還是得應付處理不完的「&lt;a href=&quot;https://ithelp.ithome.com.tw/articles/10198394&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;隕石&lt;/a&gt;」。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://x.com/AlmorTech/status/1541753810984615937?ref_src=twsrc%5Etfw%7Ctwcamp%5Etweetembed%7Ctwterm%5E1541753810984615937%7Ctwgr%5E50afa6fb15242354407e155310e15b5950d1480a%7Ctwcon%5Es1_c10&amp;amp;ref_url=https%3A%2F%2Fcdn.embedly.com%2Fwidgets%2Fmedia.html%3Ftype%3Dtext2Fhtmlkey%3Da19fcc184b9711e1b4764040d3dc5c07schema%3Dtwitterurl%3Dhttps3A%2F%2Ftwitter.com%2Falmormd%2Fstatus%2F15417538109846159373Fref_src3Dtwsrc255Etfw257Ctwcamp255Etweetembed257Ctwterm255E1541753810984615937257Ctwgr255E564e91ab5b1b194c891619dddd9112d15ed0fab5257Ctwcon255Es1_c1026ref_url3Dhttps253A252F252Fblog.amowu.com252Fprocrastiatio-and-parkinsons-law252Fimage%3D&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://x.com/AlmorTech/status/1541753810984615937?ref_src=twsrc%5Etfw%7Ctwcamp%5Etweetembed%7Ctwterm%5E1541753810984615937%7Ctwgr%5E50afa6fb15242354407e155310e15b5950d1480a%7Ctwcon%5Es1_c10&amp;amp;ref_url=https%3A%2F%2Fcdn.embedly.com%2Fwidgets%2Fmedia.html%3Ftype%3Dtext2Fhtmlkey%3Da19fcc184b9711e1b4764040d3dc5c07schema%3Dtwitterurl%3Dhttps3A%2F%2Ftwitter.com%2Falmormd%2Fstatus%2F15417538109846159373Fref_src3Dtwsrc255Etfw257Ctwcamp255Etweetembed257Ctwterm255E1541753810984615937257Ctwgr255E564e91ab5b1b194c891619dddd9112d15ed0fab5257Ctwcon255Es1_c1026ref_url3Dhttps253A252F252Fblog.amowu.com252Fprocrastiatio-and-parkinsons-law252Fimage%3D&lt;/a&gt;&lt;/p&gt;</content:encoded><category>Tech</category></item><item><title>Hahow for Business 如何跑 E2E 測試？</title><link>https://blog.amowu.com/posts/2022-06-29-cypress-on-rails/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2022-06-29-cypress-on-rails/</guid><description>隨著 Chrome headless 模式的推出，E2E 測試上 CI 已經變得容易。這篇文章分享什麼是 Cypress，為什麼我覺得寫 E2E 測試很重要，以及我們團隊是怎麼跑這個流程的。</description><pubDate>Wed, 29 Jun 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;隨著 Chrome headless 模式的推出，E2E 測試上 CI 已經變得容易。這篇文章分享什麼是 Cypress，為什麼我覺得寫 E2E 測試很重要，以及我們團隊是怎麼跑這個流程的。&lt;/p&gt;
&lt;p&gt;這篇文章會介紹 Ruby on Rails 如何配置 CircleCI 運行 Cypress 的 E2E 測試。&lt;/p&gt;
&lt;h2 id=&quot;什麼是-cypress&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#什麼是-cypress&quot;&gt;什麼是 Cypress？&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.cypress.io/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Cypress&lt;/a&gt; 是一款&lt;strong&gt;自動化 E2E 測試&lt;/strong&gt;（Automated End-to-End Testing）工具，編寫方式以 JavaScript 語言為主。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.cypress.io/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Testing Frameworks for Javascript | Write, Run, Debug | Cypress&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;E2E 這類測試方法，是從終端使用者的角度出發，模擬真實世界的操作場景，用於確保網站的行為符合預期。&lt;/p&gt;
&lt;p&gt;類似的工具還有 &lt;a href=&quot;https://www.selenium.dev/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Selenium&lt;/a&gt;、&lt;a href=&quot;https://nightwatchjs.org/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Nightwatch&lt;/a&gt;、&lt;a href=&quot;https://github.com/puppeteer/puppeteer&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Puppeteer&lt;/a&gt; 以及 &lt;a href=&quot;https://playwright.dev/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Playwright&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;除了測試之外，這些工具一般也可以用來寫&lt;a href=&quot;https://en.wikipedia.org/wiki/Web_scraping&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;網頁爬蟲&lt;/a&gt;（Web Scraper）腳本。&lt;/p&gt;
&lt;h2 id=&quot;e2e-測試-vs單元測試&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#e2e-測試-vs單元測試&quot;&gt;E2E 測試 vs 單元測試&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;可能有人會問，為什麼不寫單元測試（Unit tests）就好？&lt;/p&gt;
&lt;p&gt;因為兩者適用的場景不同。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;466&quot; height=&quot;246&quot; src=&quot;https://blog.amowu.com/_astro/image1.Djlizjkq_Z1CyHP9.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image1.Djlizjkq_Z1CyHP9.webp 466w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;很多東西是單元測試寫得再好也測不出來的。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;2 unit tests. 0 integration tests&quot; width=&quot;718&quot; height=&quot;404&quot; src=&quot;https://blog.amowu.com/_astro/image2.BgU9mUSm_1moeS4.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image2.BgU9mUSm_Z1hweY4.webp 640w, https://blog.amowu.com/_astro/image2.BgU9mUSm_1moeS4.webp 718w&quot; /&gt;
&lt;em&gt;2 unit tests. 0 integration tests&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;舉個 &lt;a href=&quot;https://business.hahow.in/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Hahow for Business&lt;/a&gt; 的例子，最近前端加入了 &lt;a href=&quot;https://reactjs.org/docs/code-splitting.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Code-Splitting&lt;/a&gt; 的機制，結果某天 Sales 在向客戶 demo 產品的時候，發現後台有點選特定頁面會造成網站 crash 的問題。（關於 Rails 怎麼做 code-splitting 我之後會專門寫一篇文章跟大家講解）&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;不好意思，有些地方出錯了！&quot; width=&quot;800&quot; height=&quot;462&quot; src=&quot;https://blog.amowu.com/_astro/image3.Bg68_MCQ_26LI76.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image3.Bg68_MCQ_19AbGv.webp 640w, https://blog.amowu.com/_astro/image3.Bg68_MCQ_1k75Ax.webp 750w, https://blog.amowu.com/_astro/image3.Bg68_MCQ_26LI76.webp 800w&quot; /&gt;
&lt;em&gt;不好意思，有些地方出錯了！&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;如上圖操作所示，像這種問題就是單元測試無法重現的。無法重現的話，就沒辦法寫測試來避免再度發生。&lt;/p&gt;
&lt;p&gt;以 B2B 這種對網站穩定性要求相對高的產品來說，合約上通常會規定網站的 HA 必須維持在一個數字。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://x.com/amo_wu/status/1108358207401914368?ref_src=twsrc%5Etfw%7Ctwcamp%5Etweetembed%7Ctwterm%5E1108358207401914368%7Ctwgr%5E49d09029bfed0045ab0da4d03205de6d64b7a8e1%7Ctwcon%5Es1_c10&amp;amp;ref_url=https%3A%2F%2Fcdn.embedly.com%2Fwidgets%2Fmedia.html%3Ftype%3Dtext2Fhtmlkey%3Da19fcc184b9711e1b4764040d3dc5c07schema%3Dtwitterurl%3Dhttps3A%2F%2Ftwitter.com%2Famo_wu%2Fstatus%2F11083582074019143683Fref_src3Dtwsrc255Etfw257Ctwcamp255Etweetembed257Ctwterm255E1108358207401914368257Ctwgr255Efec710994c10cedfec9c6e132faaa72dd563edd7257Ctwcon255Es1_c1026ref_url3Dhttps253A252F252Fblog.amowu.com252Fcypress-on-rails252Fimage%3D&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://x.com/amo_wu/status/1108358207401914368?ref_src=twsrc%5Etfw%7Ctwcamp%5Etweetembed%7Ctwterm%5E1108358207401914368%7Ctwgr%5E49d09029bfed0045ab0da4d03205de6d64b7a8e1%7Ctwcon%5Es1_c10&amp;amp;ref_url=https%3A%2F%2Fcdn.embedly.com%2Fwidgets%2Fmedia.html%3Ftype%3Dtext2Fhtmlkey%3Da19fcc184b9711e1b4764040d3dc5c07schema%3Dtwitterurl%3Dhttps3A%2F%2Ftwitter.com%2Famo_wu%2Fstatus%2F11083582074019143683Fref_src3Dtwsrc255Etfw257Ctwcamp255Etweetembed257Ctwterm255E1108358207401914368257Ctwgr255Efec710994c10cedfec9c6e132faaa72dd563edd7257Ctwcon255Es1_c1026ref_url3Dhttps253A252F252Fblog.amowu.com252Fcypress-on-rails252Fimage%3D&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;如果工程師不想下次因為一樣的問題而被 on-call 的話，或許可以考慮加入 E2E 測試。&lt;/p&gt;
&lt;p&gt;其中最保險的方式是把 E2E 測試加進團隊的 &lt;a href=&quot;https://en.wikipedia.org/wiki/CI/CD&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;CI/CD&lt;/a&gt; 流程之中。&lt;/p&gt;
&lt;p&gt;自從 &lt;a href=&quot;https://developer.chrome.com/blog/headless-chrome/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Headless Chrome&lt;/a&gt; 模式（可以把它想像成在 CLI 模擬一個 browser 環境）出現後，Cypress 這類 E2E 測試上 CI 已經變得很容易許多。&lt;/p&gt;
&lt;p&gt;但也不是所有的技術棧（Tech stack）說想要導入這個流程就能導入的。&lt;/p&gt;
&lt;p&gt;要在 CI 整合 E2E 測試，有一個重要的先決條件是：&lt;strong&gt;前後端合一的 monorepo。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;近年來流行「前後端分離」的 &lt;a href=&quot;https://en.wikipedia.org/wiki/Single-page_application&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;SPA&lt;/a&gt; 架構，無論是 &lt;a href=&quot;https://web.dev/rendering-on-the-web/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;CSR&lt;/a&gt; 或 &lt;a href=&quot;https://web.dev/rendering-on-the-web/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;SSR&lt;/a&gt;，這種架構下都容易出現將原始碼分開管理的 Multi-repo（又稱 &lt;a href=&quot;https://github.com/joelparkerhenderson/monorepo-vs-polyrepo&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Polyrepo&lt;/a&gt;）形式。&lt;/p&gt;
&lt;p&gt;Multi-repo 的劣勢在於很難自動化 E2E 這類整合測試（Integration Tests），因為不管是修改前端或是後端的 code，都有可能造成 E2E 測試失敗，如果這時候 CI 還需要整合兩邊的 repo 環境，想必在技術上會是一個困難的挑戰。&lt;/p&gt;
&lt;p&gt;Hahow for Business 使用的框架是 Ruby on Rails，RoR 本身是 monolithic 架構，前後端在同一個 codebase 底下，加上其完善的生態（例如資料庫的 &lt;a href=&quot;https://guides.rubyonrails.org/v5.1/active_record_migrations.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Migrations 和 Seed Data&lt;/a&gt;，這些機制很重要），讓 Cypress 在搭配 Ruby on Rails 的過程中，沒有遇到太大的問題。&lt;/p&gt;
&lt;p&gt;接下來，就來介紹怎麼在 Ruby on Rails 環境下跑 Cypress。&lt;/p&gt;
&lt;h2 id=&quot;getting-started&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#getting-started&quot;&gt;Getting started&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Cypress 的安裝和語法就不贅述了，詳見&lt;a href=&quot;https://docs.cypress.io/guides/getting-started/installing-cypress&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;官方文件&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;我們直接以剛才提到的「後台 crash」案例為例，撰寫 Cypress 測試，寫起來大概這種感覺：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame has-title&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;cypress/integration/visit_pages_by_side_menu.js&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;describe&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;訪問管理後台特定頁面&apos;&lt;/span&gt;&lt;span&gt;, () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;before&lt;/span&gt;&lt;span&gt;(() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;// 這是自訂的 Cypress Custom Command，用於快速登入某個帳號，這裡就不細述實作&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;cy.&lt;/span&gt;&lt;span&gt;fastSignIn&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;admin@hahow.in&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;password&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;// 跑測試之前，先打開後台網址&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;cy.&lt;/span&gt;&lt;span&gt;visit&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;http://localhost:3000/admin&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;describe&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;從側欄連結先訪問「課程管理」再訪問「成效追蹤」&apos;&lt;/span&gt;&lt;span&gt;, () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;it&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;點擊左側的「課程管理」選項，右側標題應該顯示「課程管理」&apos;&lt;/span&gt;&lt;span&gt;, () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;// 可以給要操作的 element 加上 data-test-id attribute，這樣 selector 會比較好寫&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;cy.&lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;li[data-test-id=&quot;課程管理&quot;]&apos;&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;click&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;cy.&lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;[data-test-id=&quot;control-panel&quot;] h3&apos;&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;contains&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;課程管理&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;it&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;點擊左側的「成效追蹤」，右側標題應該顯示「成效追蹤」&apos;&lt;/span&gt;&lt;span&gt;, () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;cy.&lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;li[data-test-id=&quot;成效追蹤&quot;]&apos;&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;click&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;cy.&lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;[data-test-id=&quot;control-panel&quot;] h3&apos;&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;contains&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;成效追蹤&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;先在 local 跑一遍試試，指令是 &lt;code&gt;yarn cypress open&lt;/code&gt;，記得要先把 development 的環境開起來，以 Hahow for Business 為例，大致如下：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bin/webpack-dev-server&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;# 前端 local 開發環境&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bin/rails&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;server&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;# Rails Server&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bundle&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;exec&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sidekiq&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;# Sidekiq 環境，非必要&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;yarn&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cypress&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;open&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;接著點擊要執行的檔案，Cypress 就會開啟一個 Chrome，開始跑自動化測試：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;800&quot; height=&quot;502&quot; src=&quot;https://blog.amowu.com/_astro/image4.DLhbC5hT_Z18jxBo.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image4.DLhbC5hT_tvawN.webp 640w, https://blog.amowu.com/_astro/image4.DLhbC5hT_1kmw8Q.webp 750w, https://blog.amowu.com/_astro/image4.DLhbC5hT_Z18jxBo.webp 800w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;如上圖，沒意外的話，就會看到 &lt;code&gt;AssertionError&lt;/code&gt;，表示成功重現 crash 的問題，接著就可以開始 &lt;a href=&quot;https://en.wikipedia.org/wiki/Test-driven_development&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;TDD&lt;/a&gt; 找原因了。&lt;/p&gt;
&lt;h2 id=&quot;解決-cookies問題&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#解決-cookies問題&quot;&gt;解決 Cookies 問題&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;E2E 測試通常不會只有一個檔案要跑，如果不同的測試之間所使用的帳號不一樣，就有可能會碰到 Cookies 相關的問題（例如 &lt;code&gt;InvalidAuthenticityToken&lt;/code&gt; 錯誤），解決方法如下：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;describe&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;...&apos;&lt;/span&gt;&lt;span&gt;, () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;before&lt;/span&gt;&lt;span&gt;(() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;// 清除此前測試的登入狀態&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;cy.&lt;/span&gt;&lt;span&gt;clearCookies&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;// 登入此次的測試帳號&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;// ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;beforeEach&lt;/span&gt;&lt;span&gt;(() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;// 在接下來的每一個 test case 之間，保留 cookies 狀態，&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;// 避免 InvalidAuthenticityToken 錯誤。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Cypress.Cookies.&lt;/span&gt;&lt;span&gt;preserveOnce&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;YOUR_COOKIE_NAME&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;// ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;更多細節可以參考 Cypress 有關 &lt;a href=&quot;https://docs.cypress.io/api/cypress-api/cookies&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Cookies 的章節&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;完整的程式碼大致如下：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame has-title&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;cypress/integration/visit_pages_by_side_menu.js&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;describe&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;訪問管理後台特定頁面&apos;&lt;/span&gt;&lt;span&gt;, () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;before&lt;/span&gt;&lt;span&gt;(() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;cy.&lt;/span&gt;&lt;span&gt;clearCookies&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;cy.&lt;/span&gt;&lt;span&gt;fastSignIn&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;admin@hahow.in&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;password&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;cy.&lt;/span&gt;&lt;span&gt;visit&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;http://localhost:3000/admin&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;beforeEach&lt;/span&gt;&lt;span&gt;(() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Cypress.Cookies.&lt;/span&gt;&lt;span&gt;preserveOnce&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;_hahow_for_business_session&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;describe&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;從側欄連結先訪問「課程管理」再訪問「成效追蹤」&apos;&lt;/span&gt;&lt;span&gt;, () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;it&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;點擊左側的「課程管理」選項，右側標題應該顯示「課程管理」&apos;&lt;/span&gt;&lt;span&gt;, () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;cy.&lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;li[data-test-id=&quot;課程管理&quot;]&apos;&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;click&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;cy.&lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;[data-test-id=&quot;control-panel&quot;] h3&apos;&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;contains&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;課程管理&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;it&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;點擊左側的「成效追蹤」，右側標題應該顯示「成效追蹤」&apos;&lt;/span&gt;&lt;span&gt;, () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;cy.&lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;li[data-test-id=&quot;成效追蹤&quot;]&apos;&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;click&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;cy.&lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;[data-test-id=&quot;control-panel&quot;] h3&apos;&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;contains&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;成效追蹤&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;上面這段程式碼，其實還可以寫得更漂亮些，例如看似重複的兩個 &lt;code&gt;it&lt;/code&gt; 可以透過 &lt;a href=&quot;https://github.com/badeball/cypress-cucumber-preprocessor&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;cypress-cucumber-preprocessor&lt;/a&gt; 寫成容易共用的 &lt;a href=&quot;https://en.wikipedia.org/wiki/Behavior-driven_development&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;BDD&lt;/a&gt; 模組。另外因為受限於 CSS selector 語法，常常每個測試都要侵入原始碼去加 &lt;code&gt;data-test-id&lt;/code&gt;，感覺不太好，這部分可以改用 &lt;a href=&quot;https://github.com/cypress-io/cypress-xpath&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;cypress-xpath&lt;/a&gt; 解決。&lt;/p&gt;
&lt;p&gt;關於 &lt;a href=&quot;https://github.com/cypress-io/cypress-xpath&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;cypress-xpath&lt;/a&gt; 和 &lt;a href=&quot;https://github.com/badeball/cypress-cucumber-preprocessor&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;cypress-cucumber-preprocessor&lt;/a&gt; 我之後會專門寫一篇文章跟大家講解。&lt;/p&gt;
&lt;h2 id=&quot;修復問題&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#修復問題&quot;&gt;修復問題&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;追了一會兒的原因之後，發現罪魁禍首是我們把 webpack 的 minimizer 換成 &lt;a href=&quot;https://github.com/privatenumber/esbuild-loader&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;ESBuildMinifyPlugin&lt;/a&gt; 的緣故。修復之後，如下圖所示，再跑一遍 E2E 測試，確認全部通過之後就沒問題了：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;800&quot; height=&quot;498&quot; src=&quot;https://blog.amowu.com/_astro/image5.BgMLRF9c_ZPqdX6.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image5.BgMLRF9c_Loub6.webp 640w, https://blog.amowu.com/_astro/image5.BgMLRF9c_Z1BWLv5.webp 750w, https://blog.amowu.com/_astro/image5.BgMLRF9c_ZPqdX6.webp 800w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;（關於「如何透過 &lt;a href=&quot;https://github.com/privatenumber/esbuild-loader&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;esbuild-loader&lt;/a&gt; 加速 webpack compile」我之後會專門寫一篇文章跟大家講解）&lt;/p&gt;
&lt;h2 id=&quot;continuous-integration&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#continuous-integration&quot;&gt;Continuous integration&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;接下來，為了避免問題再度發生，我們希望將 E2E 測試整合進團隊的 CI 流程，並且每個 Pull Request 都會觸發。如果測試不通過，PR 將無法被 merge，可以有效地將問題的範圍縮小在源頭。&lt;/p&gt;
&lt;p&gt;以我們團隊在用的 &lt;a href=&quot;https://circleci.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;CircleCI&lt;/a&gt; 為例，配置大致如下：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame has-title&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;.circleci/config.yml&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;version&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;2.1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;jobs&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;e2e-test&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;docker&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;image&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;cimg/ruby:2.7.4-browsers&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;image&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;cimg/postgres:12.7&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;image&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;redis:4-alpine&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;# 非必須，有跑 Sidekiq 才需要&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;steps&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;checkout&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;# 安裝 Gem 和 npm 的套件&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;bundle install&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;yarn --frozen-lockfile&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;# 這裡省略了 Gemfile.lock 和 yarn.lock 的 restore_cache 和 save_cache&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;bin/rails db:setup&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;bin/rails dev:prime&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;# or bin/rails db:seed&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;          &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Ruby on Rails server&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;          &lt;/span&gt;&lt;span&gt;command&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;bin/rails server&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;          &lt;/span&gt;&lt;span&gt;background&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;# 非必須，有跑 Sidekiq 才需要&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;          &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Sidekiq worker&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;          &lt;/span&gt;&lt;span&gt;command&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;bundle exec sidekiq&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;          &lt;/span&gt;&lt;span&gt;background&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;          &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Ping 網頁，避免 Cypress 因為等待 webpack compile 而 timeout&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;          &lt;/span&gt;&lt;span&gt;command&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;npx wait-on http-get://localhost:3000&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;yarn cypress run&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;workflows&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;test&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;jobs&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;e2e-test&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;（其它 CI Provider 的設定應該也大同小異，詳細可以參考 &lt;a href=&quot;https://docs.cypress.io/guides/continuous-integration/ci-provider-examples&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Cypress 提供的範例&lt;/a&gt;）&lt;/p&gt;
&lt;p&gt;其中 &lt;code&gt;rails db:setup&lt;/code&gt; 和 &lt;code&gt;rails dev:prime&lt;/code&gt; 可以確保每次 CI 都是一個初始狀態的環境。&lt;code&gt;cypress run&lt;/code&gt; 與 &lt;code&gt;cypress open&lt;/code&gt; 不同，前者會以 Chrome headless 模式運行測試。&lt;/p&gt;
&lt;p&gt;丟到 CircleCI 之後，如果 E2E 測試不通過，就會顯示如下圖的相關資訊：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;455&quot; src=&quot;https://blog.amowu.com/_astro/image6.DdvnJkLH_ZY9hcz.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image6.DdvnJkLH_LghRS.webp 640w, https://blog.amowu.com/_astro/image6.DdvnJkLH_ZY9hcz.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;github&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#github&quot;&gt;GitHub&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;最後，記得將 GitHub 的 &lt;code&gt;Require status checks to pass before merging&lt;/code&gt; 給打開，然後加入 &lt;code&gt;ci/circleci: e2e-test&lt;/code&gt;，如下圖所示：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;467&quot; src=&quot;https://blog.amowu.com/_astro/image7.ew-x8AAt_Z1aThtS.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image7.ew-x8AAt_Z1WvJjB.webp 640w, https://blog.amowu.com/_astro/image7.ew-x8AAt_Z1aThtS.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;設定好之後，如果 E2E 測試沒通過，PR 就無法被 merge。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;150&quot; src=&quot;https://blog.amowu.com/_astro/image8.D2cAlyYH_OdLeK.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image8.D2cAlyYH_2v8z7H.webp 640w, https://blog.amowu.com/_astro/image8.D2cAlyYH_OdLeK.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;為了方便 developer 查找測試沒通過的原因，可以利用 CircleCI 提供的 &lt;code&gt;store_artifacts&lt;/code&gt; 機制，將 Cypress 產生的截圖和錄影暫存起來，只需要加四行：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;yarn cypress run&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;store_artifacts&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;path&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;cypress/videos&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;store_artifacts&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;path&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;cypress/screenshots&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;如下圖，這樣就可以在 CircleCI 的 Artifacts 底下看到 &lt;code&gt;e2e-test&lt;/code&gt; failed 的相關截圖以及錄影的連結：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;455&quot; src=&quot;https://blog.amowu.com/_astro/image9.DHRR3bCh_Z251CL4.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image9.DHRR3bCh_ZjB3FB.webp 640w, https://blog.amowu.com/_astro/image9.DHRR3bCh_Z251CL4.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;faq&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#faq&quot;&gt;FAQ&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;circleci-如何安裝chrome&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#circleci-如何安裝chrome&quot;&gt;CircleCI 如何安裝 Chrome？&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;如果 CircleCI 遇到找不到 Chrome 的問題，可以安裝官方提供的套件 &lt;code&gt;circleci/browser-tools&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame has-title&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;.circleci/config.yml&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;version&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;2.1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;orbs&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;browser-tools&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;circleci/browser-tools@1.2.4&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;jobs&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;e2e-test&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;# ...省略&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;steps&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;browser-tools/install-chrome&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;browser-tools/install-chromedriver&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;checkout&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;# ...省略&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;（參考自 &lt;a href=&quot;https://circleci.com/developer/orbs/orb/circleci/browser-tools&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://circleci.com/developer/orbs/orb/circleci/browser-tools#usage-install_chrome&lt;/a&gt;）&lt;/p&gt;
&lt;h3 id=&quot;circleci-如何顯示中文字型&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#circleci-如何顯示中文字型&quot;&gt;CircleCI 如何顯示中文字型？&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;如果你的 E2E 測試對象是中文網站，可能會遇到 CircleCI 的截圖和錄影無法顯示中文字的問題，解決方法如下：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame has-title&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;.circleci/config.yml&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;version&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;2.1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;jobs&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;e2e-test&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;# ...省略&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;steps&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;          &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Cypress 截圖、影片支援顯示中文&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;          &lt;/span&gt;&lt;span&gt;command&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;sudo apt-get -qqy update&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;sudo apt-get -qqy --no-install-recommends install fonts-wqy-zenhei&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;sudo rm -rf /var/lib/apt/lists/*&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;sudo apt-get -qyy clean&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;（參考自 &lt;a href=&quot;https://github.com/cypress-io/cypress-docker-images/issues/109&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://github.com/cypress-io/cypress-docker-images/issues/109#issuecomment-590183007&lt;/a&gt;）&lt;/p&gt;
&lt;h2 id=&quot;結語&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#結語&quot;&gt;結語&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;導入 E2E 測試流程之後，團隊可能會遇到以下問題：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;錢&lt;/li&gt;
&lt;li&gt;時間&lt;/li&gt;
&lt;li&gt;人力&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;一開始 E2E 測試還很少的時候，可能不會有什麼問題；但是隨著測試案例增加，CI 運行的時間勢必也會增長，需要付出的金錢成本也會提高。&lt;/p&gt;
&lt;p&gt;其中最容易造成浪費的部分，就是每個 Pull Request 都會觸發 E2E 測試這個環節。通常來說，developer 不會開了 PR 之後就完事了，後續還會 push commits 到 PR，這就造成每次 push 也都會觸發 E2E 測試的問題。&lt;/p&gt;
&lt;p&gt;一個比較快的解決方式，是在 &lt;code&gt;e2e-test&lt;/code&gt; job 之前，加入 &lt;a href=&quot;https://circleci.com/docs/2.0/workflows&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;manual approval&lt;/a&gt; 的機制，讓 developer 自己決定跑 E2E 測試的時機點。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;93&quot; src=&quot;https://blog.amowu.com/_astro/image10.DkABgUnF_3UCyI.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image10.DkABgUnF_1I7mKd.webp 640w, https://blog.amowu.com/_astro/image10.DkABgUnF_3UCyI.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;其次是時間成本問題，E2E 測試很容易發生隨機錯誤（&lt;a href=&quot;https://www.jetbrains.com/teamcity/ci-cd-guide/concepts/flaky-tests/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Flaky Tests&lt;/a&gt;），如果沒有做 &lt;a href=&quot;https://docs.cypress.io/guides/guides/parallelization&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Parallelization&lt;/a&gt;，常常會因為一個 flaky test 造成整個 E2E 測試 failed，需要再重跑一遍，導致 DX（Developer experience）變得極差。&lt;/p&gt;
&lt;p&gt;這部分我們是將流程遷移至 &lt;a href=&quot;https://www.cypress.io/dashboard/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Cypress Dashboard&lt;/a&gt; 服務之後才有所改善。關於 &lt;a href=&quot;https://www.cypress.io/dashboard/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Cypress Dashboard&lt;/a&gt; 我之後會專門寫一篇文章跟大家講解。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.cypress.io/dashboard/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Cypress Cloud | Elevated Test Automation In Your CI&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;最後是人力問題，就跟「寫註解」還有「寫文件」一樣，developer 也不喜歡「寫測試」，這裡我的建議是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;轉念，如果這個功能出問題，第一時間是 on-call 你修的話，就寫測試來防禦吧&lt;/li&gt;
&lt;li&gt;先寫業務核心的重要功能（例如：支付流程）&lt;/li&gt;
&lt;li&gt;交給專業的來，如果團隊有 QA 或 QE 的角色，通常會做回歸測試（Regression Tests），這部分如果能跟團隊的 E2E 測試流程整合在一起的話，想必能事倍功半&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;當公司從小團隊發展成大團隊之後，一些流程就變得必要。還是小團隊的時候，一些事情直接做無仿，但當規模變大了，就需要有規範的流程。當然，有了流程就可能出現繁雜的障礙。但是一家公司不能永遠處於初創期，它總要進入成熟期。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;這些規範，有時不是為了最好的結果，而是為了防止最壞的結果。一些人不習慣這些規範流程，會講這是內卷（involution），其實這些看似繁雜的過程，可以避免很多災難性的結果。當你只有 100 元的家產時，不會太擔心把事情搞砸了損失家產，而當你有 100 萬的時候，做事的方式就完全不同了。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;— — 吳軍《硅谷來信》&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;參考文章&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#參考文章&quot;&gt;參考文章&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://circleci.com/blog/what-is-end-to-end-testing/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;What is end-to-end testing? | CircleCI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://martinfowler.com/articles/practical-test-pyramid.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;The Practical Test Pyramid&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>Tech</category></item><item><title>Hahow for Business 如何保護串流內容？</title><link>https://blog.amowu.com/posts/2020-09-04-hls-signed-cookies/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2020-09-04-hls-signed-cookies/</guid><description>近來因疫情關係，不少 OTT 與線上教育等影音串流平台的流量急劇上升，本篇文章以 Hahow 為例，帶你了解隨選視訊背後的原理以及如何保護數位內容。</description><pubDate>Fri, 04 Sep 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;近來因疫情關係，不少 OTT 與線上教育等影音串流平台的流量急劇上升，本篇文章以 Hahow 為例，帶你了解隨選視訊背後的原理以及如何保護數位內容。&lt;/p&gt;
&lt;p&gt;這篇文章會介紹 Hahow for Business 是如何透過 AWS CloudFront 的 Signed Cookies 機制來保護 HLS 格式的串流影片。&lt;/p&gt;
&lt;h2 id=&quot;大綱&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#大綱&quot;&gt;大綱&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;前言&lt;/li&gt;
&lt;li&gt;什麼是 VOD？&lt;/li&gt;
&lt;li&gt;什麼是 HLS？&lt;/li&gt;
&lt;li&gt;如何保護串流內容？&lt;/li&gt;
&lt;li&gt;什麼是 Signed URLs／Cookies？&lt;/li&gt;
&lt;li&gt;結論&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;前言&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#前言&quot;&gt;前言&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;六月初，&lt;a href=&quot;https://business.hahow.in/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Hahow for Business&lt;/a&gt; 上線了一個新功能：&lt;strong&gt;影片播放器開始提供不同解析度來源的切換選項&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;其中「自動」的選項，可以根據使用者當前的網速，自動切換適配的解析度。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;675&quot; height=&quot;457&quot; src=&quot;https://blog.amowu.com/_astro/image1.Ds6wStXl_PTggi.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image1.Ds6wStXl_NJdHK.webp 640w, https://blog.amowu.com/_astro/image1.Ds6wStXl_PTggi.webp 675w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;恰巧，Hahow for Business 自去年六月正式啟動以來，也剛好滿一年。&lt;/p&gt;
&lt;p&gt;最初，為了能夠儘快投入市場試水溫，產品開發時程相對吃緊，很多東西都是先求有再求好，所以勢必會留下一些技術債，例如 &lt;strong&gt;VOD&lt;/strong&gt; 就是其中之一。&lt;/p&gt;
&lt;p&gt;延伸閱讀：&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://citysite1025.medium.com/%E7%82%BA%E4%BD%95%E9%81%B8%E6%93%87%E6%88%90%E7%82%BA%E4%B8%80%E5%80%8B-product-owner-85afa9a4e8fb&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;為何選擇成為一個 Product Owner？&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;什麼是-vod&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#什麼是-vod&quot;&gt;什麼是 VOD？&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;在 Media Streaming（媒體串流）的世界裡，有兩種不同的實作類型：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;On-Demand Streaming（隨需串流、隨選、點播）&lt;/li&gt;
&lt;li&gt;Live Streaming（即時串流、直播）&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;引述自 &lt;a href=&quot;https://aws.amazon.com/tw/cloudfront/streaming/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;AWS 官方的說明&lt;/a&gt;：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;使用隨需串流時，影片內容會存放在 Amazon S3 中。&lt;strong&gt;觀眾可以選擇在任何時候觀看影片&lt;/strong&gt;，因此稱為「隨需」串流。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;在即時串流的情況下，您的內容可能是&lt;strong&gt;即時活動&lt;/strong&gt;或&lt;strong&gt;全年無休&lt;/strong&gt;即時頻道交付。前者的例子包括串流體育賽事、頒獎典禮、重大發表和其他高收視率即時活動的廣播公司和內容整合公司。後者的例子包括希望可在不使用第三方分發平台的情況下，封裝線性即時頻道並透過網際網路直接交付給觀眾的工作室、廣播公司和付費電視服務營運商。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;由於 Hahow for Business 的串流媒介是影片（Video），串流類型是 On-Demand Streaming，故稱作 Video on Demand（簡稱 VOD）。&lt;/p&gt;
&lt;p&gt;這也是各大 OTT 平台（例如：Netflix、KKTV、愛奇藝等）主要的串流類型。&lt;/p&gt;
&lt;p&gt;其它像 Twitch、17 之類的直播平台，就屬於第二種類型。當然，也有兩種類型都提供的平台，例如 YouTube。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;300&quot; src=&quot;https://blog.amowu.com/_astro/image2.Di5VdUER_mFoxi.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image2.Di5VdUER_fR4vi.webp 640w, https://blog.amowu.com/_astro/image2.Di5VdUER_mFoxi.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Hahow for Business 的 VOD infrastructure 建置在 AWS 上，架構大致如下：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;圖中虛線內容是本文介紹重點&quot; width=&quot;700&quot; height=&quot;148&quot; src=&quot;https://blog.amowu.com/_astro/image3.xtSZ1_sI_N8BgC.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image3.xtSZ1_sI_Z4qvuU.webp 640w, https://blog.amowu.com/_astro/image3.xtSZ1_sI_N8BgC.webp 700w&quot; /&gt;
&lt;em&gt;圖中虛線內容是本文介紹重點&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;先快速帶過幾個關鍵服務：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;將影片原始檔上傳至 S3 Bucket&lt;/li&gt;
&lt;li&gt;透過 Elastic Transcoder 轉檔成指定格式&lt;/li&gt;
&lt;li&gt;轉完的影片檔輸出至 S3 Bucket&lt;/li&gt;
&lt;li&gt;將輸出的影片部署至 CloudFront（CDN）&lt;/li&gt;
&lt;li&gt;客戶端隨需播放影片&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;這篇文章不會著墨於怎麼架設 AWS 的 VOD，網路上有許多教學文章，也可以查看官方文件。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;最一開始，我們只有在 Elastic Transcoder 上輸出 1080p MP4 格式的影片。&lt;/p&gt;
&lt;p&gt;這會導致兩個問題：「&lt;strong&gt;貴&lt;/strong&gt;」與「&lt;strong&gt;慢&lt;/strong&gt;」。&lt;/p&gt;
&lt;p&gt;對內，串流的檔案越大，需要付出的流量費用就越高。對於口袋不夠深的新創來說，這可能因為商業成本的考量而影響未來的工程決策。&lt;/p&gt;
&lt;p&gt;對外，1080p 大小的 MP4 如果在網路環境較差的情況下，影片容易卡在讀取的環節，嚴重影響使用者體驗。&lt;/p&gt;
&lt;p&gt;基於上述原因，我們決定將串流的格式從 MP4 換成目前主流的 HLS。&lt;/p&gt;
&lt;h2 id=&quot;什麼是-hls&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#什麼是-hls&quot;&gt;什麼是 HLS？&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;在提到 HLS 之前，先解釋一下什麼是 ABS（自適性串流）：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;此技術根據即時檢測用戶的頻寬和 CPU 使用率，調整影片流的品質。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;這需要使用一種可以將單一影片源輸出為多位元速率的編碼器。播放器客戶端依賴可用資源在不同位元速率的流之間切換。結果就是：更少快取、更快的開始播放、為低階和高階連結都提供良好的體驗。 — — 《&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E8%87%AA%E9%81%A9%E6%80%A7%E4%B8%B2%E6%B5%81&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;自適性串流 — 維基百科&lt;/a&gt;》&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;然後回來看 HLS 的介紹：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;HTTP Live Streaming（縮寫是 HLS）是由蘋果公司提出基於 HTTP 的流媒體網絡傳輸協議。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;它的工作原理是把整個流分成一個個小的基於 HTTP 的文件來下載，每次只下載一些。當媒體流正在播放時，客戶端可以選擇從許多不同的備用源中以不同的速率下載同樣的資源，允許流媒體會話適應不同的數據速率。在開始一個流媒體會話時，客戶端會下載一個包含元數據的擴充 M3U（m3u8）播放列表文件，用於尋找可用的媒體流。 — — 《&lt;a href=&quot;https://zh.wikipedia.org/wiki/HTTP_Live_Streaming&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;HTTP Live Streaming — 維基百科&lt;/a&gt;》&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;把它畫成圖之後，感覺大致如下：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;497&quot; src=&quot;https://blog.amowu.com/_astro/image4.DZo_PmFD_2Pk9P.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image4.DZo_PmFD_1Nw4cr.webp 640w, https://blog.amowu.com/_astro/image4.DZo_PmFD_2Pk9P.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;HLS 使用副檔名為 m3u8 的檔案作為索引，檔案切片格式為 TS。支援的客戶端包括 iPhone 和 Android 等裝置。至於不支援的瀏覽器，則是可以透過 &lt;a href=&quot;https://github.com/video-dev/hls.js/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;hls.js&lt;/a&gt; 之類的 library 來實現。同時，HLS 也能夠被用在直播形式。&lt;/p&gt;
&lt;p&gt;其它 ABS 還有諸如 Microsoft 的 Smooth Streaming、Adobe 的 HDS 以及 Google 的 MPEG-DASH。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;使用 Elastic Transcoder 的好處就是，這些主流的轉檔格式，AWS 都已經幫大家建立好 &lt;a href=&quot;https://docs.aws.amazon.com/elastictranscoder/latest/developerguide/system-presets.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Presets&lt;/a&gt; 供直接使用。&lt;/p&gt;
&lt;p&gt;原本以為只要將 1080p MP4 的 Preset 修改成 HLS 的 Preset，就可以收工打卡下班了。結果，並沒有想像中這麼簡單。&lt;/p&gt;
&lt;p&gt;在測試過程中，我們碰到了 MP4 和 HLS 在內容保護機制上，處理方式不同的問題。&lt;/p&gt;
&lt;h2 id=&quot;如何保護串流內容&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#如何保護串流內容&quot;&gt;如何保護串流內容？&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;下圖是擷取自 &lt;a href=&quot;https://youtu.be/zzeho2uLpHM&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;AWS security media delivery&lt;/a&gt;，有關「媒體交付安全性」的其中一張投影片：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;來源 https://youtu.be/zzeho2uLpHM&quot; width=&quot;700&quot; height=&quot;392&quot; src=&quot;https://blog.amowu.com/_astro/image5.qNx0fZXd_Z1UtnO3.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image5.qNx0fZXd_2mH3er.webp 640w, https://blog.amowu.com/_astro/image5.qNx0fZXd_Z1UtnO3.webp 700w&quot; /&gt;
&lt;em&gt;來源 &lt;a href=&quot;https://youtu.be/zzeho2uLpHM&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://youtu.be/zzeho2uLpHM&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;裡面介紹了幾種 AWS 提供的串流內容保護方式，彼此可共存不互斥。由下至上分別是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Watermarking&lt;/li&gt;
&lt;li&gt;Geoblocking&lt;/li&gt;
&lt;li&gt;DRM&lt;/li&gt;
&lt;li&gt;AES encryption&lt;/li&gt;
&lt;li&gt;Token / signed URLs&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;watermarking浮水印&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#watermarking浮水印&quot;&gt;Watermarking（浮水印）&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.aws.amazon.com/zh_tw/elastictranscoder/latest/developerguide/watermarks.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Elastic Transcoder 支援在影片轉檔階段，直接將指定圖片（例如公司 logo）印在影格上&lt;/a&gt;。壞處是可能會遮擋住教學內容（例如 Photoshop 的操作介面）。另一種比較簡單的做法是透過 HTML + CSS，直接在播放器上印出圖片，好處是可受程式控制（例如：位置、透明度等），壞處是幾乎沒有保護效果。&lt;/p&gt;
&lt;h2 id=&quot;geoblocking鎖地區&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#geoblocking鎖地區&quot;&gt;Geoblocking（鎖地區）&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.aws.amazon.com/zh_tw/AmazonCloudFront/latest/DeveloperGuide/georestrictions.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;CloudFront 提供地理限制／封鎖（Geo-restriction）的功能&lt;/a&gt;。透過第三方的 GeoIP 資料庫來判斷使用者的國家／地區，限制資源的存取，準確性約 99.8%。這也是國外 OTT 經常實作的機制，缺點是可以透過 VPN 繞過去。&lt;/p&gt;
&lt;h2 id=&quot;drm&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#drm&quot;&gt;DRM&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;DRM 是 Digital Rights Management（數位版權管理），實作起來最複雜，但因為保護效果最好，所以也是目前大部分 OTT 的主流機制。&lt;/p&gt;
&lt;p&gt;運作原理的部分，因為不是本文重點，就不多贅述。詳見 Elastic Transcoder 的《&lt;a href=&quot;https://docs.aws.amazon.com/zh_tw/elastictranscoder/latest/developerguide/drm.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;數位版權管理&lt;/a&gt;》。&lt;/p&gt;
&lt;p&gt;需要注意的是，任何保護機制都是防君子不防小人，沒有絕對的安全，只有破解困難程度的差別。&lt;/p&gt;
&lt;p&gt;延伸閱讀：&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.techbang.com/posts/71460-doesnt-that-mean-drm-protection-technology-is-great-how-do-those-films-on-pirated-websites-get-them-out-of-movies-on-movie-sites-like-netflix&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;不是說DRM保護技術很厲害？盜版網站上那些影片，是如何從 Netflix 這類影音網站上把影片抓下來的？&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;aes-encryption&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#aes-encryption&quot;&gt;AES Encryption&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.aws.amazon.com/zh_tw/elastictranscoder/latest/developerguide/content-protection.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;使用 Elastic Transcoder 搭配 AWS KMS（Key Management Service）來加密串流媒體檔案的片段，並在播放時進行解密。&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;延伸閱讀：&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://medium.com/starbugs/what-are-encoding-encrypt-and-hashing-4b03d40e7b0c&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;一次搞懂密碼學中的三兄弟 — Encode、Encrypt 跟 Hash&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;token--signedurls&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#token--signedurls&quot;&gt;Token / Signed URLs&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;最後一個，也是 Hahow for Business 目前主要的實作方式 — — Signed URLs 和 Signed Cookies，接下來就重點介紹。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;392&quot; src=&quot;https://blog.amowu.com/_astro/image6.a-FviUYH_1DKf22.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image6.a-FviUYH_QJxgA.webp 640w, https://blog.amowu.com/_astro/image6.a-FviUYH_1DKf22.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;什麼是-signed-urlscookies&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#什麼是-signed-urlscookies&quot;&gt;什麼是 Signed URLs／Cookies？&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.aws.amazon.com/zh_cn/AmazonCloudFront/latest/DeveloperGuide/PrivateContent.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;CloudFront 提供兩種方法訪問 S3 的 Private Content&lt;/a&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Signed URLs&lt;/li&gt;
&lt;li&gt;Signed Cookies&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;第一種是 Signed URLs，它的原理是：後端（Backend）透過 CloudFront 金鑰，向 CloudFront API 請求訪問某筆資源，然後返回一組帶有 Token 參數的網址，具時效性。&lt;/p&gt;
&lt;p&gt;以 Ruby 為例（Hahow for Business 的後端是 Ruby on Rails），假如我們想訪問 &lt;code&gt;url=https://cdn.business.hahow.in/foo.mp4&lt;/code&gt; 的檔案，程式碼大致如下：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;signer&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Aws&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;CloudFront&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;UrlSigner&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;key_pair_id:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;cf-keypair-id&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;private_key_path:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;./cf_private_key.pem&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;signed_url&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; signer.&lt;/span&gt;&lt;span&gt;signed_url&lt;/span&gt;&lt;span&gt;(url,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;expires:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;hour&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;after&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;這時候得到的 &lt;code&gt;signed_url&lt;/code&gt; 大概會長這樣：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;https://cdn.business.hahow.in/foo.mp4?Expires=...&amp;amp;Signature=...&amp;amp;Key-Pair-Id=...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;接著，前端就可以使用這組 Signed URL 去訪問資源了，如下圖：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;142&quot; src=&quot;https://blog.amowu.com/_astro/image7.Cb53QGMK_tWfsU.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image7.Cb53QGMK_29p9By.webp 640w, https://blog.amowu.com/_astro/image7.Cb53QGMK_tWfsU.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;一般來說，如果訪問的是&lt;strong&gt;單筆資源&lt;/strong&gt;，例如這個例子中的 MP4，那麼用 Signed URLs 就可以。&lt;/p&gt;
&lt;p&gt;但如果要訪問 HLS 這類&lt;strong&gt;多筆資源&lt;/strong&gt;，Signed URLs 就會碰到問題。&lt;/p&gt;
&lt;p&gt;如下圖所示，雖然一開始的 Master Playlist（詳見前面的 HLS 圖）還可以透過 Signed URL 訪問，但由於接下來前端會解析索引裡面的其它連結（m3u8 或 ts）然後直接送出請求，但因為不是 Signed URL，導致訪問會被 CloudFront 拒絕。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;437&quot; src=&quot;https://blog.amowu.com/_astro/image8.BN2281zv_qG7SQ.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image8.BN2281zv_ZmviDK.webp 640w, https://blog.amowu.com/_astro/image8.BN2281zv_qG7SQ.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;雖然期間也找到幾種解法，例如這篇「&lt;a href=&quot;https://qiita.com/kinocoffeeblack/items/e1ece9d47ea948ef6708&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;使用 Lambda 替所有連結都簽名&lt;/a&gt;」的奇耙技巧⋯⋯。&lt;/p&gt;
&lt;p&gt;但最後還是決定改用 Signed Cookies。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;事實上，官方文件也是這麼建議的：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;在以下案例使用已簽章的 URL：&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;⋯（略）&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;在以下案例使用已簽章的 Cookie：&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;您想要提供對多個限制檔案的存取，例如，HLS 格式視訊的所有檔案或網站中訂閱者區域的所有檔案。&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;⋯（略）&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;— — 《&lt;a href=&quot;https://docs.aws.amazon.com/zh_tw/AmazonCloudFront/latest/DeveloperGuide/private-content-choosing-signed-urls-cookies.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;在已簽章的 URL 和已簽章的 Cookie 之間進行選擇&lt;/a&gt;》&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Signed Cookies 跟 Signed URLs 原理類似，都是後端透過 CloudFront API 取得 Token，&lt;strong&gt;差別在於 Signed Cookies 可以同時指定多筆資源&lt;/strong&gt;，然後返回一組具時效性的 Cookies 儲存在客戶端上。如果接下來請求的 m3u8 或 ts 在允許範圍內，那麼只要帶上這組 Cookies 就都可以順利訪問。&lt;/p&gt;
&lt;p&gt;同樣以 Ruby 為例，假如想訪問 &lt;code&gt;url=https://cdn.business.hahow.in/{video-id}/index.m3u8&lt;/code&gt;，程式碼大概會長這樣：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;signer&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Aws&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;CloudFront&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;CookieSigner&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;key_pair_id:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;cf-keypair-id&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;private_key_path:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;./cf_private_key.pem&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;signed_cookies&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; signer.&lt;/span&gt;&lt;span&gt;signed_cookie&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;https://cdn.business.hahow.in&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;policy:&lt;/span&gt;&lt;span&gt; policy.&lt;/span&gt;&lt;span&gt;to_json&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;需要注意的是，與 &lt;code&gt;signed_url()&lt;/code&gt; API 不同，&lt;code&gt;signed_cookie()&lt;/code&gt; 第一個參數傳的不是資源的完整 URL，而是 Origin（&lt;code&gt;https://cdn.business.hahow.in&lt;/code&gt;）。&lt;/p&gt;
&lt;p&gt;取而代之的是，在參數 &lt;code&gt;policy&lt;/code&gt; 指定相關資源的 URL（wildcard 形式）以及時效：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;policy&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&apos;Statement&apos;&lt;/span&gt;&lt;span&gt; =&amp;gt; [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;&apos;Resource&apos;&lt;/span&gt;&lt;span&gt; =&amp;gt; &lt;/span&gt;&lt;span&gt;&quot;https://cdn.business.hahow.in/{video-id}/*&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;&apos;Condition&apos;&lt;/span&gt;&lt;span&gt; =&amp;gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;          &lt;/span&gt;&lt;span&gt;&quot;DateLessThan&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;              &lt;/span&gt;&lt;span&gt;&apos;AWS:EpochTime&apos;&lt;/span&gt;&lt;span&gt; =&amp;gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;hour&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;from_now&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;to_i&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;這樣即便前端解析索引之後的連結是 &lt;code&gt;https://cdn.business.hahow.in/{video-id}/{bitrate}.m3u8&lt;/code&gt; 或是 &lt;code&gt;https://cdn.business.hahow.in/{video-id}/{bitrate}/{segment}.ts&lt;/code&gt;，只要請求帶上 &lt;code&gt;sigend_cookies&lt;/code&gt; 都能順利訪問。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;754&quot; src=&quot;https://blog.amowu.com/_astro/image9.DzwDF0UF_2hior1.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image9.DzwDF0UF_ZerXVa.webp 640w, https://blog.amowu.com/_astro/image9.DzwDF0UF_2hior1.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;其中，由於我們使用了 Cookie 機制，如果前端要把請求帶上 Cookie 發送，記得在 AJAX 中打開 &lt;code&gt;withCredentials&lt;/code&gt; 屬性。&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;xhr&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;XMLHttpRequest&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;xhr.withCredentials &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;如果前端使用的是 &lt;a href=&quot;https://github.com/video-dev/hls.js&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;hls.js&lt;/a&gt;，則可直接透過 &lt;a href=&quot;https://github.com/video-dev/hls.js/blob/master/docs/API.md&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;xhrSetup&lt;/a&gt; API 簡單地實現這件事。&lt;/p&gt;
&lt;p&gt;否則，即便 CloudFront 返回且儲存 Signed Cookies，瀏覽器也不會發送。&lt;/p&gt;
&lt;p&gt;另外，需要注意的是，如果要發送 Cookie，Server 回應 Header 的 &lt;code&gt;Access-Control-Allow-Origin&lt;/code&gt; 就不能設為 &lt;code&gt;*&lt;/code&gt;（&lt;a href=&quot;https://docs.aws.amazon.com/zh_tw/AmazonS3/latest/dev/cors.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;可以在 S3 的 CORS 設定&lt;/a&gt;），必須指定明確、與請求網頁一致的 Origin。&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;CORSRule&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;AllowedOrigin&lt;/span&gt;&lt;span&gt;&amp;gt;https://business.hahow.in&amp;lt;/&lt;/span&gt;&lt;span&gt;AllowedOrigin&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;CORSRule&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;同時，Cookie 依然遵循同源政策（Same-origin Policy），只有用 Server Domain 設置的 Cookie 才會發送，其它 Domain 的 Cookie 並不會發送。&lt;/p&gt;
&lt;p&gt;也就是說，如果我們設置 Cookie 的 domain 是 &lt;code&gt;.business.hahow.in&lt;/code&gt;，但是訪問資源的連結是 &lt;code&gt;*.cloudfront.net&lt;/code&gt;，那麼 Cookie 就無法被發送。所以 Hahow for Business 也趁著這次更新順便把 CloudFront 的 CNAME 掛上 &lt;code&gt;cdn.business.hahow.in&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;延伸閱讀：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.ruanyifeng.com/blog/2016/04/same-origin-policy.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;浏览器同源政策及其规避方法&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.ruanyifeng.com/blog/2016/04/cors.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;跨域资源共享 CORS 详解&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;成果&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#成果&quot;&gt;成果&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;回到最初我們想要解決的兩大問題：「貴」與「慢」。&lt;/p&gt;
&lt;p&gt;好巧不巧，就在 HLS 上線之後沒多久，我們的其中一間企業客戶，就突擊來了個動員壓測，好在事後的問卷調查取得了 89% 以上「順暢」的成績。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;797&quot; src=&quot;https://blog.amowu.com/_astro/image10.Dal7Bv6U_OFuvA.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image10.Dal7Bv6U_1HeIaG.webp 640w, https://blog.amowu.com/_astro/image10.Dal7Bv6U_OFuvA.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;至於帳單的部分，相比前一個月的金額，CloudFront 的流量費用大幅下降了 68%。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;573&quot; height=&quot;308&quot; src=&quot;https://blog.amowu.com/_astro/image11.CfbzQ8VV_2a1r62.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image11.CfbzQ8VV_2a1r62.webp 573w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;整體來說，這次的改動算是達成了目標，既「開源」又「節流」，一舉數得。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;291&quot; height=&quot;219&quot; src=&quot;https://blog.amowu.com/_astro/image12.no6irS1F_nLGsx.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image12.no6irS1F_nLGsx.webp 291w&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;結論&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#結論&quot;&gt;結論&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;總結一下本文的重點：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;首先，介紹了兩種串流類型，分別是 On-Demand Streaming（點播）和 Live Streaming（直播），Hahow for Business 屬於前者的 Video on Demand（隨選影片），簡稱 VOD。&lt;/li&gt;
&lt;li&gt;VOD 的格式千百種，其中 HLS 相較傳統 MP4 的優勢在於，HLS 的檔案被切分成許多片段，不需要一次載完整部影片才開始播放。並且會根據使用者的網速，切換至適合的解析度來源，提高使用者的觀看體驗。&lt;/li&gt;
&lt;li&gt;接著，介紹了幾種保護串流內容的方法，分別有：浮水印、鎖地區、DRM、加密，以及 Hahow for Business 在使用的 Signed URLs 和 Signed Cookies。&lt;/li&gt;
&lt;li&gt;最後，解釋了 Signed URLs 和 Signed Cookies 的原理以及使用場景，前者適合 MP4 等單筆資源，後者則是適用於 HLS 這類多筆資源。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;後記&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#後記&quot;&gt;後記&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;當然，Hahow for Business 的 VOD 還有許多可以精進的地方，例如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;實作更加安全的 DRM 機制&lt;/li&gt;
&lt;li&gt;遷移至 &lt;a href=&quot;https://aws.amazon.com/tw/mediaconvert/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;AWS Elemental MediaConvert&lt;/a&gt; 系列（取代 Elastic Transcoder）&lt;/li&gt;
&lt;li&gt;S3 &amp;amp; CloudFront 的備援方案等⋯&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;文末，筆者想拋出一個議題與大家一起思考。&lt;/p&gt;
&lt;p&gt;可能會有人好奇，Hahow for Business 的 VOD 為什麼不直接用 SaaS？&lt;/p&gt;
&lt;p&gt;這其實也是技術團隊在做技術選型時，常碰到的決策問題：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Self-hosted Service&lt;/strong&gt; vs &lt;strong&gt;Managed Service&lt;/strong&gt; vs &lt;strong&gt;SaaS&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;467&quot; src=&quot;https://blog.amowu.com/_astro/image13.CgtuAl3-_DUOXu.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image13.CgtuAl3-_Z1as19W.webp 640w, https://blog.amowu.com/_astro/image13.CgtuAl3-_DUOXu.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;延伸閱讀：&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.davidh83110.com/%E6%8A%80%E8%A1%93%E7%B0%A1%E4%BB%8B/2020/03/05/saas-self-hosted.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;SaaS/Managed Services &amp;amp; Self-Hosted 衍伸思考&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;目前 &lt;a href=&quot;https://hahow.in/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Hahow&lt;/a&gt;（for Consumer）的 VOD 就是使用 &lt;a href=&quot;https://vimeo.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Vimeo&lt;/a&gt; 這個 SaaS，雖然好用，但也存在一些問題。&lt;/p&gt;
&lt;p&gt;舉幾個過去我們碰到的例子：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Vimeo 提供的播放器無法實現「劇院模式（視窗全螢幕）」，後來改用開源播放器 + Vimeo API 解決&lt;/li&gt;
&lt;li&gt;影片卡頓問題長期佔據客單榜首，後來 Vimeo 將 CDN 換成 Akamai 之後才有改善&lt;/li&gt;
&lt;li&gt;服務掛掉也無能為力，以至於必須準備另一個更貴的 SaaS 備用方案 &lt;a href=&quot;https://wistia.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Wistia&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;這讓我想起曾經在 &lt;a href=&quot;https://blog.starrocket.io/posts/newsletter-2020-04-15/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Star Rocket Blog&lt;/a&gt; 看到的一段話：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;隨著 SaaS 的興起，許多在產品開發及營運上所需的工具，舉凡資料庫管理、專案管理軟體、CRM 等，其實都能在網路上找到多個能直接付費使用的外部服務。但這麼做雖然能大幅省去開發新工具的時間與人力，但也同時得承受像是外部服務突然終止、費用水漲船高、服務的功能沒有隨著公司的成長與時俱進等各種風險。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;曾在 Uber、Stripe 擔任資深工程師、現任冥想 app Calm 的 CTO Will Larson，在本文中分享他會如何從風險、價值與成本這三個角度去思考「自行開發 vs. 付費購買」的問題。他也表示：「&lt;strong&gt;作為一間科技公司，若在你的核心競爭力以外利用到這些外部服務，那通常能為公司帶來極高的價值；否則它們往往只會拖慢公司的發展。&lt;/strong&gt;」&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;— — 《&lt;a href=&quot;https://lethain.com/build-vs-buy/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Build versus buy&lt;/a&gt;》&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;這也是筆者喜歡和面試者討論的議題，沒有絕對的答案，大家不妨也可以思考看看～&lt;/p&gt;
&lt;p&gt;最後，Hahow 正在招募「&lt;a href=&quot;https://hahow.breezy.hr/p/0af680796203-zi-shen-hou-duan-gong-cheng-shi-senior-backend-engineer-b2b-ruby-on-rails&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;資深後端工程師&lt;/a&gt;」，歡迎有興趣的朋友加入我們！&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;368&quot; src=&quot;https://blog.amowu.com/_astro/image14.BuUA6W93_q9ATF.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image14.BuUA6W93_aj9bM.webp 640w, https://blog.amowu.com/_astro/image14.BuUA6W93_q9ATF.webp 700w&quot; /&gt;&lt;/p&gt;</content:encoded><category>Tech</category></item><item><title>工程師的世界越複雜，你的世界就越簡單</title><link>https://blog.amowu.com/posts/2020-07-05-engineering/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2020-07-05-engineering/</guid><description>藝術家、科學家、發明家還有工匠，相比工程師有什麼不同之處？</description><pubDate>Sun, 05 Jul 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;藝術家、科學家、發明家還有工匠，相比工程師有什麼不同之處？&lt;/p&gt;
&lt;p&gt;前陣子， &lt;a href=&quot;https://redis.io/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Redis&lt;/a&gt; 的作者 antirez &lt;a href=&quot;http://antirez.com/news/133&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;宣佈&lt;/a&gt; 退出維護團隊。理由大致如下：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;*我寫程式是為了表達自己，我認為自己寫的東西是一件藝術品，而不僅僅是為了完成任務而寫的有用的東西。如果我寫的東西有用，那只是機緣巧合，因為我的首要目標是做出一些美的東西。****我寧願被人記住是一個糟糕的藝術家，也不要是一個好的工程師。***&lt;em&gt;Redis 現在需要的，較多的是專案維護，而不是表達自己。這並不是我想做的。&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;這讓我想起曾經讀過的《 &lt;a href=&quot;https://books.google.com.tw/books?id=spfTBLWvVxcC&amp;amp;lpg=PP1&amp;amp;hl=zh-TW&amp;amp;pg=PP1&amp;amp;q&amp;amp;f=false&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Hackers &amp;amp; Painters&lt;/a&gt; 》（譯：駭客與畫家）一書提到：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;應該把駭客和畫家當作同一種人看待，他們都是創作者。&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;駭客（hacker）追求的三個特色：好玩、高智商和探索精神，而不是實用性和金錢。&lt;/p&gt;
&lt;p&gt;&lt;em&gt;（註：這裡的 hacker 是指那些最優秀的軟體工程師，惡意入侵電腦系統的人應該被稱為 cracker）&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https:blog.amowu.com/posts/2013-10-01-hackers-and-painters/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;重點筆記《Hackers and Painters》&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;這看起來很美好，我也嚮往這樣的「Hacker 精神」。但隨著工作的時間久了，我開始覺得工程師與藝術家還是有本質上的不同。&lt;/p&gt;
&lt;p&gt;最近聽了《 &lt;a href=&quot;https://www.amazon.com/Engineering-Beginners-Guide-Guides/dp/1851686622&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Engineering: A Beginner’s Guide&lt;/a&gt;》這本書的 &lt;a href=&quot;https://www.igetget.com/eBook/%E4%BA%BA%E4%BA%BA%E9%83%BD%E8%AF%A5%E6%87%82%E7%9A%84%E5%B7%A5%E7%A8%8B%E5%AD%A6?param=mZBf1sGf9&amp;amp;token=XOnaYG1qlM7amvGYerDZOy9JVnXL40BpxO3Bkp1NKxoRdb86P2Q5AzgEj9vE5rDo&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;得到解讀版本&lt;/a&gt; ，裡面提到了一些角色和工程師的比較，覺得有趣，就摘錄出來。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;313&quot; height=&quot;500&quot; src=&quot;https://blog.amowu.com/_astro/image1.B1OadP1Y_Byn3j.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image1.B1OadP1Y_Byn3j.webp 313w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;什麼是工程（Engineering）？&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;工程學有很多個分支，例如：土木工程、機械工程、電機工程、材料工程、生物工程以及電腦工程等等。英國土木工程師協會最早將工程學定義為「&lt;strong&gt;&lt;strong&gt;為了人類的使用和便利而引導自然中巨大能源的技術&lt;/strong&gt;&lt;/strong&gt;」。&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;套用在我們這行的話，軟體工程師掌握的是「透過光電這些自然能源，提供人類能夠便利操作資訊的技術（Information Technology）」。&lt;/p&gt;
&lt;p&gt;那麼，科學家、發明家還有工匠，這些角色和工程師比較，有什麼不同之處呢？&lt;/p&gt;
&lt;h2 id=&quot;工程師和科學家有什麼不同&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#工程師和科學家有什麼不同&quot;&gt;工程師和科學家有什麼不同？&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;我們常常把「理工科」放在一起說，理科和工科，其實就是自然科學和工程學。 &lt;strong&gt;科學家更擅長發現，工程師更擅長實現。&lt;/strong&gt; 科學家主要的工作，是探索「是什麼」，而工程師思考的是「能夠做什麼」。科學家們發現未知之事，工程師們創造未有之物。這兩者缺一不可。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;不少 IT 公司都設有科學家，例如 AWS 的 CTO Werner Vogels 過去就擔任過相關職位&quot; width=&quot;700&quot; height=&quot;334&quot; src=&quot;https://blog.amowu.com/_astro/image2.DwDt9-fh_Z1EXfzf.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image2.DwDt9-fh_Z2tH129.webp 640w, https://blog.amowu.com/_astro/image2.DwDt9-fh_Z1EXfzf.webp 700w&quot; /&gt;
&lt;em&gt;不少 IT 公司都設有科學家，例如 AWS 的 CTO Werner Vogels 過去就擔任過相關職位&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;工程師和發明家有什麼不同&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#工程師和發明家有什麼不同&quot;&gt;工程師和發明家有什麼不同？&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;發明家關注的是發明（這不是廢話？），工程師關注的是創新。發明是「第一次出現新產品或新工藝的想法」，創新是「在實踐中第一次嘗試這種想法」。 &lt;strong&gt;發明家是那個提出可能性的人，工程師是那群真正動手，把可能性轉化為現實的人。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;現代已經沒有像愛迪生之類的發明家，能讓我聯想到形象最接近的大概就是 Elon Musk&quot; width=&quot;700&quot; height=&quot;366&quot; src=&quot;https://blog.amowu.com/_astro/image3.D_d4dbdg_iBJz.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image3.D_d4dbdg_ZNi3IK.webp 640w, https://blog.amowu.com/_astro/image3.D_d4dbdg_iBJz.webp 700w&quot; /&gt;
&lt;em&gt;現代已經沒有像愛迪生之類的發明家，能讓我聯想到形象最接近的大概就是 Elon Musk&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;工程師和工匠有什麼不同&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#工程師和工匠有什麼不同&quot;&gt;工程師和工匠有什麼不同？&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;最早一批工程師，都是從工匠變化而來的。工匠是把普通人培養成手藝人，來完成複雜精細的工作，而工程師思考的，是怎麼讓普通人不用學習手藝，也能完成複雜的工作。需要多年經驗才能完成的工作，那是手藝。而建立一套標準化的流程，讓門外漢也能完成，那就是工藝。 &lt;strong&gt;工匠傳承手藝，工程師把手藝變成工藝。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;大家喜愛日本製造、德國製造或是瑞士製造的精品，這背後都是工匠精神的體現，我也曾借此在《殺雞用牛刀》一文聊過 Google 和 Facebook 兩家截然不同的工程文化。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.amowu.com/posts/2019-01-21-weekly-006/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;殺雞用牛刀&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;綜上所述，工程師的不同之處在於：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;工程師信仰科學，但是更關注怎樣借助科學創造一個可能的世界；他們總是在創新，但是更關注怎樣創新才能讓想法落地；他們重視手藝，但是更關注怎麼把手藝變成標準化的工藝。&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;工程師所有工作的最終目標： &lt;strong&gt;把全部的複雜都封裝起來，只交給你一個簡單的界面。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;回到開頭的話題，我認為工程師與藝術家的最大不同處就在於，工程的複雜度遠高於一件藝術品，因為這種複雜，反而讓工程學的世界，很難講出動人的故事。我們都期待能出現一位像藝術家一樣的英雄來創造奇跡。可是在真實的世界裡，那些好用的產品背後，往往都有一支工程師團隊在默默努力。&lt;/p&gt;
&lt;p&gt;你會看到一群工程師在開發同一個產品，但你不會看到一群畫家在畫同一幅畫。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;一個人走得快，一群人走得遠。&lt;/strong&gt;&lt;/p&gt;</content:encoded><category>Tech</category></item><item><title>Hahow 為什麼沒有 iOS App？</title><link>https://blog.amowu.com/posts/2020-03-18-hahow-ios-app-18/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2020-03-18-hahow-ios-app-18/</guid><description>大綱</description><pubDate>Wed, 18 Mar 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;大綱&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#大綱&quot;&gt;大綱&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;前言&lt;/li&gt;
&lt;li&gt;Hahow 為什麼沒有 iOS App？&lt;/li&gt;
&lt;li&gt;為什麼有了「App 內購買」還是被拒絕？&lt;/li&gt;
&lt;li&gt;為什麼 Hahow 要使用「消耗型項目」IAP？&lt;/li&gt;
&lt;li&gt;為什麼 Udemy 可以使用「消耗型項目」IAP？&lt;/li&gt;
&lt;li&gt;Hahow iOS App 接下來的計畫？&lt;/li&gt;
&lt;li&gt;結語&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;2022/07/20 更新：Hahow 已經有自己的 iOS App 囉 🎊&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://apps.apple.com/tw/app/hahow-%E5%A5%BD%E5%AD%B8%E6%A0%A1-%E6%9C%80%E6%9C%89%E8%B6%A3%E7%9A%84%E7%B7%9A%E4%B8%8A%E8%AA%B2%E7%A8%8B%E5%B9%B3%E5%8F%B0/id1529044948&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Hahow 好學校 - 一站式跨域人才學習入口&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;前言&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#前言&quot;&gt;前言&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;最近看到一篇 The Verge &lt;a href=&quot;https://www.theverge.com/2020/2/19/21144053/apple-publication-book-app-store-confidential-tom-sadowski&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;報導&lt;/a&gt;，Apple 的律師要求 App Store 的前任經理停止發行《App Store Confidential》這本書，因為此書透露了「重大的商業機密」，違反了員工與公司簽訂的合約。&lt;/p&gt;
&lt;p&gt;但是此書的作者 Sadowski 表示，這本書的很多內容其實是大家都知道的，例如：應用程式必須將使用者轉化為 付費客戶。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;660&quot; height=&quot;368&quot; src=&quot;https://blog.amowu.com/_astro/image1.CXiQsTQ6_wJLDj.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image1.CXiQsTQ6_ZQlzKa.webp 640w, https://blog.amowu.com/_astro/image1.CXiQsTQ6_wJLDj.webp 660w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;眾所周知，Apple 的 App Store 平台審核機制封閉，很多東西是他們說的算。&lt;/p&gt;
&lt;p&gt;作為一位曾經參與 Hahow App 的開發人員，看到這一篇報導感觸很深，滿肚子苦水不吐不快。&lt;/p&gt;
&lt;p&gt;許多 Hahow 的使用者常常詢問，為什麼 Android 的 App 已經上架這麼久了，iOS App 卻還看不見影呢？&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;352&quot; src=&quot;https://blog.amowu.com/_astro/image2.fDFBsZcu_ZCsTG8.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image2.fDFBsZcu_15WqJI.webp 640w, https://blog.amowu.com/_astro/image2.fDFBsZcu_ZCsTG8.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;hahow-為什麼沒有-iosapp&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#hahow-為什麼沒有-iosapp&quot;&gt;Hahow 為什麼沒有 iOS App？&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;其實 iOS App 一直都是與 Android App 同步開發的。&lt;/p&gt;
&lt;p&gt;最早可以回朔到 2017 年 10 月，當初 Web 使用者不斷反應，希望可以在手機上，下載 Hahow 的課程，方便通勤時間也能離線觀看。&lt;/p&gt;
&lt;p&gt;於是我自己利用公司讀書會的機會，嘗試使用 &lt;a href=&quot;https://reactnative.dev/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;React Native&lt;/a&gt; 開發了一個 Hahow App 的原型（Prototype），簡單演示了「下載離線觀看」和「通知」兩個手機上才能做到的功能。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;529&quot; src=&quot;https://blog.amowu.com/_astro/image3.Bb2sdHWX_11CnjF.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image3.Bb2sdHWX_WsFO9.webp 640w, https://blog.amowu.com/_astro/image3.Bb2sdHWX_11CnjF.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;也因此，成功說服了幾位頭頭，於 2018 年 2 月正式投入開發 Hahow App。&lt;/p&gt;
&lt;p&gt;並於同年 6 月，順利在 Google Play 上架 Android App。&lt;/p&gt;
&lt;p&gt;有關於我們如何用 &lt;a href=&quot;https://expo.io/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Expo&lt;/a&gt; 這個 React Native SDK 開發 App、踩過哪些坑，以及專案的 Scrum 怎麼跑之類的，會再另外找時間分享一篇《Hahow 如何開發 Android App》文章，這裡就先不多贅述。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;483&quot; src=&quot;https://blog.amowu.com/_astro/image4.BIz8KTDi_Z1R5L1s.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image4.BIz8KTDi_Z6x6Wq.webp 640w, https://blog.amowu.com/_astro/image4.BIz8KTDi_Z1R5L1s.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;就在第一版 Android App 上架 Google Play 的同時，我們也將 iOS App 送審了 App Store。&lt;/p&gt;
&lt;p&gt;但是，由於第一版的 Hahow App 功能還很陽春，只提供了登入、下載已購課程和離線觀看，以至於 Apple 以「付費內容需要在 App 內提供付費管道」的理由拒絕我們上架。&lt;/p&gt;
&lt;p&gt;隨著每次發布一個 Android 新版本，送審 App Store 的期間，Apple 都會以各種理由拒絕你，例如：需要提供完整的註冊登入系統、首頁、瀏覽以及搜尋課程的頁面，還有必須訪客在不註冊登入的前提下，提供試用帳號之類的。&lt;/p&gt;
&lt;p&gt;這導致我們的 Roadmap 一直因為 Apple 的要求而不斷修正，必須放棄開發很多原本只有在手機 App 才能提供的功能體驗，例如：聽課和通知。&lt;/p&gt;
&lt;p&gt;經過幾個月的折騰之後，我們終於完成了 Apple 所要求的最後一項功能 ── App 內購買（In-App Purchase，以下簡稱 IAP）。&lt;/p&gt;
&lt;p&gt;歡欣鼓舞之際，本以為這次終於可以在 App Store 上看見 Hahow App 成功上架的畫面。然而，最後 Apple 還是拒絕了我們的送審。&lt;/p&gt;
&lt;h2 id=&quot;為什麼有了app-內購買還是被拒絕&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#為什麼有了app-內購買還是被拒絕&quot;&gt;為什麼有了「App 內購買」還是被拒絕？&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;簡而言之，Apple 以 Hahow App 的 IAP 商品不適用「消耗型項目」為理由，拒絕了我們。&lt;/p&gt;
&lt;h3 id=&quot;什麼是消耗型項目iap&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#什麼是消耗型項目iap&quot;&gt;什麼是「消耗型項目」IAP？&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;基本上，Apple IAP 提供&lt;a href=&quot;https://developer.apple.com/in-app-purchase/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;四種方式&lt;/a&gt;在 iOS App 內付費：消耗型、非消耗型、自動續期訂閱，以及非續期訂閱，後面兩種屬於訂閱制的範圍，這裡不討論，我們來看前兩種的說明：&lt;/p&gt;
&lt;h3 id=&quot;消耗型項目consumable&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#消耗型項目consumable&quot;&gt;消耗型項目（Consumable）&lt;/a&gt;&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;使用者可以購買各種消耗型項目（例如遊戲中的生命或寶石）以繼續 app 內的進行。消耗型項目只可使用一次，使用之後即失效，必須再次購買。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;需要注意的是，這類型的 IAP，可以重複購買，但無法提供「恢復購買」的選項。&lt;/p&gt;
&lt;h3 id=&quot;非消耗型項目non-consumable&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#非消耗型項目non-consumable&quot;&gt;非消耗型項目（Non-Consumable）&lt;/a&gt;&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;使用者可購買非消耗型項目以提升 app 內的功能。非消耗型項目只需購買一次，不會過期（例如修圖 app 中的其它濾鏡）。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;與消耗型 IAP 相反，非消耗型項目只能購買一次，並且可以「恢復購買」，因為購買的資料是儲存在 Apple 的資料庫。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;416&quot; src=&quot;https://blog.amowu.com/_astro/image5.DwqrS_5-_ge69Q.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image5.DwqrS_5-_a5b0I.webp 640w, https://blog.amowu.com/_astro/image5.DwqrS_5-_ge69Q.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;為什麼-hahow-要使用消耗型項目iap&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#為什麼-hahow-要使用消耗型項目iap&quot;&gt;為什麼 Hahow 要使用「消耗型項目」IAP？&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;假設 Hahow 有兩堂 100 元的課程 A 和 B，如果它們共用同一組 100 元的「非消耗型項目」IAP，那麼當使用者買了 A，那麼他就無法再購買 B，因為「非消耗型」IAP「只能購買一次」。&lt;/p&gt;
&lt;p&gt;除非分別為 A 和 B 都各自建立一組 100 元的非消耗型 IAP，但是 Hahow 有 300 堂以上的線上課程，如果每堂課程都需要建立一組「非消耗型」IAP，那麼投入的人力成本會上升（Apple 沒有提供 API 自動化，建立 IAP 需要手動在後台操作）。&lt;/p&gt;
&lt;p&gt;於是我們參考了同是線上教育平台 ── &lt;a href=&quot;https://www.udemy.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Udemy&lt;/a&gt; 的 iOS App 作法，選擇「可以重複購買」的「消耗型項目」IAP。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;457&quot; src=&quot;https://blog.amowu.com/_astro/image6.DoDPjyaY_1vKW3n.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image6.DoDPjyaY_Z1N0BF6.webp 640w, https://blog.amowu.com/_astro/image6.DoDPjyaY_1vKW3n.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;但是 Apple 認為我們的「課程」不適用「消耗型項目」IAP，因為「課程」應該要「可以恢復購買」。&lt;/p&gt;
&lt;h2 id=&quot;為什麼-udemy-可以使用消耗型項目iap&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#為什麼-udemy-可以使用消耗型項目iap&quot;&gt;為什麼 Udemy 可以使用「消耗型項目」IAP？&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;當初就是因為 Udemy 使用這個方式操作，所以我們才認為可行。但是 Apple 的審核人員卻以「因為 Udemy 的課程&lt;strong&gt;超過 1000 堂以上&lt;/strong&gt;，所以特例為他們開放」這個令人傻眼的理由，回絕了我們的申訴。&lt;/p&gt;
&lt;p&gt;我並沒有在 &lt;a href=&quot;https://developer.apple.com/app-store/review/guidelines/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;App Store 審核指南&lt;/a&gt; 上看見有這條規則，所以這就是為什麼我在文章開頭提到的「App Store 平台審核機制封閉，很多東西他們說的算」。&lt;/p&gt;
&lt;h2 id=&quot;hahow-ios-app接下來的計畫&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#hahow-ios-app接下來的計畫&quot;&gt;Hahow iOS App 接下來的計畫？&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Apple 的審核人員給了我們兩個建議：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;為每一堂課建立「非消耗型項目」IAP&lt;/li&gt;
&lt;li&gt;使用「消耗型項目」IAP 建立「虛擬點數」的購買機制，也是目前的主流做法&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;遺憾的是，2019 年 5 月，我們決定將人力投入到其它的 Hahow 專案，停止 Hahow App 的後續開發及維護 。&lt;/p&gt;
&lt;h2 id=&quot;結語&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#結語&quot;&gt;結語&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;很多東西到最後已經不是技術的問題，Code 能解決的都是小問題。&lt;/p&gt;
&lt;p&gt;最後，我們公司正在招募 &lt;a href=&quot;https://hahow.breezy.hr/p/dc7bdec5debb-ios-gong-cheng-shi-ios-engineer&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;iOS Engineer&lt;/a&gt; 以及 &lt;a href=&quot;https://hahow.breezy.hr/p/61c55e37ce22-android-gong-cheng-shi-android-engineer&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Android Engineer&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;如果你熟悉 App Store Review Guidelines，跌過無數次 Apple 裏規則的坑，擁有豐富的送審經驗，擅長與 Apple 審核人員打交道。那麼，我們非常需要你這樣的人才。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;549&quot; height=&quot;276&quot; src=&quot;https://blog.amowu.com/_astro/image7.BAOLh1SI_Z1POoDp.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image7.BAOLh1SI_Z1POoDp.webp 549w&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;參考資料&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#參考資料&quot;&gt;參考資料&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.theverge.com/2020/2/19/21144053/apple-publication-book-app-store-confidential-tom-sadowski&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Apple tries to halt publication of App Store book allegedly containing company secrets&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.apple.com/in-app-purchase/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;In-App Purchase&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.apple.com/cn/in-app-purchase/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;App 内购买项目&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.36kr.com/p/5293547?ktm_source=feed&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;最前线｜苹果禁售前员工新书，称其透露了App Store的“重大商业机密”&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.techbang.com/posts/76318-former-apple-employee-sits-on-new-book-on-app-store-secrets-apple-jumps-to-ask-for-ban-book-reveals-app-store-major-trade-secrets&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;蘋果前員工出了本App Store的新書，蘋果抓狂要求禁售：書中「透露 App Store 重大商業機密」&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://youdownloadtheappanditdoesntwork.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;You Download the App and it Doesn’t Work&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>Tech</category></item><item><title>無限環境（ㄧ）Netlify Deploy Previews</title><link>https://blog.amowu.com/posts/2020-03-01-netlify-deploy-previews/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2020-03-01-netlify-deploy-previews/</guid><description>大綱</description><pubDate>Sun, 01 Mar 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;大綱&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#大綱&quot;&gt;大綱&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;什麼是「無限環境」？&lt;/li&gt;
&lt;li&gt;什麼是 Netlify Deploy Previews？&lt;/li&gt;
&lt;li&gt;快速入門指南&lt;/li&gt;
&lt;li&gt;總結&lt;/li&gt;
&lt;li&gt;附錄&lt;/li&gt;
&lt;li&gt;參考資料&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;什麼是無限環境&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#什麼是無限環境&quot;&gt;什麼是「無限環境」？&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;大家常用的 &lt;a href=&quot;https://guides.github.com/introduction/flow/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;GitHub flow&lt;/a&gt; 其實有一個常常被忽略的重點 — — 合併前部署。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;270&quot; src=&quot;https://blog.amowu.com/_astro/image1.DyyWJYFP_2uLk59.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image1.DyyWJYFP_1FDjGw.webp 640w, https://blog.amowu.com/_astro/image1.DyyWJYFP_2uLk59.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;我自己在前端開發的 &lt;a href=&quot;https://github.com/features/code-review/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Code Review&lt;/a&gt; 上，常常碰到下列兩個問題：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;PR 合併、部署到 staging 或 production 環境之後，才發現程式碼有問題&lt;/li&gt;
&lt;li&gt;為了避免第一個問題發生，就必須先 checkout PR 到 local 環境，然後經過一系列繁瑣的步驟跑起來，最後才能開始驗收和測試&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;為了解決上述問題，就需要引入無限環境的概念。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;所謂的無限環境，就是&lt;strong&gt;自動將目前 PR 中的最新 commit，部署到一個臨時環境中，並返回該環境的 URL 網址。&lt;/strong&gt;（如果口袋夠深的話，甚至可以部署每一個 commit 以方便比對。）
&lt;a href=&quot;https://www.infoq.cn/article/ICx4zr8mpIYO6kD9Qveb&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;真正的敏捷工作流 — — GitHub flow&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;如果能實現這個條件，將為開發團隊帶來兩點好處：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Reviewer 有任何懷疑時，便可以直接在預覽環境中驗證，而非憑空猜疑。&lt;/li&gt;
&lt;li&gt;Reviewer 也可以放心大膽的驗證自己的懷疑，不需要在 local 開發環境耗時費力地切換。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;接下來我會寫三篇「無限環境」系列的文章，介紹一些可以達成這個目的的工具。&lt;/p&gt;
&lt;p&gt;這是第一篇文章，介紹如何透過 Netlify 的 Deploy Previews 服務，建立靜態網頁的無限環境。&lt;/p&gt;
&lt;h2 id=&quot;什麼是-netlify-deploy-previews&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#什麼是-netlify-deploy-previews&quot;&gt;什麼是 Netlify Deploy Previews？&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.netlify.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Netlify&lt;/a&gt; 是一個類似 &lt;a href=&quot;https://www.heroku.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Heroku&lt;/a&gt; 的 All-in-one &lt;a href=&quot;https://zh.wikipedia.org/zh-tw/%E5%B9%B3%E5%8F%B0%E5%8D%B3%E6%9C%8D%E5%8A%A1&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;PaaS&lt;/a&gt;，提供各種現代 web 專案會用到的自動化服務，例如：靜態網站部署、CDN、持續交付和一鍵配置 HTTPS 等。&lt;/p&gt;
&lt;p&gt;其中 &lt;a href=&quot;https://www.netlify.com/blog/2016/07/20/introducing-deploy-previews-in-netlify/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Deploy Previews&lt;/a&gt; 這項服務，可以將 GitHub repository 中的每個 pull request 部署到&lt;strong&gt;唯一&lt;/strong&gt;的 URL，與 staging 和 production 環境的完全不同。你和你的團隊可以在合併到主分支、並且部署到正式環境之前，提前看到更改的外觀以及驗收功能是否正確。&lt;/p&gt;
&lt;h2 id=&quot;快速入門指南&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#快速入門指南&quot;&gt;快速入門指南&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;這篇文章會以時下最流行的 &lt;a href=&quot;https://create-react-app.dev/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Create React App&lt;/a&gt; 為例，示範如何實現 React 網站的無限環境。&lt;/p&gt;
&lt;h3 id=&quot;步驟一建立-react網站&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#步驟一建立-react網站&quot;&gt;步驟一、建立 React 網站&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;透過 &lt;code&gt;npx create-react-app&lt;/code&gt; 指令，快速產生一個 React App 專案：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;npx&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;create-react-app&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;test-netlify-deploy-previews&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;test-netlify-deploy-previews&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;npm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;start&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;打開 &lt;a href=&quot;http://localhost:3000&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;http://localhost:3000&lt;/a&gt; 確認 React 網站成功運行：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;540&quot; src=&quot;https://blog.amowu.com/_astro/image2.9r3FlSK__1oo77s.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image2.9r3FlSK__1lGjmf.webp 640w, https://blog.amowu.com/_astro/image2.9r3FlSK__1oo77s.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;h3 id=&quot;步驟二建立-github-repository&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#步驟二建立-github-repository&quot;&gt;步驟二、建立 GitHub repository&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;這篇文章以 GitHub 為例，Netlify 同時也支援 GitLab 和 Bitbucket。&lt;/p&gt;
&lt;p&gt;首先，在自己的 GitHub 新增一個 repository：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;339&quot; src=&quot;https://blog.amowu.com/_astro/image3.DFOgzMtv_Z28nMtC.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image3.DFOgzMtv_ZqpkM5.webp 640w, https://blog.amowu.com/_astro/image3.DFOgzMtv_Z28nMtC.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;接下來，將程式碼 &lt;code&gt;git push&lt;/code&gt; 至 GitHub repository：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;remote&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;origin&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;git@github.com:amowu/test-netlify-deploy-previews.git&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;commit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-m&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;chore: create react app&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;push&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;origin&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;master&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3 id=&quot;步驟三註冊配置-netlify&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#步驟三註冊配置-netlify&quot;&gt;步驟三、註冊＆配置 Netlify&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;前往 Netlify &lt;a href=&quot;https://app.netlify.com/signup&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;註冊&lt;/a&gt; 頁面，這裡以連結 GitHub 帳號的方式為例：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;525&quot; src=&quot;https://blog.amowu.com/_astro/image4.DHyTfumt_ZxL9sy.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image4.DHyTfumt_ZBUPX5.webp 640w, https://blog.amowu.com/_astro/image4.DHyTfumt_ZxL9sy.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;第一次需要「Create a new team」建立一個 team，免費方案只允許一位 team member。&lt;/p&gt;
&lt;p&gt;接下來，選擇「New Site from Git」：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;376&quot; src=&quot;https://blog.amowu.com/_astro/image5.BEDVX5Ra_Z2fn8yA.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image5.BEDVX5Ra_ZvNH9a.webp 640w, https://blog.amowu.com/_astro/image5.BEDVX5Ra_Z2fn8yA.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;選擇 GitHub 作為持續交付的服務對象：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;368&quot; src=&quot;https://blog.amowu.com/_astro/image6.FO5ZzUXT_Z1PyYif.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image6.FO5ZzUXT_Z70xRO.webp 640w, https://blog.amowu.com/_astro/image6.FO5ZzUXT_Z1PyYif.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;第一次需要透過「Configure Netlify on GitHub」安裝 Netlify 的 GitHub App：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;430&quot; src=&quot;https://blog.amowu.com/_astro/image7.CjR_i1z2_K0VKS.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image7.CjR_i1z2_FjUm4.webp 640w, https://blog.amowu.com/_astro/image7.CjR_i1z2_K0VKS.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;選擇要安裝在 GitHub 的個人帳號或是組織帳號，點選「Configure」：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;517&quot; src=&quot;https://blog.amowu.com/_astro/image8.De59bDp7_Z28UPXm.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image8.De59bDp7_jVtri.webp 640w, https://blog.amowu.com/_astro/image8.De59bDp7_Z28UPXm.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;如果希望自己所有的 repositories 都套用 Netlify 服務，可以選擇「All repositories」。因為這裡只跑文章範例，所以選擇「Only select repositories」：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;753&quot; src=&quot;https://blog.amowu.com/_astro/image9.Bg5UKToC_AXtql.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image9.Bg5UKToC_Z1TLSVP.webp 640w, https://blog.amowu.com/_astro/image9.Bg5UKToC_AXtql.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;點選 repository 進入配置頁面：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;425&quot; src=&quot;https://blog.amowu.com/_astro/image10.CcLZetZV_Z1aLvWJ.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image10.CcLZetZV_XKh3B.webp 640w, https://blog.amowu.com/_astro/image10.CcLZetZV_Z1aLvWJ.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;接下來是&lt;strong&gt;重點&lt;/strong&gt;，在 Basic build settings 底下有「Build command」和「Publish directory」兩個欄位，要填寫正確，否則 Netlify 無法正確地部署網站。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Build command 填入 &lt;code&gt;yarn build&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Publish directory 填入 &lt;code&gt;build/&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;yarn build&lt;/code&gt; 是 Create React App 提供的指令，會將靜態網站的檔案輸出到 &lt;code&gt;build/&lt;/code&gt; 資料夾底下。&lt;/p&gt;
&lt;p&gt;如果使用的是其它靜態網站產生工具（例如 Jekyll），這兩個欄位填入的內容注意不要填錯了。&lt;/p&gt;
&lt;p&gt;最後，點擊「Deploy site」開始部署網站：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;400&quot; src=&quot;https://blog.amowu.com/_astro/image11.FajYF-1C_15cwaB.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image11.FajYF-1C_Z1TykSL.webp 640w, https://blog.amowu.com/_astro/image11.FajYF-1C_15cwaB.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;部署成功之後，就會看到一組屬於 master 分支的 production 環境（支援自訂網域）的 URL 網址：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;409&quot; src=&quot;https://blog.amowu.com/_astro/image12.CjEdg0fC_Zg1kUk.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image12.CjEdg0fC_19kD4m.webp 640w, https://blog.amowu.com/_astro/image12.CjEdg0fC_Zg1kUk.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;到這裡，就完成了 Netlift 的基本配置。接下來，介紹如何實現 Deploy Previews。&lt;/p&gt;
&lt;h3 id=&quot;最終步驟玩轉-deploypreviews&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#最終步驟玩轉-deploypreviews&quot;&gt;最終步驟、玩轉 Deploy Previews&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;其實也不用特別配置，因為 Netlify 預設就會開啟 Deploy Previews 這個功能。我們直接送一個 Pull Request 試試。&lt;/p&gt;
&lt;p&gt;打開 &lt;strong&gt;src/App.js&lt;/strong&gt;，修改幾行程式碼如下：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- Edit &amp;lt;&lt;/span&gt;&lt;span&gt;code&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;src/App.js&amp;lt;/&lt;/span&gt;&lt;span&gt;code&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; and save to reload.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; Hello World!&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;接下來，建立新的分支，並 &lt;code&gt;git push&lt;/code&gt; 到 GitHub：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;branch&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;feature/hello-world&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;commit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-m&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;refactor(App): hello world&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;push&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-u&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;origin&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;feature/hello-world&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;最後，打開 GitHub，新增一個 merge &lt;code&gt;master&lt;/code&gt; from &lt;code&gt;feature/hello-world&lt;/code&gt; 的 Pull Request，捲至頁面至下方的 status checks 區塊，應該會出現 &lt;strong&gt;netlify//deploy-preview&lt;/strong&gt; 的 check：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;254&quot; src=&quot;https://blog.amowu.com/_astro/image13.ATmF1l4p_Z1oYIkO.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image13.ATmF1l4p_ZaoiTV.webp 640w, https://blog.amowu.com/_astro/image13.ATmF1l4p_Z1oYIkO.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;等待「Deploy preview ready!」出現之後，表示 Netlify 已經順利將 PR 部署到一個暫時的環境，這時候可以透過點擊右方「Details」打開 Netlify 提供的唯一 URL，應該就會看到文字被更改「Hello World!」的頁面。&lt;/p&gt;
&lt;p&gt;成果展示：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;2050&quot; height=&quot;1476&quot; src=&quot;https://blog.amowu.com/_astro/image14.CfA7PlRK_11LUn5.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image14.CfA7PlRK_Zlu25T.webp 640w, https://blog.amowu.com/_astro/image14.CfA7PlRK_1aSx5a.webp 750w, https://blog.amowu.com/_astro/image14.CfA7PlRK_Z1G8QqD.webp 828w, https://blog.amowu.com/_astro/image14.CfA7PlRK_Z21Dlua.webp 1080w, https://blog.amowu.com/_astro/image14.CfA7PlRK_Z2oCnz2.webp 1280w, https://blog.amowu.com/_astro/image14.CfA7PlRK_Zx8jt4.webp 1668w, https://blog.amowu.com/_astro/image14.CfA7PlRK_ZqIXwK.webp 2048w, https://blog.amowu.com/_astro/image14.CfA7PlRK_11LUn5.webp 2050w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;至此，完善 GitHub flow 的最後一哩路，「合併前部署」的流程就建立起來了。&lt;/p&gt;
&lt;p&gt;未來 Code Reviewer 驗收 PR 的時機點，就可以發生在 merge master 之前，大大降低了「上線之後才發現問題」的發生機率。&lt;/p&gt;
&lt;h2 id=&quot;總結&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#總結&quot;&gt;總結&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Google 的 &lt;a href=&quot;https://google.github.io/eng-practices/review/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Code Review Developer Guide&lt;/a&gt; 曾經提到，看 PR 的優先順序，應該是先跑跑看結果是否正確。如果一開始就投入大量時間去 review 程式碼，但是發現跑出來的結果根本是錯的，那麼這其實是很浪費人力成本的。&lt;/p&gt;
&lt;p&gt;這篇文章介紹了 Netlify 的 Deploy Previews 服務，結合 GitHub flow，讓 code author 在送出 React app 的修改 PR 時，自動在合併回主分支之前，先提前部署一次性的「無限環境」，讓 code reviewer 優雅地驗收測試，避免發生上線 production 環境之後才發現程式碼有問題。&lt;/p&gt;
&lt;p&gt;介紹完 Netlift Deploy Previews 之後，可能會有讀者好奇：「萬一我們的專案不是靜態網站、而是動態網站呢？」。&lt;/p&gt;
&lt;p&gt;真實的世界，不會只有靜態網站那麼單純。下一篇，我會介紹如何透過 Heroku 的 &lt;a href=&quot;https://devcenter.heroku.com/articles/github-integration-review-apps&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Review Apps&lt;/a&gt; 來實現動態網站的無限環境，敬請期待。&lt;/p&gt;
&lt;h2 id=&quot;附錄&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#附錄&quot;&gt;附錄&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;如何刪除安裝在-github-repository-的-netlifyapp&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#如何刪除安裝在-github-repository-的-netlifyapp&quot;&gt;如何刪除安裝在 GitHub repository 的 Netlify app？&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;如果之後不需要 Deploy Previews 這個服務，可以前往安裝的 repository 頁面，依序執行以下步驟：&lt;/p&gt;
&lt;p&gt;Settings &amp;gt; Integrations &amp;amp; services &amp;gt; Installed GitHub Apps &amp;gt; Netlify &amp;gt; Configure &amp;gt; Uninstall Netlify &amp;gt; Uninstall&lt;/p&gt;
&lt;h3 id=&quot;可以套用在-components-library嗎&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#可以套用在-components-library嗎&quot;&gt;可以套用在 components library 嗎？&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;假如你的專案不是網頁應用，而是元件庫（例如 &lt;a href=&quot;https://github.com/insin/nwb&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;nwb&lt;/a&gt; 的 &lt;a href=&quot;https://github.com/insin/nwb/blob/master/docs/guides/ReactComponents.md&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;React Components and Libraries&lt;/a&gt;）。那麼，通常專案會搭配 &lt;a href=&quot;https://storybook.js.org/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Storybook&lt;/a&gt; 這個工具一起協助開發。&lt;/p&gt;
&lt;p&gt;這時候，只需將 build settings 設定成 Storybook 的指令，然後指定輸出資料夾即可：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Build command：&lt;code&gt;npm run build-storybook -- -c .storybook&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Publish directory：&lt;code&gt;storybook-static&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;參考資料&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#參考資料&quot;&gt;參考資料&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.infoq.cn/article/ICx4zr8mpIYO6kD9Qveb&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;真正的敏捷工作流 — — GitHub flow&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.netlify.com/blog/2016/07/20/introducing-deploy-previews-in-netlify/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Introducing Deploy Previews in Netlify&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://google.github.io/eng-practices/review/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Google’s Code Review Guidelines&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>Tech</category></item><item><title>全遠端工作</title><link>https://blog.amowu.com/posts/2019-06-03-all-remote/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2019-06-03-all-remote/</guid><description>遠端工作並非一個需要克服的挑戰。而是一個明顯的商業優勢。</description><pubDate>Mon, 03 Jun 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;遠端工作並非一個需要克服的挑戰。而是一個明顯的商業優勢。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;原文標題：All Remote
原文網址：&lt;a href=&quot;https://about.gitlab.com/company/culture/all-remote/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://about.gitlab.com/company/culture/all-remote/&lt;/a&gt;
原文作者：GitLab&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;GitLab 是一間全遠端（All Remote）的公司，&lt;a href=&quot;https://about.gitlab.com/company/team/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;團隊成員&lt;/a&gt;遍布全球 50 多個國家。這裡介紹了一些 GitLab 的&lt;a href=&quot;https://about.gitlab.com/company/culture/all-remote/tips/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;工作原理&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id=&quot;遠端工作宣言&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#遠端工作宣言&quot;&gt;遠端工作宣言&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;全遠端工作提倡：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;從世界各地招聘和工作，而非從公司附近&lt;/li&gt;
&lt;li&gt;彈性工時優於固定工時&lt;/li&gt;
&lt;li&gt;通過寫下來記錄知識，而非口頭傳授&lt;/li&gt;
&lt;li&gt;建立流程，而非在職培訓&lt;/li&gt;
&lt;li&gt;公開分享資訊，而不是只告訴你該知道的&lt;/li&gt;
&lt;li&gt;開放每份文件供任何人編輯，而非自上而下的存取權限控制&lt;/li&gt;
&lt;li&gt;非同步通訊優於同步通訊&lt;/li&gt;
&lt;li&gt;重視工作結果，而非投入的工時&lt;/li&gt;
&lt;li&gt;正式的溝通管道優於非正式的溝通管道&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;666&quot; height=&quot;220&quot; src=&quot;https://blog.amowu.com/_astro/image1.Bkehykaf_OM7NW.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image1.Bkehykaf_ZagjHm.webp 640w, https://blog.amowu.com/_astro/image1.Bkehykaf_OM7NW.webp 666w&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;為何遠端&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#為何遠端&quot;&gt;為何遠端？&lt;/a&gt;&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;遠端工作並非一個需要克服的挑戰。而是一個明顯的商業優勢。
 — — Victor, Product Manager, GitLab&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;從節省辦公空間的成本到提供員工日常生活的更大靈活性，完全遠端工作為組織及其員工提供了許多優勢。 但我們也體認到，並非所有人都適合成為一家完全遠端的公司。 以下是一些優點和缺點。&lt;/p&gt;
&lt;h2 id=&quot;優點&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#優點&quot;&gt;優點&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;對於員工&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#對於員工&quot;&gt;對於員工&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;日常生活更彈性（小孩、父母、朋友、雜務、運動、快遞）&lt;/li&gt;
&lt;li&gt;不會浪費時間、精力或金錢在通勤上（捷運和公車費用、汽油、汽車維護費、收費站等）&lt;/li&gt;
&lt;li&gt;減少接觸生病同事的細菌&lt;/li&gt;
&lt;li&gt;減少被打斷的狀態，提高&lt;a href=&quot;https://www.inc.com/brian-de-haaff/3-ways-remote-workers-outperform-office-workers.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;生產力&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;能夠在不請假的情況下遊歷其它地方（家庭、娛樂等）&lt;/li&gt;
&lt;li&gt;自由移動&lt;/li&gt;
&lt;li&gt;有些人發現遠端更容易與難搞的同事溝通&lt;/li&gt;
&lt;li&gt;減少入職可能會有的社交壓力&lt;/li&gt;
&lt;li&gt;在家吃飯（有時）更好，更便宜&lt;/li&gt;
&lt;li&gt;某些國家的稅收可能更便宜&lt;/li&gt;
&lt;li&gt;不需要工作服（譯者註：不用煩惱今天穿什麼）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;從家庭時間到旅行計劃，有很多關於遠程工作如何影響全球 GitLab 團隊成員生活的&lt;a href=&quot;https://about.gitlab.com/company/culture/all-remote/stories/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;例子和故事&lt;/a&gt;。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;這些靈活性使家庭的生指品質呈指數級增長，從而減輕壓力，提高您的工作效率和積極性。 這些價值是金錢無法衡量的。 — — Haydn, Regional Sales Director, GitLab&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;對於組織&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#對於組織&quot;&gt;對於組織&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;招募優秀的人才，&lt;a href=&quot;https://about.gitlab.com/jobs/faq/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;無論他們所居何處&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;員工的干擾更少，效率更高&lt;/li&gt;
&lt;li&gt;增加省下來的辦公室成本和&lt;a href=&quot;https://about.gitlab.com/2019/02/28/why-we-pay-local-rates/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;補償金&lt;/a&gt;（基於在低成本地區招聘）&lt;/li&gt;
&lt;li&gt;吸引自我啟動者（self-starters，他們不等待被賦予任務、職責，他們會直接出擊，在短時間內獲得最好的成果）&lt;/li&gt;
&lt;li&gt;更容易快速發展您的公司&lt;/li&gt;
&lt;li&gt;減少會議次數，重點關注結果和出色的工作成果&lt;/li&gt;
&lt;li&gt;在當地動亂或自然災害（例如政治或天氣相關事件）的情況下保持業務的連續性&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;對於世界&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#對於世界&quot;&gt;對於世界&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;全遠端工作的優勢不僅止於一個組織及其人員。 由於沒有通勤的員工，也沒有辦公大樓或園區，所以遠端公司的碳足跡都要小得多。 對於全球公司而言，為低成本地區帶來薪酬較高的工作也會產生積極的經濟影響。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://youtu.be/NoFLJLJ7abE?si=x09YybzRJTeBYWdW&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://youtu.be/NoFLJLJ7abE?si=x09YybzRJTeBYWdW&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;缺點&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#缺點&quot;&gt;缺點&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;儘管有其優點，但全遠端工作並不適合所有人。根據他們的生活方式和工作偏好，它可能對潛在員工以及組織有不利之處。&lt;/p&gt;
&lt;h3 id=&quot;對於員工-1&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#對於員工-1&quot;&gt;對於員工&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;當你處於遠端時，入職可能很困難，因為它涉及更多的自我學習，而你並沒有與新同事和新員工在一起&lt;/li&gt;
&lt;li&gt;遠端角色的&lt;a href=&quot;https://www.linkedin.com/pulse/transition-remote-work-1-month-casey-allen-shobe/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;第一個月&lt;/a&gt;可能會感到孤獨，特別是如果您從傳統的辦公環境過渡&lt;/li&gt;
&lt;li&gt;如果組織沒有意識到替員工之間建立保持聯繫的方式，那麼遠端可能會導致溝通技巧中斷&lt;/li&gt;
&lt;li&gt;有些人可能會發現很難在他們生活和睡眠的同一環境中工作，因為專用的工作空間有助於將環境從他們的家庭生活轉換為工作&lt;/li&gt;
&lt;li&gt;不同時區的團隊成員可能不得不在會議時間上妥協&lt;/li&gt;
&lt;li&gt;世界各地的貨幣和稅收要求的差異可能給組織帶來挑戰&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;對於組織-1&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#對於組織-1&quot;&gt;對於組織&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;因為全遠端工作非傳統，有時會讓投資者、合作夥伴和客戶有所顧慮&lt;/li&gt;
&lt;li&gt;世界各地的貨幣和稅收要求的差異可能給組織帶來挑戰&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;為何時機已成熟&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#為何時機已成熟&quot;&gt;為何時機已成熟？&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;如果沒有技術的不斷發展，並且持續開發和改善這類工具，完全遠端工作是不可能的。&lt;/p&gt;
&lt;p&gt;我們不僅僅看到這些對全遠端公司的影響。事實上，在一些大型園區的組織中，員工通常會進行視訊對話，而不是花 10 分鐘去其它大樓。&lt;/p&gt;
&lt;p&gt;以下是使遠端工作成為可能的一些關鍵因素：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;隨處可連的高速網路，100Mb/s 以上的有線網路、5GHz Wifi、4G 行動網路&lt;/li&gt;
&lt;li&gt;視訊對話軟體，Google Hangouts、Zoom&lt;/li&gt;
&lt;li&gt;行動科技，每個人的口袋裡都有一台電腦&lt;/li&gt;
&lt;li&gt;語音轉文字軟體的進化，比打字更快、更準&lt;/li&gt;
&lt;li&gt;通訊應用程式，Slack、Mattermost、Zulip&lt;/li&gt;
&lt;li&gt;任務追蹤工具，Trello、GitHub issues、GitLab issues&lt;/li&gt;
&lt;li&gt;貢獻工具，GitHub Pull Requests、GitLab Merge Requests&lt;/li&gt;
&lt;li&gt;靜態網站，GitHub Pages、GitLab Pages&lt;/li&gt;
&lt;li&gt;英語水平，更多人學習英語&lt;/li&gt;
&lt;li&gt;城市的交通擁堵在增長&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;全遠端並不意味什麼&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#全遠端並不意味什麼&quot;&gt;「全遠端」並不意味什麼？&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;讓我們來解決一些關於完全遠端工作的常見誤解。&lt;/p&gt;
&lt;p&gt;首先要做的事情是：一個全遠端的公司意味著沒有多個人所在的辦公室或總部。 在分區辦公室沒有人的唯一方法就是沒有總部。&lt;/p&gt;
&lt;p&gt;通常「遠端」和「分佈式」是可以互換使用的術語，但它們並不完全相同。GitLab 更喜歡「遠端」一詞，因為「分佈式」表示多個實體辦公室。「遠端」指的是能夠從任何地方完成工作。&lt;/p&gt;
&lt;p&gt;對於員工而言，成為一家完全遠端公司的一分子並不意味著獨立工作或孤立，因為它不是人類互動的替代品。技術使我們能夠與團隊&lt;a href=&quot;https://about.gitlab.com/company/culture/all-remote/tips/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;保持密切聯繫&lt;/a&gt;，無論是在非同步的文字中還是通過視訊進行的即時對話。團隊應該密切合作、經常溝通，並感覺自己是一個大型團隊的寶貴成員。&lt;/p&gt;
&lt;p&gt;遠端工作也並不意味著你身體受到限制。您可以隨意在任何地方工作。這可能是在家裡與家人、咖啡館、一個共享空間，或當地的圖書館，而你的小寶貝正在享受故事書時間。您可以全天與同事進行頻繁的視訊聊天或虛擬會議配對，如果您位於彼此附近，您甚至可以與其他同事見面，親自一起工作。&lt;/p&gt;
&lt;p&gt;在組織層面，「完全遠端」並不僅僅意味著外包工作。相反，這意味著您可以聘請來自世界各地最優秀的人才。它也不是一種管理範式。您仍然有一個分層組織，但重點是輸出而不是輸入。&lt;/p&gt;
&lt;p&gt;總而言之，遠端基本上是關於自由和個人選擇。在 GitLab，我們&lt;a href=&quot;https://about.gitlab.com/handbook/values/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;重視您的結果&lt;/a&gt;，而不是您完成工作的地方。&lt;/p&gt;
&lt;h2 id=&quot;gitlab-如何建立他們的全遠端團隊&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#gitlab-如何建立他們的全遠端團隊&quot;&gt;GitLab 如何建立他們的全遠端團隊&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;GitLab 已經學習了很多關於構建和管理完全遠端團隊所需的知識，並希望分享這些知識以幫助其他人取得成功。&lt;/p&gt;
&lt;p&gt;了解 GitLab &lt;a href=&quot;https://about.gitlab.com/company/culture/all-remote/tips/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;如何使其工作&lt;/a&gt;並查看我們的&lt;a href=&quot;https://about.gitlab.com/company/culture/all-remote/tips&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;遠端工作提示&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id=&quot;資源&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#資源&quot;&gt;資源&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;瀏覽 GitLab 的&lt;a href=&quot;https://about.gitlab.com/company/culture/all-remote/resources&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;資源頁面&lt;/a&gt;，了解有關 GitLab 全遠端方法的更多資訊，閱讀新聞中的遠端工作，以及了解其他公司的主導方式。&lt;/p&gt;
&lt;p&gt;這裡列出了受 GitLab 文化啟發的&lt;a href=&quot;https://about.gitlab.com/handbook/got-inspired/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;公司名單&lt;/a&gt;。&lt;/p&gt;</content:encoded><category>Tech</category></item><item><title>最後下班的人，先離職</title><link>https://blog.amowu.com/posts/2019-05-20-weekly-020/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2019-05-20-weekly-020/</guid><description>前陣子在書店架上看見一本書，書名是《最後下班的人，先離職》。</description><pubDate>Mon, 20 May 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;前陣子在書店架上看見一本書，書名是《最後下班的人，先離職》。&lt;/p&gt;
&lt;p&gt;雖然標題有點殺人，但是翻了幾頁之後，還是勾起了我出社會之後第一份工作的一些回憶，那段總是最後一個關辦公室燈火的日子，雖然最後不是因為這個理由離職，但現在回想起來，真的不健康。&lt;/p&gt;
&lt;p&gt;換了一份工作之後，開始注重起「時間管理」，學習如何在有限的時間內，完成無限的任務。但是最近，有位同事過不了這個檻，離職了。&lt;/p&gt;
&lt;p&gt;所以想藉著這篇文章，討論什麼樣的組織架構、人力配置、合作方式，才適合快速擴張中的新創公司。&lt;/p&gt;
&lt;h2 id=&quot;為什麼常常加班&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#為什麼常常加班&quot;&gt;為什麼常常加班？&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;大部分的人認為，超時加班是公司或管理階層該解決的問題，但員工其實也是當事者之一。&lt;/p&gt;
&lt;p&gt;要解決這個問題，就必須先掌握「為什麼常常加班？」的本質原因。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;574&quot; src=&quot;https://blog.amowu.com/_astro/image1.BbSw6nJo_GrXqk.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image1.BbSw6nJo_Z3afJM.webp 640w, https://blog.amowu.com/_astro/image1.BbSw6nJo_GrXqk.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;加班的理由通常可分成兩大項，首先是因為某些緣故，導致想下班也「無法下班」，其次是其實可以下班，卻「不下班」。&lt;/p&gt;
&lt;p&gt;先就「無法下班」來思考。一般而言，會造成這種現象的理由可能包括「不能下班的氣氛」（例如最近很火的 &lt;a href=&quot;https://zh.wikipedia.org/wiki/996%E5%B7%A5%E4%BD%9C%E5%88%B6&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;996&lt;/a&gt;）、「工作做不完」、「需要待命」。&lt;/p&gt;
&lt;p&gt;其中「工作做不完」這點似乎還能繼續探討背後的原因。比如「工作負荷太重」，任務多到在上班時間內做不完。另一個原因可能是工作量正常，但是「工作效率不彰」所導致，可能是「團隊的進度拖延」，也可能是個人的「工作方式有問題」。&lt;/p&gt;
&lt;p&gt;接著針對「不下班」的問題，把所有想得到的理由全部列出來。「熱愛工作」、「想賺加班費」、「回家也沒事做」。這三個原因之中的「想賺加班費」，有可能是因為「薪水太低」，或是薪水不錯，但「需要更多錢」這兩個理由。&lt;/p&gt;
&lt;p&gt;總結所有原因或理由整理之後可以發現，在各種常常加班的原因之中，摻雜著「職場問題」與「個人問題」。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;本文&lt;strong&gt;不探討&lt;/strong&gt;諸如「熱愛工作」之類的個人問題（熱愛工作你還會離職？）。&lt;/p&gt;
&lt;p&gt;焦點放在職場問題：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;不能下班的氣氛&lt;/li&gt;
&lt;li&gt;需要待命&lt;/li&gt;
&lt;li&gt;工作負荷太重&lt;/li&gt;
&lt;li&gt;團隊的進度拖延&lt;/li&gt;
&lt;li&gt;薪水太低&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;三角貿易&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#三角貿易&quot;&gt;三角貿易&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;三角貿易（英文：triangle trade）起源於 16 世紀的歐洲。最著名的是歐洲，西非以及美洲之間的大西洋三角貿易：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;歐洲 → 西非：酒精、軍火、紡織品等工業製品&lt;/li&gt;
&lt;li&gt;西非 → 美洲：奴隸&lt;/li&gt;
&lt;li&gt;美洲 → 歐洲：貴金屬、礦石、蔗糖、菸草、咖啡、可可、糧食等農產品及原材料&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;448&quot; height=&quot;327&quot; src=&quot;https://blog.amowu.com/_astro/image2.BC5vnpzf_ZK8w00.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image2.BC5vnpzf_ZK8w00.webp 448w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;回來看 KPI 這件事，一間公司的績效量化指標通常也可以歸類為三種，剛好對應專案管理難題的金三角：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;品質 = 要好&lt;/li&gt;
&lt;li&gt;時程 ＝ 要快&lt;/li&gt;
&lt;li&gt;營收 = 要便宜（營收升高，相對成本就降低）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;525&quot; src=&quot;https://blog.amowu.com/_astro/image3.ULy2hE0b_1Bu57g.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image3.ULy2hE0b_2272OQ.webp 640w, https://blog.amowu.com/_astro/image3.ULy2hE0b_1Bu57g.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;因此，我建議可以將團隊分為三個 team：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Cheap Team → 負責替公司帶來營收（money）&lt;/li&gt;
&lt;li&gt;Good Team → 負責解決產品品質問題（quality）&lt;/li&gt;
&lt;li&gt;Fast Team → 負責加速公司發展（time）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;429&quot; height=&quot;305&quot; src=&quot;https://blog.amowu.com/_astro/image4.BHJhbnrw_ZcGpUY.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image4.BHJhbnrw_ZcGpUY.webp 429w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Cheap Team 負責事項：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;公司長期目標（Roadmap）的新功能（Feature）開發&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Good Team 負責事項：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;來自產品本身的需求（例如：客服回報的 Bug）&lt;/li&gt;
&lt;li&gt;撰寫 Cheap Team 的測試案例&lt;/li&gt;
&lt;li&gt;重構技術債&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Fast Team 負責事項：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;其它額外的需求（例如：資料查詢、埋追蹤事件、主機掛了）&lt;/li&gt;
&lt;li&gt;DevOps，將日常瑣碎的事務自動化&lt;/li&gt;
&lt;li&gt;基建設施（Infrastructure）&lt;/li&gt;
&lt;li&gt;活動網頁&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;這樣的分配可能會造成「被分配在某個 team 的人沒有成就感」的問題，所以可以根據團隊的特性調整，例如每一季輪調一次，這樣一來可以解決倦怠感，二來透過輪流，不熟悉相關事務的人可以去問最熟的人向他學習，達到溝通交流的目的，也解決「這個業務只有他會，所以每次都找他」的問題。&lt;/p&gt;
&lt;p&gt;另外，這個機制也可以解決「需要待命」的問題，例如 Fast Team 要在主機掛掉的時候馬上反應處理，如果每次都是同一批人，那累積久了，他就是下一個離職的那個人。&lt;/p&gt;
&lt;p&gt;如果有的人只喜歡做某件事，例如 DevOps，不喜歡做新功能，那也可以不參與輪調機制，但必須專注該項業務，否則可能會造成「團隊的進度拖延」問題。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;解決了「需要待命」和「團隊的進度拖延」的問題之後，接下來看「薪水太低」與「工作負荷太重」的問題。&lt;/p&gt;
&lt;p&gt;加薪一般來說有兩種方法：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;增加底薪&lt;/li&gt;
&lt;li&gt;增加獎金的抽成比例&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;而這兩種方法的都需要靠「晉升」才能辦到。&lt;/p&gt;
&lt;p&gt;目前比較主流的晉升體系是「雙通道」，根據員工的適才適性，分成兩種管道：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;專業方向的 Individual Contributor，簡稱 IC&lt;/li&gt;
&lt;li&gt;管理方向的 People Management，簡稱 M&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;394&quot; src=&quot;https://blog.amowu.com/_astro/image5.BH7qHpIN_Z28axCr.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image5.BH7qHpIN_Zot2er.webp 640w, https://blog.amowu.com/_astro/image5.BH7qHpIN_Z28axCr.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;以 Oracle 這間公司為例，IC-3 的資深工程師可以往 IC-6 的架構師發展，M2 的 Manager 可以往 M6 的 VP 努力前進。&lt;/p&gt;
&lt;p&gt;這一切看起來都很美好，喜歡搞技術的，可以持續專研精進自己；擅長與人溝通、協調資源能力強的人，可以往管理職發展。&lt;/p&gt;
&lt;p&gt;但是亞洲的職場環境，通常鼓勵員工往管理職發展，一來獎金分配的比例比較高，二來職稱也比較好看（經理、總裁、總監聽起來比工程師、架構師還高大尚）。&lt;/p&gt;
&lt;p&gt;導致許多 IC 紛紛往 M 發展，造成原本個性不適合管理人的員工遭遇「彼得原理」，結果就是：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;擠擠一堆 M 在那出一張嘴，沒人做事的窘境&lt;/li&gt;
&lt;li&gt;一切自己來，最後出現「工作負荷太重」的問題&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;而要解決這兩點問題，只有走上「淘汰」或「離職」兩條路，造成管理職位出現空缺需要填補，然後還是回到鼓勵大家應該往管理職發展的惡性循環。最後出現類似「〇〇歲還是工程師沒升管理職，這個人是不是有什麼問題」之類的價值觀。&lt;/p&gt;
&lt;p&gt;我不否認管理職的重要性，但是領導能力強的人本來就很稀缺，如果職場的制度是鼓勵你往管理晉升，而不是留住有貢獻價值的人，當一個不適合 M 職能的 IC 站上 M，有可能賠了夫人又折兵。&lt;/p&gt;
&lt;h2 id=&quot;雙首長制&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#雙首長制&quot;&gt;雙首長制&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;每逢選舉時日，這個國家的在野黨候選人就會提出要廢除「&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%8D%8A%E6%80%BB%E7%BB%9F%E5%88%B6&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;雙首長制&lt;/a&gt;」，主張改成「&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E6%80%BB%E7%BB%9F%E5%88%B6&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;總統制&lt;/a&gt;」或「&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E8%AD%B0%E6%9C%83%E5%88%B6&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;內閣制&lt;/a&gt;」。但是政黨輪替之後往往就不了了之，因為當總統實在太爽了，位於行政及立法之上，可以不受立法限制。&lt;/p&gt;
&lt;p&gt;扯遠了⋯⋯，關於雙首長制的更多缺點，請參見「&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%8D%8A%E6%80%BB%E7%BB%9F%E5%88%B6&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;維基百科&lt;/a&gt;」。&lt;/p&gt;
&lt;p&gt;如果說「三角貿易」解決的是來自 PO 右側，屬於外部需求的「工作負荷太重」問題，那麼接下來要提到的「雙首長制」組織架構，是我認為可以解決 PO 左側，屬於來自工程師給予的「工作負荷太重」的一種方法。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;552&quot; src=&quot;https://blog.amowu.com/_astro/image6.tmG6G4yJ_2ogwYY.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image6.tmG6G4yJ_1DvdPi.webp 640w, https://blog.amowu.com/_astro/image6.tmG6G4yJ_2ogwYY.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;以 CPO 為首，產品開發團隊主要由 PO 和 Tech Lead 帶領，分別負責產品時程與品質的管理，一個專注人、一個專注事。&lt;/p&gt;
&lt;p&gt;PO 的負責範圍：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;產品設計（掌握使用者要什麼）&lt;/li&gt;
&lt;li&gt;時程管理（確保任務是在前進的）&lt;/li&gt;
&lt;li&gt;資源協調（溝通、排除任何會阻礙前進的問題）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Tech Lead 的負責範圍：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;架構選型&lt;/li&gt;
&lt;li&gt;風格一致&lt;/li&gt;
&lt;li&gt;權限控管&lt;/li&gt;
&lt;li&gt;任務規劃&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果找不到合適的 PO，也可以先從 Designer 搭配 Project Manager 開始，一個負責產品設計，一個管理時程和協調資源。&lt;/p&gt;
&lt;p&gt;至於這裡不設立 CTO 的原因，我認為以台灣產業短視近利的尿性來看，還是以 CPO 為主會比較務實。如果是新創團隊，那麼迭代的腳步更快，CTO 與 CPO 並存什麼的更是天方夜譚。我曾經在《&lt;a href=&quot;https://blog.amowu.com/posts/2019-01-21-weekly-006/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;殺雞用牛刀&lt;/a&gt;》這篇文章探討過 Google 的工程導向文化和 Facebook 產品導向文化，且先不說台灣，可能大部分灣區的新創初期都是在燒錢，很少有時間給你搞技術，隨便一句「先求有再求好」就可以讓你掉棒，至於是不是之後真的會求好，亦或是繼續再求有，就不得而知了，因為很少有新創可以活過五年的。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;總結一下，離職的原因千百種，其中較常見的理由是「常常加班」。加班的原因主要可以歸納為「個人問題」與「職場問題」兩種類型，本文主要探討幾種職場問題，以及解決方法。&lt;/p&gt;
&lt;p&gt;透過「三角貿易」的概念，將團隊劃分成負責加速的 Fast Team，負責品質的 Good Team，以及負責營收的 Cheap Team，解決「要快、要好、要便宜」的三角難題。&lt;/p&gt;
&lt;p&gt;Cheap Team 專心開發能為公司帶來營收的新功能，其它額外需求交給 Good Team 和 Good Team 去折騰，避免有團隊成員開發到一半被抓去做其它事的窘境，解決了「團隊的進度拖延」問題。&lt;/p&gt;
&lt;p&gt;其中最容易發生「需要待命」的任務限縮在 Fast Team，讓外部人員知道碰到緊急狀況要直接找誰負責，解決 PO「工作負荷太重」的問題。另外透過每一季輪班的機制，避免壓力都扛在幾個人身上，也解決 Engineer「需要待命」的問題。&lt;/p&gt;
&lt;p&gt;最後是在 CPO 負責的產品團隊底下，設立了 Tech Lead 和 Product Owner 並行的「雙首長制」，Tech Lead 負責維護開發品質，適合讓喜歡專研技術的 IC 當作晉升目標；Product Owner 負責管理資源和確保產品走在時程上，適合擅長溝通協調的人發展。晉升體系如果完善，就有了明確的努力方向，因為「薪水太低」而離職的人或許也會降低。&lt;/p&gt;
&lt;p&gt;至於因為「不能下班的氣氛」而加班的人，我認為還是儘早離職比較好。或許如&lt;/p&gt;
&lt;p&gt;阮一峰在他的&lt;a href=&quot;http://www.ruanyifeng.com/blog/2019/05/weekly-issue-55.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;文章&lt;/a&gt;中提到的：「程序員要做的不是反對 996，而是提倡遠程辦公。」&lt;/p&gt;
&lt;h2 id=&quot;自由與秩序該如何取捨&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#自由與秩序該如何取捨&quot;&gt;自由與秩序該如何取捨？&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;394&quot; src=&quot;https://blog.amowu.com/_astro/image7.vqQANVVE_ZKuEDs.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image7.vqQANVVE_ZLg1hk.webp 640w, https://blog.amowu.com/_astro/image7.vqQANVVE_ZKuEDs.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;最近在玩《刺客教條》這款遊戲，裡面刺客對抗聖殿騎士的故事不禁讓我有所感悟。&lt;/p&gt;
&lt;p&gt;上個世紀，集權專制帶來的慘痛教訓，如今這個世界上的大部分人，似乎都知道絕對秩序的壞處。追求自由，已成了我們這個時代最大的政治正確。&lt;/p&gt;
&lt;p&gt;但是，沒有秩序的自由毫無意義，追求片面的自由與追求絕對的秩序同樣危險。&lt;/p&gt;
&lt;p&gt;專案的管理方法不斷地推陳出新（Waterfall、Agile、Scrum、Kanban、KPI、OKR），但是團隊的運行卻不總是那麼順利。會不會其實問題是出在人本身而不是管理方法？&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;393&quot; src=&quot;https://blog.amowu.com/_astro/image8.BOjq07w5_13QQMJ.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image8.BOjq07w5_1UAHfH.webp 640w, https://blog.amowu.com/_astro/image8.BOjq07w5_13QQMJ.webp 700w&quot; /&gt;&lt;/p&gt;</content:encoded><category>Weekly</category></item><item><title>影響我一生的十大遊戲（一）</title><link>https://blog.amowu.com/posts/2019-05-06-weekly-019/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2019-05-06-weekly-019/</guid><description>自 1989 年的平成元年算起，持續了約 30 年的「平成時代」已於今年 4 月畫下句點，5 月起由新年號「令和」正式接班。</description><pubDate>Mon, 06 May 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;自 1989 年的平成元年算起，持續了約 30 年的「平成時代」已於今年 4 月畫下句點，5 月起由新年號「令和」正式接班。&lt;/p&gt;
&lt;p&gt;日本遊戲媒體&lt;a href=&quot;https://www.famitsu.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;週刊ファミ通&lt;/a&gt;日前&lt;a href=&quot;https://www.famitsu.com/news/201904/22175124.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;公布&lt;/a&gt;由讀者票選的「平成最佳遊戲」前三名經典作品。&lt;/p&gt;
&lt;p&gt;拿下第一名的是 1995 年在 SFC（俗稱：&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E8%B6%85%E7%B4%9A%E4%BB%BB%E5%A4%A9%E5%A0%82&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;超任&lt;/a&gt;）平台發售的《&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E6%97%B6%E7%A9%BA%E4%B9%8B%E8%BD%AE&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;超時空之鑰&lt;/a&gt;》。本作受到 30 歲以上玩家（包括我）一致壓倒性支持，高票獲選。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;640&quot; height=&quot;349&quot; src=&quot;https://blog.amowu.com/_astro/image1.2OHwjVFi_xd3XS.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image1.2OHwjVFi_xd3XS.webp 640w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;《超時空之鑰》是由 &lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%8F%B2%E5%85%8B%E5%A8%81%E7%88%BE&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;SQUARE&lt;/a&gt;（當時尚未合併 &lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%8F%B2%E5%85%8B%E5%A8%81%E5%B0%94%E8%89%BE%E5%B0%BC%E5%85%8B%E6%96%AF&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;SQUARE ENIX&lt;/a&gt;）負責製作發行，《&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E4%B8%83%E9%BE%99%E7%8F%A0&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;七龍珠&lt;/a&gt;》作者&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E9%B3%A5%E5%B1%B1%E6%98%8E&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;鳥山明&lt;/a&gt;、《&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%8B%87%E8%80%85%E9%AC%A5%E6%83%A1%E9%BE%8D%E7%B3%BB%E5%88%97&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;勇者鬥惡龍&lt;/a&gt;》之父&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%A0%80%E4%BA%95%E9%9B%84%E4%BA%8C&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;堀井雄二&lt;/a&gt;，以及《&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E6%9C%80%E7%B5%82%E5%B9%BB%E6%83%B3%E7%B3%BB%E5%88%97&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;太空戰士&lt;/a&gt;》之父&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%9D%82%E5%8F%A3%E5%8D%9A%E4%BF%A1&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;坂口博信&lt;/a&gt;聯手打造的夢幻級 RPG。無論是劇情、美術、系統或音樂，放到今天來看仍是不朽的經典名作。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;640&quot; height=&quot;367&quot; src=&quot;https://blog.amowu.com/_astro/image2.CcaZoftA_Z1ILaSX.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image2.CcaZoftA_Z1ILaSX.webp 640w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;作為一路走過昭和、平成、令和時代的三朝元老級玩家，見證了這 30 年間五花八門的遊戲作品問世。最近看了 YouTube 頻道 &lt;a href=&quot;https://www.youtube.com/channel/UCLgGLSFMZQB8c0WGcwE49Gw&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;攻壳 Gamker&lt;/a&gt; 的《&lt;a href=&quot;https://www.youtube.com/watch?v=oY4DxPRV0Bs&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;十大影響我的遊戲&lt;/a&gt;》系列之後，也想來寫一篇影響我一生的十大遊戲。&lt;/p&gt;
&lt;h2 id=&quot;第十位熱血行進曲fc&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#第十位熱血行進曲fc&quot;&gt;第十位：熱血行進曲（FC）&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;649&quot; height=&quot;365&quot; src=&quot;https://blog.amowu.com/_astro/image3.BDc9we_G_Z27RLjN.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image3.BDc9we_G_oVRMT.webp 640w, https://blog.amowu.com/_astro/image3.BDc9we_G_Z27RLjN.webp 649w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;1992 年在 FC（俗稱：&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E7%BA%A2%E7%99%BD%E6%9C%BA&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;紅白機&lt;/a&gt;）上發售的《&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E7%86%B1%E8%A1%80%E8%A1%8C%E9%80%B2%E6%9B%B2&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;熱血行進曲&lt;/a&gt;》是我最早接觸的「強社交」類型作品。什麼是強社交？簡單來講就是「互相傷害、往死裡打」系列。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;448&quot; src=&quot;https://blog.amowu.com/_astro/image4.C-lf8hth_ZvFzVY.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image4.C-lf8hth_Z1kkACr.webp 640w, https://blog.amowu.com/_astro/image4.C-lf8hth_ZvFzVY.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;445&quot; src=&quot;https://blog.amowu.com/_astro/image5.BuxY6doR_Z2f8tnL.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image5.BuxY6doR_23j7BR.webp 640w, https://blog.amowu.com/_astro/image5.BuxY6doR_Z2f8tnL.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;正所謂「不打不相識」，俗話說「見面三分情」，在我小時候還沒有網際網路的時代，社交基本都靠交換玩具、聊卡通、聊漫畫、打球，以及打電動這些娛樂來聯繫感情。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;525&quot; src=&quot;https://blog.amowu.com/_astro/image6.Cq8veJ6y_ZblJDe.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image6.Cq8veJ6y_egd4m.webp 640w, https://blog.amowu.com/_astro/image6.Cq8veJ6y_ZblJDe.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;老人圍在公園涼亭下象棋，小孩圍在電視機前搏感情&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;另外還有一款類似的遊戲，同樣也影響我深遠。那就是 1995 年在 Windows 平台上的《&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%B0%8F%E6%9C%8B%E5%8F%8B%E9%BD%8A%E6%89%93%E4%BA%A4&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;小朋友齊打交&lt;/a&gt;》（簡稱：LF）。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;485&quot; src=&quot;https://blog.amowu.com/_astro/image7.jImXgU4w_Z1UOTpz.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image7.jImXgU4w_2piKL9.webp 640w, https://blog.amowu.com/_astro/image7.jImXgU4w_Z1UOTpz.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;這款遊戲最有魅力的地方在於四個人需要擠在同一塊鍵盤上操作。還記得我讀高職的時候，原本下課鈴響都是搶球場優先，但自從學校替每間教室配一台爛電腦之後，我們的下課目標就變成了搶鍵盤。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;320&quot; height=&quot;240&quot; src=&quot;https://blog.amowu.com/_astro/image8.BLQkIvYj_Z2mffjR.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image8.BLQkIvYj_Z2mffjR.webp 320w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;因為我很喜歡這兩款遊戲帶來的同樂氛圍，所以大學的畢業專題便以此為靈感，開發了一款多人連線的對戰遊戲。那段埋首寫程式的日子為我日後的遊戲程式設計打下基礎，並順利應徵上第一份正職工作。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;350&quot; src=&quot;https://blog.amowu.com/_astro/image9.BmXes-wq_Z14El83.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image9.BmXes-wq_DL0iN.webp 640w, https://blog.amowu.com/_astro/image9.BmXes-wq_Z14El83.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;還是學生的時候，同學或是三五好友的時間較一致，放學下課比較沒事，想打電動，約一約就可以隨時開始打了。&lt;/p&gt;
&lt;p&gt;可是隨著年紀增長，你的朋友在上班，你的朋友在陪女友，你還能夠朋友幾個人聚在一起？幾個一起玩遊戲的好朋友，也許從一代二代認識的，現在，我們要一起玩個四代，很難了。&lt;/p&gt;
&lt;p&gt;所以，你看朋友之間都已經這麼難聚在一起玩遊戲了，如果還要把這麼珍貴的時間花在與那些外掛氾濫的遊戲玩家對賭，豈不浪費人生。&lt;/p&gt;
&lt;p&gt;所以我還是堅持買主機玩線下的單人遊戲或是可以同樂的多人遊戲，因為買的是一個尊嚴。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;131&quot; src=&quot;https://blog.amowu.com/_astro/image10.DTAgBUYH_1t9dPS.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image10.DTAgBUYH_1D2aRb.webp 640w, https://blog.amowu.com/_astro/image10.DTAgBUYH_1t9dPS.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;總結：《熱血行進曲》和《小朋友齊打交》這些「強社交」類型的遊戲，影響了我的朋友關係與職涯發展，故列為第十位。&lt;/p&gt;
&lt;p&gt;下一期，影響我一生的第二款遊戲，我打算介紹某款台灣製作的 PC 遊戲，敬請期待。&lt;/p&gt;</content:encoded><category>Weekly</category></item><item><title>向死而生</title><link>https://blog.amowu.com/posts/2019-04-22-weekly-018/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2019-04-22-weekly-018/</guid><description>HBO 當家美劇《權力遊戲》終於迎來了最終季，在上周開始全球播映。</description><pubDate>Mon, 22 Apr 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;HBO 當家美劇《權力遊戲》終於迎來了最終季，在上周開始全球播映。&lt;/p&gt;
&lt;p&gt;首集上線之後馬上就創造了 5,500 萬次的「&lt;a href=&quot;https://www.theverge.com/2019/4/17/18412159/game-of-thrones-got-season-8-premiere-pirated-55-million-times-first-24-hours-hbo&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;盜版收視&lt;/a&gt;」。而相較於盜版來源，官方的收看來源總計也不過 1,740 萬名觀眾。&lt;/p&gt;
&lt;p&gt;即便是在美國這種相對容易訴諸法律的國家，在非法收視排行榜上仍居第三位。&lt;/p&gt;
&lt;p&gt;雖然在台灣可以合法收看該影集，但因為正版授權的版本都經過了「&lt;a href=&quot;https://ent.ltn.com.tw/news/breakingnews/1286231&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;審查&lt;/a&gt;」，不少片段遭到刪剪，因此有些人轉向收看盜版的「完整版本」。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;過去許多軟體開發商設計很多數位鎖，想要防止被盜版，但歷史證明都沒有用。開發數位鎖不但浪費了軟體開發商大筆的時間和金錢，最後鎖住的卻往往是有良心乖乖買正版的使用者。 — — 《&lt;a href=&quot;https://blog.techbridge.cc/2018/11/14/future-of-software/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;軟體如何賺錢？&lt;/a&gt;》&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;作為一個追劇近 10 年的死忠粉絲，一直不解為何劇組要將這部劇的標題命名為 Game of Thrones（權力遊戲），而不是原作小說的書名「冰與火之歌」？&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;523&quot; src=&quot;https://blog.amowu.com/_astro/image1.C8y-qNB9_Z2doUIG.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image1.C8y-qNB9_ZEvnML.webp 640w, https://blog.amowu.com/_astro/image1.C8y-qNB9_Z2doUIG.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;直到 2013 年的某一天，當時《權力遊戲》的進度還只是在第三季的時候，我就被知乎這篇「&lt;a href=&quot;https://www.zhihu.com/question/20718470/answer/16291861&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;《冰與火之歌》為什麼取名叫『冰與火之歌』？&lt;/a&gt;」之中的一則回答給驚豔：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A Son（瓊恩·雪諾）g of Ice（萊安娜·史塔克）and Fire（雷加·坦格利安）&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;368&quot; src=&quot;https://blog.amowu.com/_astro/image2.uWieDE6P_Z19Rp0T.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image2.uWieDE6P_yG1ow.webp 640w, https://blog.amowu.com/_astro/image2.uWieDE6P_Z19Rp0T.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;要知道雪諾的身世之謎這個秘密，可是一直到了 2017 年第七季的最後一集《&lt;a href=&quot;https://www.youtube.com/watch?v=WZPPdXB_vYU&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;The Dragon and the Wolf&lt;/a&gt;》才被公諸於世。由此可見這個伏筆埋得之深，想必連劇組自己當初也沒想到。&lt;/p&gt;
&lt;p&gt;所以相比之下，我其實是更喜歡「冰與火之歌」這組命名的。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;但是最近隨著知識的長進，接觸了更多的人情世故之後，我原先的這個想法開始有了改變，漸漸能理解為什麼要叫《權力遊戲》。&lt;/p&gt;
&lt;p&gt;除了「電影要比小說更貼近大眾」這個理由之外，其實這個世界上的所有人類活動，你都可以看作是一次次「權力的遊戲」。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;295&quot; height=&quot;475&quot; src=&quot;https://blog.amowu.com/_astro/image3.DudDJrf8_WMzGf.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image3.DudDJrf8_WMzGf.webp 295w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;最近讀了一本書，書名是《有限與無限的遊戲》。書名中的「Games」除了翻譯「遊戲」之外，也可以翻為「博弈」。&lt;/p&gt;
&lt;p&gt;那麼「有限與無限的博弈」是什麼意思？&lt;/p&gt;
&lt;p&gt;本書作者把世界上所有的人類活動都看作是一次又一次博弈。&lt;/p&gt;
&lt;p&gt;其中大量的活動是「有限」的博弈，小到下棋、跟人吵架，大到經營一家企業、發起一場戰爭等等。這種博弈有個特點，那就是以「取勝」為目的，這也是為什麼叫有限博弈，因為贏了就結束了，只有輸贏一種結局。&lt;/p&gt;
&lt;p&gt;還有一種「無限」的博弈，這種博弈是以「延續」為目的，例如：婚姻、文化、宗教還有生命等等，在延續的過程中會產生出無數種可能的結局。&lt;/p&gt;
&lt;p&gt;這種世界觀有什麼用呢？它可以解答一個人類的終極問題：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;人的一生該怎麼過？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;什麼是有價值的，什麼是有意義的。人要是死了，一切都是白談，所以「生命」這個無限遊戲想必是最值得追求的。那當我們把時間維度放大到生死邊界的時候，該追逐什麼就一目了然了。&lt;/p&gt;
&lt;p&gt;玩有限遊戲時的一次次成敗，不過是我們人生中的某一次體驗、某一段經歷，而讓我們的人生真正產生價值的，是那些可以不間斷延續下去的事物。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;有限遊戲在邊界內玩，無限遊戲卻是在和邊界玩，探索、改變邊界本身。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;有限遊戲看起來像在做一件有期限的事情，我們做的時候常常假定自己是能永遠活下去的；與之相反，無限遊戲是一件沒有邊界的事情，所以當我們做的時候，是抱著自己一定會死的心情。&lt;/p&gt;
&lt;p&gt;有限遊戲的參與者為了取勝，會在有限的時間維度下自願給自己設定許多邊界，摒棄自己一部分的自由；無限遊戲的參與者會將時間維度拉長到一生，抱著向死而生的心境去生活，透過延續各種無限遊戲，來達成根本自由的狀態。&lt;/p&gt;
&lt;p&gt;我們的社會就是一個有限遊戲，目的是取得勝利從而獲得權力。社會為了提高自身獲勝的機率，設置了一系列的運行機制，用來確保社會中的每一位公民都在玩同一種有限遊戲。&lt;/p&gt;
&lt;p&gt;既然我們都是生活在社會中的公民，那麼是不是注定一生中的大量時間只能被用來玩各種有限遊戲？&lt;/p&gt;
&lt;p&gt;這本書的最大價值，就是為我們提供了一個新的方式去理解世界的認知模型，或許我們應該用無限遊戲的心態去玩有限遊戲，發現生命的可能性。&lt;/p&gt;
&lt;p&gt;就像傳統電子遊戲也分「開放世界遊戲」、「沙盒遊戲」和「箱庭遊戲」，它們不分誰優誰劣，這些遊戲會根據不同的目標群體，有著不同的優點和缺點。但是可以確定的是不論是什麼遊戲類型，在合適的製作人手中都能創作出最棒的遊戲作品。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;有限遊戲是有劇本的，而無限遊戲則是傳奇的。&lt;/p&gt;
&lt;/blockquote&gt;</content:encoded><category>Weekly</category></item><item><title>為什麼靜香要嫁給大雄？</title><link>https://blog.amowu.com/posts/2019-04-15-weekly-017/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2019-04-15-weekly-017/</guid><description>2019 年可以說是 3DCG 動畫電影的一年，尤其是改編經典故事的作品。除了年初的《艾莉塔：戰鬥天使》之外，還有近期上映的《小飛象》，以及未來即將播出的《名偵探皮卡丘》和《獅子王》。</description><pubDate>Mon, 15 Apr 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;123&quot; src=&quot;https://blog.amowu.com/_astro/image1.DokDxkTO_t038F.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image1.DokDxkTO_ZoH9Br.webp 640w, https://blog.amowu.com/_astro/image1.DokDxkTO_t038F.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;2019 年可以說是 3DCG 動畫電影的一年，尤其是改編經典故事的作品。除了年初的《艾莉塔：戰鬥天使》之外，還有近期上映的《小飛象》，以及未來即將播出的《名偵探皮卡丘》和《獅子王》。&lt;/p&gt;
&lt;p&gt;知名日本遊戲公司 SQUARE ENIX 日前宣布，《勇者鬥惡龍》系列也將推出 3DCG 動畫電影《勇者鬥惡龍 你的故事（暫譯）》。預定 8 月 2 日在日本上映。&lt;/p&gt;
&lt;p&gt;身為一個 DQ 的粉絲，看到這則新聞的時候很興奮，覺得不吹一波對不起自己的情懷。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://youtu.be/pUFO5uE2FeM?si=BUieXr6kb0vapde&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://youtu.be/pUFO5uE2FeM?si=BUieXr6kb0vapde&lt;/a&gt;_&lt;/p&gt;
&lt;p&gt;《勇者鬥惡龍 你的故事》是由過去同樣製作過 3DCG 動畫電影《STAND BY ME 哆啦A夢》的團隊負責。&lt;/p&gt;
&lt;p&gt;作為一個從「小叮噹」漫畫時代開始看起的過來人，這裡順便科普一下《STAND BY ME 哆啦A夢》的故事，其實是結合了多部原作中的經典劇情，例如《從未來之國千里迢迢而來》、《雪山上的浪漫史》、《大雄的結婚前夜》、《再見哆啦A夢》和《哆啦A夢回來了》等篇目。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;295&quot; src=&quot;https://blog.amowu.com/_astro/image2.N4JcblRh_Z1vuV80.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image2.N4JcblRh_bclBo.webp 640w, https://blog.amowu.com/_astro/image2.N4JcblRh_Z1vuV80.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;其中在看《大雄的結婚前夜》的當下，我都抱持「為什麼藤子不二雄要讓靜香嫁給大雄？」的疑問。直到這段「靜香與爸爸的對話」：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;靜香：「我很擔心，能不能好好地與大雄生活」
爸爸：「我認為你選擇大雄是完全正確的。大雄會為別人的幸福而高興，為別人的不幸而傷心。這對於一個人來說是最重要的東西」&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;當時三觀受到的衝擊，可以說不亞於「 &lt;a href=&quot;https://blog.amowu.com/posts/2019-01-28-weekly-007/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;為什麼井上雄彥不讓湘北拿冠軍？&lt;/a&gt; 」一樣的程度。&lt;/p&gt;
&lt;p&gt;話題回到 DQ。說來也巧合，前陣子才剛好和同事聊起這系列的想法。基本上《勇者鬥惡龍》的正統系列我只玩過 4、5、6 和 9 代，其中前面三代因為故事有關聯，被合稱作「天空系列三部曲」，也是被跨平台重製或移植次數最多的一個系列。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;240&quot; src=&quot;https://blog.amowu.com/_astro/image3.DOCZjtGe_kxwPX.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image3.DOCZjtGe_ZtHxve.webp 640w, https://blog.amowu.com/_astro/image3.DOCZjtGe_kxwPX.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;如果只讓我推薦一款 DQ 給你，我會毫不猶豫的選擇 5 代《天空的新娘》。如果讓我選出心目中最棒的穿越故事劇情，我也會毫不猶豫的告訴你是《天空的新娘》。&lt;/p&gt;
&lt;p&gt;而這次的電影《勇者鬥惡龍 你的故事》就是以《勇者鬥惡龍 5 天空的新娘》為原案，所以爆米花可以先準備好了，到時候我們電影院報到（如果台灣會上映的話⋯⋯）。&lt;/p&gt;</content:encoded><category>Weekly</category></item><item><title>我們與惡的距離</title><link>https://blog.amowu.com/posts/2019-04-08-weekly-016/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2019-04-08-weekly-016/</guid><description>知名策略遊戲《文明帝國》有個被玩家吐槽的 Bug。遊戲中每位國家領導人皆擁有預設的「侵略指數」，而其中「和平主義者」甘地的預設數值為 1，這鮮明地反映了他在真實世界的人格特質：不挑起戰爭，不侵犯他國。</description><pubDate>Mon, 08 Apr 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;知名策略遊戲《&lt;a href=&quot;https://zh.wikipedia.org/zh-tw/%E6%96%87%E6%98%8E%E7%B3%BB%E5%88%97&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;文明帝國&lt;/a&gt;》有個被玩家吐槽的 Bug。遊戲中每位國家領導人皆擁有預設的「侵略指數」，而其中「和平主義者」甘地的預設數值為 1，這鮮明地反映了他在真實世界的人格特質：不挑起戰爭，不侵犯他國。&lt;/p&gt;
&lt;p&gt;然而，若玩家在遊戲中採取民主政體時，國家的侵略數值會自動下降 2 點。此時，甘地的數值會變為 -1。有趣的是，遊戲中並沒有注意到負數的判斷，導致該數字直接跳至最高的 255 點，聖雄也因此褪下和平外衣，瞬間化身為「核平主義者」！&lt;/p&gt;
&lt;p&gt;這故事告訴我們寫「&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E9%82%8A%E7%95%8C%E6%A1%88%E4%BE%8B&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;邊界測試&lt;/a&gt;」的重要性。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;524&quot; src=&quot;https://blog.amowu.com/_astro/image1.Bf5_pONF_Z1JlfO6.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image1.Bf5_pONF_Zre60F.webp 640w, https://blog.amowu.com/_astro/image1.Bf5_pONF_Z1JlfO6.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;上周這篇《&lt;a href=&quot;https://blog.amowu.com/2019/04/weekly-015.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;上帝擲骰子嗎？&lt;/a&gt;》一文中，我提到了幾位自己很嚮往的程式設計師，其中一位 Jeff Dean 有朋友問我，他是誰？&lt;/p&gt;
&lt;p&gt;在 Google，如果你問最牛的工程師是誰，大家會告訴你是 Google 的大腦 — — &lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%82%91%E5%A4%AB%C2%B7%E8%BF%AA%E6%81%A9&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;傑夫·迪恩&lt;/a&gt;（Jeff Dean）。他不但開創了雲計算時代，也是深度學習算法的發明人。另外，如果問收入最高的工程師是誰，也是迪恩。他的收入比大部分 VP 要高很多。&lt;/p&gt;
&lt;p&gt;最後，如果你問作為一個軟體工程師能走多遠，要是你能像迪恩那樣當上美國工程院院士，就可以寫程式一輩子。&lt;/p&gt;
&lt;p&gt;更多關於傑夫·迪恩的故事，請參見《&lt;a href=&quot;https://m.igetget.com/share/course/pay/detail/4/42&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;吳軍的谷歌方法論&lt;/a&gt;》。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;489&quot; src=&quot;https://blog.amowu.com/_astro/image2.CyBY7Dx5_ZBoVp.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image2.CyBY7Dx5_ZJERyC.webp 640w, https://blog.amowu.com/_astro/image2.CyBY7Dx5_ZBoVp.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;在西方，一座教堂蓋上百年是常有的事情，中間自然要更換很多建築師，等到教堂完工時，通常和最初的設計會有很大差別。&lt;/p&gt;
&lt;p&gt;建築大師&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%AE%89%E4%B8%9C%E5%B0%BC%C2%B7%E9%AB%98%E8%BF%AA&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;高第&lt;/a&gt;為了保證「&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%9C%A3%E5%AE%B6%E5%A0%82&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;聖家堂&lt;/a&gt;」的設計思想不至於被後來的設計師改動，一反先建造內部的主體，再裝上外立面的做法，而是先從外立面開始建設，這樣後人就不會縮小教堂的規模了。&lt;/p&gt;
&lt;p&gt;人們都說寫程式就像蓋房子，或許我們能從這些偉大的建築師之中，發掘出他們的智慧來應用。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;540&quot; height=&quot;560&quot; src=&quot;https://blog.amowu.com/_astro/image3.DMLqIjwL_cfFVW.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image3.DMLqIjwL_cfFVW.webp 540w&quot; /&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;中研院院士&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E8%83%A1%E4%BD%9B_%28%E8%87%BA%E7%81%A3%29&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;胡佛&lt;/a&gt;曾經說道，民主選舉有三個層次：國家認同、憲政結構、基本政策。&lt;/p&gt;
&lt;p&gt;美國沒有國家認同的問題，除非夏威夷想要「夏獨」。憲政結構也就是一個「聯邦制」。所以選舉最後要辯論的就只剩下是否加減稅、墮胎或同性戀是否合法化這些「基本政策」問題。&lt;/p&gt;
&lt;p&gt;台灣的困境是，國家認同的問題還沒解決，每每選舉佔據新聞版面的都是統獨議題，甚至連憲政結構的部分也在討論要換「總統制」或是「內閣制」之類的，導致與民生最相關的基本政策進展緩慢。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;a href=&quot;https://youtu.be/hFyl9SvALk0?si=SX-lVKmcjokNN4Vx&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://youtu.be/hFyl9SvALk0?si=SX-lVKmcjokNN4Vx&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;傳統的射擊遊戲裡，因為爆頭即死，所以玩家傾向提高「槍法」高效殺敵。&lt;/p&gt;
&lt;p&gt;但在真實世界，作戰部隊追求的是無傷殺敵，因為受傷的代價太大了。為了不受傷，哪怕降低效率和浪費資源也在所不惜。&lt;/p&gt;
&lt;p&gt;有個統計，戰場上平均每兩萬發子彈才能有一發擊中敵人，這些子彈主要都拿來做火力壓制，「彈幕」一詞就來源這裡。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;276&quot; src=&quot;https://blog.amowu.com/_astro/image4.D-QyTjih_8mrJa.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image4.D-QyTjih_1flx6V.webp 640w, https://blog.amowu.com/_astro/image4.D-QyTjih_8mrJa.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;東西方對龍的印象其實挺兩極的，前者認為神聖，後者認為邪惡。有一說，西方神話中所提到的魔鬼形象，恰巧與東方神話中所提到的神相似（角、鱗片）。或許，在西方信仰的這夥外星文明裡，和東方信仰的文明剛好是處於對立面？比方說：大洪水是西方神發動，卻是女媧給堵上的。&lt;/p&gt;
&lt;p&gt;— — 《&lt;a href=&quot;https://www.youtube.com/watch?v=0vomd4ymSLc&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;老高與小茉 Mr &amp;amp; Mrs Gao&lt;/a&gt;》&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;198&quot; src=&quot;https://blog.amowu.com/_astro/image5.BucAW1ft_Z2pkztz.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image5.BucAW1ft_1MxAuX.webp 640w, https://blog.amowu.com/_astro/image5.BucAW1ft_Z2pkztz.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;郵件是一個重要的溝通工具。在職場上甚至比見面和開會還要重要。很多人這輩子可能彼此只會透過郵件溝通。那麼一封好的郵件應該長什麼樣呢？我們與人見面的基本禮儀，不一定要打扮得花枝招展，而是保持得乾乾淨淨。一封好的郵件也一樣。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;郵件禮儀的第一步，就是用真名&lt;/li&gt;
&lt;li&gt;標題用不到 20 字來總結這封信的核心內容&lt;/li&gt;
&lt;li&gt;真正的高手，所有文字只有一個顏色、一個大小、一個字型&lt;/li&gt;
&lt;li&gt;排版只用三種方式：分段（閱讀邏輯）、縮排（層次關係）、粗體（突出重點）&lt;/li&gt;
&lt;li&gt;每段的首句是整段的概括。用句簡短、用詞簡單、用字簡潔&lt;/li&gt;
&lt;li&gt;結尾的部分再次總結郵件的內容，並強調對方需要行動的部分&lt;/li&gt;
&lt;li&gt;收到郵件盡快回覆，代表你的能力、效率、重視的程度&lt;/li&gt;
&lt;li&gt;送出前再次檢查標題、稱呼、錯別字、附件&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;雖然 Slack 已經取代了大部分的郵件使用場景，但是我認為還是一樣受用，對於寫文章也很有幫助。&lt;/p&gt;
&lt;p&gt;— — 《&lt;a href=&quot;https://m.igetget.com/share/course/pay/detail/4/14&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;羅輯思維&lt;/a&gt;》&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;170&quot; src=&quot;https://blog.amowu.com/_astro/image6.B61EsyUm_ZaKYQM.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image6.B61EsyUm_Z2kX4HC.webp 640w, https://blog.amowu.com/_astro/image6.B61EsyUm_ZaKYQM.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Google 收購 YouTube 之後，透過關鍵幀的特徵值提取和信息指紋的比對技術，可以找出兩段影片是否相似。因此 Google 制定了一個有意思的廣告分成策略：所有影片都可以插入廣告，但廣告收益全部歸原創影片所有。這樣一來，拷貝上傳別人的影片就無法獲得收入。沒有了經濟利益，就少了很多盜版。&lt;/p&gt;
&lt;p&gt;— — 《&lt;a href=&quot;https://www.books.com.tw/products/CN11192265&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;數學之美&lt;/a&gt;》&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;394&quot; src=&quot;https://blog.amowu.com/_astro/image7.bVl3Tofs_1Nervy.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image7.bVl3Tofs_1Mt5RG.webp 640w, https://blog.amowu.com/_astro/image7.bVl3Tofs_1Nervy.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;最近同事推薦了一部公視和 HBO 合作推出的社會寫實劇 — — 《&lt;a href=&quot;https://www.pts.org.tw/theworld_betweenus/news.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;我們與惡的距離&lt;/a&gt;》。趁著連假一口氣看了四集，真的好看！&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;故事透過「無差別殺人事件」呈現不同立場的關係者視角，描摹加害者家屬與被害者家屬的心理，探究人權律法的掙扎以及精神病識，同時反思媒體現象。 — — 維基百科&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;一個好的故事不會給你絕對的答案，而是給你「思考」的空間。&lt;/p&gt;
&lt;p&gt;全天下沒有一個爸爸媽媽，要花個二十年去養一個殺人犯，加害者的家屬也可能是被害者。如果一個民主法治的國家要靠不符合程序的死刑殺人才可以討好民眾、討好媒體，那我們殺的人，並不會比殺人犯還少。&lt;/p&gt;
&lt;p&gt;這讓我想到最近一部我已經棄坑、也在討論善惡的韓劇 — — 《&lt;a href=&quot;https://programs.sbs.co.kr/drama/haechi&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;獬豸&lt;/a&gt;》。片頭一段令人印象深刻的話：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;獬豸，能審判善惡的傳說中的神獸。然而，你知道為何它只存在於傳說中嗎？因為想在現實中審判善惡，是不可能的。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;感謝良心電視台公視，讓我們看到台劇並沒有比較差，台灣歐爸還是有希望的（雖然我比較喜歡賈靜雯的演技）。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;最後，作為同樣感謝的贊助商，這期就特別留個百萬版面（？）給 HBO 放廣告。4 月 11 日，《&lt;a href=&quot;http://hboasia.com/HBO/en-tw/shows/game-of-thrones/season8/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;冰與火之歌&lt;/a&gt;》最終季即將播映！究竟最後會是誰坐上鐵王座！？&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;1011&quot; src=&quot;https://blog.amowu.com/_astro/image8.2GIrsqaN_Z2cDNgp.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image8.2GIrsqaN_ZaS7vy.webp 640w, https://blog.amowu.com/_astro/image8.2GIrsqaN_Z2cDNgp.webp 700w&quot; /&gt;&lt;/p&gt;</content:encoded><category>Weekly</category></item><item><title>上帝擲骰子嗎？</title><link>https://blog.amowu.com/posts/2019-04-01-weekly-015/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2019-04-01-weekly-015/</guid><description>下圖是 Reddit 論壇上的一位大神，把近一世紀前的「第五次索爾維會議」黑白照片給上色後的結果。</description><pubDate>Mon, 01 Apr 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;下圖是 &lt;a href=&quot;https://zh.wikipedia.org/wiki/Reddit&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Reddit&lt;/a&gt; 論壇上的一位&lt;a href=&quot;https://www.reddit.com/r/pics/comments/z7a7c/einstein_bohr_curie_more_at_solvay_conference/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;大神&lt;/a&gt;，把近一世紀前的「&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E7%B4%A2%E5%B0%94%E7%BB%B4%E4%BC%9A%E8%AE%AE&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;第五次索爾維會議&lt;/a&gt;」黑白照片給上色後的結果。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;348&quot; src=&quot;https://blog.amowu.com/_astro/image1.Ds-VEtQ-_1BR0Y1.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image1.Ds-VEtQ-_2p0jnJ.webp 640w, https://blog.amowu.com/_astro/image1.Ds-VEtQ-_1BR0Y1.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E7%B4%A2%E5%B0%94%E7%BB%B4%E4%BC%9A%E8%AE%AE&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;索爾維會議&lt;/a&gt;的全名是「索爾維國際物理學化學研究會」，是由企業家&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E6%AC%A7%E5%86%85%E6%96%AF%E7%89%B9%C2%B7%E7%B4%A2%E5%B0%94%E7%BB%B4&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;索爾維&lt;/a&gt;於 1912 年在比利時布魯塞爾創辦的一個學會。通過邀請世界著名的物理學家和化學家對前沿問題進行討論的會議。每三年舉辦一次。在物理學的發展史上占有重要地位。&lt;/p&gt;
&lt;p&gt;當中最著名的一次索爾維會議是 1927 年召開的第五次索爾維會議。此次會議主題為「電子和光子」，世界上最主要的物理學家聚在一起討論量子理論。&lt;/p&gt;
&lt;p&gt;這次會議上最令後世津津樂道的故事，當屬&lt;a href=&quot;https://zh.wikipedia.org/zh-tw/%E9%98%BF%E5%B0%94%E4%BC%AF%E7%89%B9%C2%B7%E7%88%B1%E5%9B%A0%E6%96%AF%E5%9D%A6&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;愛因斯坦&lt;/a&gt;和&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%B0%BC%E5%B0%94%E6%96%AF%C2%B7%E7%8E%BB%E5%B0%94&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;波耳&lt;/a&gt;針對「&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E4%B8%8D%E7%A1%AE%E5%AE%9A%E6%80%A7%E5%8E%9F%E7%90%86&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;不確定性原理&lt;/a&gt;」的鬥嘴橋段：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;愛因斯坦：「波耳，上帝從不擲骰子」
波耳：「愛因斯坦，不要告訴上帝應該怎麼做」&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;參加這次「第五次索爾維會議」的 29 人中，有 17 人最後獲得了諾貝爾獎。&lt;/p&gt;
&lt;p&gt;這讓我想起 1992 年奧運會的籃球項目，美國隊第一次由 NBA 球星出征，號召了籃球世界最強的 12 個人，組成了這支籃球史上最傳奇的「&lt;a href=&quot;https://zh.wikipedia.org/wiki/1992%E5%B9%B4%E5%A4%8F%E5%AD%A3%E5%A5%A7%E6%9E%97%E5%8C%B9%E5%85%8B%E9%81%8B%E5%8B%95%E6%9C%83%E7%BE%8E%E5%9C%8B%E7%94%B7%E5%AD%90%E7%B1%83%E7%90%83%E9%9A%8A&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;夢幻隊&lt;/a&gt;」(Dream Team) 。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;450&quot; src=&quot;https://blog.amowu.com/_astro/image2.BUzTmXzs_ZLhkqb.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image2.BUzTmXzs_ZTENvW.webp 640w, https://blog.amowu.com/_astro/image2.BUzTmXzs_ZLhkqb.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;好奇之下，想說 IT 界有沒有類似的群星合照？（例如 &lt;a href=&quot;https://zh.wikipedia.org/wiki/%E6%9E%97%E7%BA%B3%E6%96%AF%C2%B7%E6%89%98%E7%93%A6%E5%85%B9&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Linus&lt;/a&gt;、&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E7%B4%84%E7%BF%B0%C2%B7%E5%8D%A1%E9%A6%AC%E5%85%8B&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;John Carmack&lt;/a&gt;、&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%82%91%E5%A4%AB%C2%B7%E8%BF%AA%E6%81%A9&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Jeff Dean&lt;/a&gt; 等等⋯⋯）&lt;/p&gt;
&lt;p&gt;查了一下，雖然算不上電腦科學家（敲程式的都不喜歡拍照？），但大概也是比較有名的合照，他們就是「&lt;a href=&quot;https://zh.wikipedia.org/wiki/PayPal%E9%BB%91%E6%89%8B%E9%BB%A8&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;PayPal 黑手黨&lt;/a&gt;」。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;585&quot; src=&quot;https://blog.amowu.com/_astro/image3.oL2sv1ul_1s7M0D.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image3.oL2sv1ul_ZdMAU.webp 640w, https://blog.amowu.com/_astro/image3.oL2sv1ul_1s7M0D.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;PayPal 黑手黨（PayPal Mafia）是指一群前 &lt;a href=&quot;https://zh.wikipedia.org/zh-tw/PayPal&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;PayPal&lt;/a&gt; 員工。他們在離開 PayPal 再次創業建立的企業包括 Tesla、LinkedIn、YouTube 和 Yelp。其中的三個成員，包括&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E4%BC%8A%E9%9A%86%C2%B7%E9%A9%AC%E6%96%AF%E5%85%8B&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;馬斯克&lt;/a&gt;後來都成為了億萬富翁。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;485&quot; height=&quot;419&quot; src=&quot;https://blog.amowu.com/_astro/image4.iUSryvK2_Z19QB6e.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image4.iUSryvK2_Z19QB6e.webp 485w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;PayPal 黑手黨是 2001 年網際網路泡沫破滅後，面向消費者網際網路公司重新崛起的代表。&lt;/p&gt;
&lt;p&gt;他們成功的背後，除了在 PayPal 時期的學習、矽谷的環境，以及各自技能的多樣性之外，最主要的原因在於他們擁有「&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%85%AB%E5%8F%9B%E9%80%86&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;八叛徒&lt;/a&gt;」的矽谷基因。離開 PayPal 後，在激烈競爭的環境下，儘管經歷挫折，仍然維持強烈且持久的情誼，互相支援和信任，一起共同奮鬥。&lt;/p&gt;
&lt;p&gt;在矽谷還沒有誕生之前，全美國（或全世界）的叛逆者們都喜歡往矽谷地區跑，因為那裡遠離美國東部的政治、文化和金融中心，更遠離歐洲。&lt;/p&gt;
&lt;p&gt;不喜歡美國傳統文化的人就跑到舊金山灣區（當時還不叫矽谷），通過文藝的形式（&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%AC%89%E7%9A%AE%E5%A3%AB&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;嬉皮&lt;/a&gt;），或者政治的形式（抗議）來實現他們改造世界的夢想。&lt;/p&gt;
&lt;p&gt;這些充滿怪想法中的一些人是搞技術的，天天琢磨著顛覆現有產業，最終成功創辦出了一間又一間小公司。&lt;/p&gt;
&lt;p&gt;科技實際上只是這些想改變世界的叛逆者們的工具而已。&lt;/p&gt;</content:encoded><category>Weekly</category></item><item><title>Story of Your Life</title><link>https://blog.amowu.com/posts/2019-03-25-weekly-014/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2019-03-25-weekly-014/</guid><description>語言本能</description><pubDate>Mon, 25 Mar 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;語言本能&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#語言本能&quot;&gt;語言本能&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;329&quot; height=&quot;500&quot; src=&quot;https://blog.amowu.com/_astro/image1.ByeNFNsQ_ZF7Sro.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image1.ByeNFNsQ_ZF7Sro.webp 329w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;本周閱讀 &lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%8F%B2%E8%BF%AA%E8%8A%AC%C2%B7%E5%B9%B3%E5%85%8B&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Steven Pinker&lt;/a&gt;「語言與人性四部曲」的《&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E8%AA%9E%E8%A8%80%E6%9C%AC%E8%83%BD&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;語言本能：探索人類語言進化的奧秘&lt;/a&gt;》時，提到了「&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E8%96%A9%E4%B8%95%E7%88%BE-%E6%B2%83%E5%A4%AB%E5%81%87%E8%AA%AA&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;沙皮爾-沃爾夫假說&lt;/a&gt;」。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;沙皮爾-沃爾夫假說（Sapir–Whorf hypothesis）是一個關於人類語言的假說，由語言學家兼人類學家 Edward Sapir 及其學生 Benjamin Whorf 所提出，是一門心理學及語言學的假說。這項學說認為，人類的思考模式受到其使用語言的影響，因而對同一事物時可能會有不同的看法。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;S-W Hypothesis 還可以分為強弱兩種，分別是 Linguistic relativity（語言相對論）和 Linguistic determinism（語言決定論）。前者認為，不同語言在結構上的差異，對認知過程有所影響的。譬如一個人認為彩虹有幾種顏色，是由他的母語有那些基本顏色的詞彙來決定的。後者則認為語言極大程度地決定了一個人的世界觀。譬如愛斯基摩人對於雪的認識遠超於其它母語的人，因為他們對於不同狀態的雪（地上的雪、正飄下的雪、堆積的雪、雪堆）都有不同的名字，認為彼此是完全不一樣的東西。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;611&quot; height=&quot;293&quot; src=&quot;https://blog.amowu.com/_astro/image2.B2T8VnlI_Z18ii7F.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image2.B2T8VnlI_Z18ii7F.webp 611w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;語言可以分為強未來時表述（如英語）和弱未來時表述（如中文）。 使用強未來時表述語言使人從認知上將現在和未來區分開來，使未來感覺起來更加遙遠。而使用弱未來時表述語言的人，認為現在和未來是連接相通的，而他們現在的行為會對未來產生更大的影響，於是他們會進行更多著眼於未來的事，例如儲蓄。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;414&quot; src=&quot;https://blog.amowu.com/_astro/image3.yk1CN3OX_Z24AGCr.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image3.yk1CN3OX_lfTbC.webp 640w, https://blog.amowu.com/_astro/image3.yk1CN3OX_Z24AGCr.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;在我們博大精深的中文裡，如果你想稱呼一位男性長輩親戚，有伯父、叔叔、舅舅、姑丈、姨丈⋯⋯，而英文就一個詞「uncle」。中文讓我們對於家族關係更加敏感。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;237&quot; src=&quot;https://blog.amowu.com/_astro/image4.B0BkNcs6_Z2k6MiP.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image4.B0BkNcs6_1TmmoA.webp 640w, https://blog.amowu.com/_astro/image4.B0BkNcs6_Z2k6MiP.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;中文的所有數字都只有一個音節，但英文有多個音節（如 seven）。因為音節短，所以在有限的工作記憶中佔優勢。另一方面，像中文和日語都有規律可循的數字系統，如 11 ＝ 十一（對比英文中的 eleven)，潛移默化地加強了亞洲人對數字關係的認識。這樣的優勢被一部分研究認為是亞洲數學天賦的原因。&lt;/p&gt;
&lt;p&gt;除了影響人的顏色、時間觀、金錢觀、人際觀、數字關之外，語言還有可能影響其它像空間、因果、個性（多重人格）、工作等等，有興趣的人可以看這篇《&lt;a href=&quot;https://www.zhihu.com/question/27797363&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;使用不同的語言，會對人們的思維方式產生怎樣的影響？&lt;/a&gt;》&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;462&quot; src=&quot;https://blog.amowu.com/_astro/image5.BRy0Rfem_Z1w5j8V.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image5.BRy0Rfem_Z1QGrJQ.webp 640w, https://blog.amowu.com/_astro/image5.BRy0Rfem_Z1w5j8V.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;華裔科幻作家&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%A7%9C%E5%B3%AF%E6%A5%A0&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;姜峯楠&lt;/a&gt;的短篇小說《&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E4%BD%A0%E4%B8%80%E7%94%9F%E7%9A%84%E6%95%85%E4%BA%8B&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;妳一生的預言&lt;/a&gt;》（Story of Your Life）就是基於沙皮爾-沃爾夫假說來架構的故事，並且在 2016 年改編成電影《&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E9%99%8D%E4%B8%B4_%28%E7%94%B5%E5%BD%B1%29&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;異星入境&lt;/a&gt;》（Arrival）上映。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;348&quot; height=&quot;348&quot; src=&quot;https://blog.amowu.com/_astro/image6.BlqQPHcp_ZvAKIB.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image6.BlqQPHcp_ZvAKIB.webp 348w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;故事主要講述身為語言學家的女主角 Louise，受美國軍方征召，負責研究來訪地球的外星生物「七腳族」的語言。&lt;/p&gt;
&lt;p&gt;七腳族告訴 Louise 3000 年後的未來，牠們的種族會需要人類的幫助，這就是牠們來地球的目的。作為回報，七腳族分享了一項「武器」 — — 語言，它可以改變人類對時間的線性感知，使他們能夠體驗尚未發生的「未來記憶」。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;292&quot; src=&quot;https://blog.amowu.com/_astro/cover.5VjlRc37_Z1mgcFu.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/cover.5VjlRc37_2th6ET.webp 640w, https://blog.amowu.com/_astro/cover.5VjlRc37_Z1mgcFu.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;人類過去認為宇宙是一個線性時空，以為時間是單向、不可逆的，但是來訪的七腳族外星人教導人類時間和空間一樣，是同時存在的，時間的過去和未來都像空間一樣是展開在眼前的，這也表現在牠們的語言中。當 Louise 學會外星語言，才恍然大悟，開始「回憶」起未來的事。&lt;/p&gt;
&lt;h2 id=&quot;後記&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#後記&quot;&gt;後記&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;本身是一位程式設計師，對於語言會改變思維這件事也深有同感。&lt;/p&gt;
&lt;p&gt;雖然工程師的語言鄙視鏈中常常出現 C++、Java、PHP、Python 這些常客，但其實都脫離不了&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;物件導向式&lt;/a&gt;語言的&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E6%8C%87%E4%BB%A4%E5%BC%8F%E7%B7%A8%E7%A8%8B&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;指令式程式設計&lt;/a&gt;（Imperative programming）思維。&lt;/p&gt;
&lt;p&gt;直到接觸了&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%87%BD%E6%95%B0%E5%BC%8F%E7%BC%96%E7%A8%8B&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;函數式程式設計&lt;/a&gt;（functional programming）的語言（如 Erlang、Lisp、Scala）之後，才發現另一種不同的程式設計思維方式。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;270&quot; src=&quot;https://blog.amowu.com/_astro/image7.vfu5X_oG_Z1t5P6w.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image7.vfu5X_oG_Z2idPu9.webp 640w, https://blog.amowu.com/_astro/image7.vfu5X_oG_Z1t5P6w.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;最近在學習 RxJS 的時候，發現《異星入境》這種從「線性時空」轉換至「非線性時空」的思維方式，對於理解 observable 的 stream 概念很有幫助。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;316&quot; src=&quot;https://blog.amowu.com/_astro/image8.CisFXVGL_Z1XSFit.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image8.CisFXVGL_rs6yh.webp 640w, https://blog.amowu.com/_astro/image8.CisFXVGL_Z1XSFit.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;638&quot; height=&quot;359&quot; src=&quot;https://blog.amowu.com/_astro/image9.5jKUU2v7_ZNctae.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image9.5jKUU2v7_ZNctae.webp 638w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;《異星入境》是少數我認為同時結合硬科幻與軟科幻的優秀電影作品，另一部則是神作《&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E6%98%9F%E9%99%85%E7%A9%BF%E8%B6%8A&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;星際效應&lt;/a&gt;》（Interstellar）。&lt;/p&gt;</content:encoded><category>Weekly</category></item><item><title>天下熙熙，皆為利來；天下攘攘，皆為利往</title><link>https://blog.amowu.com/posts/2019-03-18-weekly-013/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2019-03-18-weekly-013/</guid><description>文摘</description><pubDate>Mon, 18 Mar 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;文摘&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#文摘&quot;&gt;文摘&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;大賣空&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#大賣空&quot;&gt;《大賣空》&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;《&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%A4%A7%E8%B3%A3%E7%A9%BA&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;大賣空&lt;/a&gt;》（The Big Short）是一本由 Michael Lewis 所著，講述 2000 年代「&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E6%B3%A1%E6%B2%AB%E7%BB%8F%E6%B5%8E&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;泡沫經濟&lt;/a&gt;」題材的暢銷書籍。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;257&quot; height=&quot;386&quot; src=&quot;https://blog.amowu.com/_astro/image1.BZPEGykH_F9YTM.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image1.BZPEGykH_F9YTM.webp 257w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;賣空，也可以稱作「空頭」，是貫穿全書的一個核心概念。&lt;/p&gt;
&lt;p&gt;說到「金融」這個詞，簡單一點的，股票、債券、基金，還有形形色色的理財產品；複雜一點的，結構性的資產證券化、衍生類的期權、期貨等等。但其實金融產品說白了，它就是一份「合約」。&lt;/p&gt;
&lt;p&gt;只要是合約，就有買賣雙方：甲方和乙方。最多再有個經紀人：中間方。金融業說白了就是一個關於「風險和預測」的行業。能簽合約，通常說明雙方對一件事的風險和預測立場是不同的。覺得會漲的，我們就說他對未來「看多」，說他們是「多頭」；相反，那些覺得會跌的，是對未來「看空」，他們就是「空頭」。&lt;/p&gt;
&lt;p&gt;《大賣空》講得就是「&lt;a href=&quot;https://zh.wikipedia.org/wiki/2007%E5%B9%B4%E2%80%932008%E5%B9%B4%E7%92%B0%E7%90%83%E9%87%91%E8%9E%8D%E5%8D%B1%E6%A9%9F&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;美國次貸危機&lt;/a&gt;」之後，收穫巨大利益的四群人的故事，他們被稱作「大空頭」。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;378&quot; src=&quot;https://blog.amowu.com/_astro/cover.BW9-xI9i_fuoES.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/cover.BW9-xI9i_Dbn3j.webp 640w, https://blog.amowu.com/_astro/cover.BW9-xI9i_fuoES.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;一個人要是沒有任何成見，就算再笨，也能理解最困難的問題。但是如果一個人堅信，擺在他面前的問題他早已瞭然於胸，毫無疑慮，那就算他在聰明也無法理解最簡單的事。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;―― 托爾斯泰&lt;/p&gt;
&lt;h3 id=&quot;行動的勇氣&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#行動的勇氣&quot;&gt;《行動的勇氣》&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;336&quot; height=&quot;500&quot; src=&quot;https://blog.amowu.com/_astro/image2.DiI0ufX7_Z15vy1W.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image2.DiI0ufX7_Z15vy1W.webp 336w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;金融危機通常是指銀行過度向股票或房地產市場放貸，隨後泡沫破滅，銀行放出去的貸款收不回來，老百姓存在銀行裡的錢就取不出來，銀行不得不宣佈破產。這就是所謂的「擠兌」。一旦銀行出現了擠兌，那麼銀行首先需要保證盡量兌現儲戶的存款，而不得不停止向正常的企業發放貸款，從而導致一些企業出現資金鏈斷裂，進而出現大量的企業破產、工人失業等等一系列的惡性循環。簡單來說就是，銀行缺錢，企業缺錢，老百姓缺錢，最後經濟停擺。&lt;/p&gt;
&lt;p&gt;美國次貸危機不同。美國的金融監管碎片化，出現了很多傳統銀行體系之外、形式多樣的金融機構。它們往往不是銀行，而是某某資產管理或信貸公司，但它們卻像傳統銀行一樣涉足房貸、抵押擔保和票據融資業務。它們的資金也不是來自於儲戶的存款，而是通過一種叫做短期票據市場再回購協議，從其它銀行和基金公司吸收存款。次級債券就是這樣一種金融產品。&lt;/p&gt;
&lt;p&gt;次貸危機之後，一路調查下來才發現實在太荒唐了，沒有穩定收入的人都能貸款買 5 套房，甚至有人貸款用的還是自己家寵物狗的名字。那些看好房市的「多頭」不是瘋子就是傻子。&lt;/p&gt;
&lt;p&gt;《行動的勇氣》這本書的作者，是次貸危機期間，擔任美聯儲主席的 Ben Bernanke，講述面對金融危機的市場混亂和政治派別之間的互相攻擊，深處漩渦中的決策者們，更多的是需要勇於擔當和勇於變革的進取之心，而不是怨天尤人和推卸責任的政治技巧。&lt;/p&gt;
&lt;p&gt;美聯儲相當於台灣的央行，是美國最高級別的金融行業管理部門，美聯儲主席等同於我們的央行總裁。&lt;/p&gt;
&lt;p&gt;美聯儲主席這個職位對世界經濟的影響深遠。美元是全球通行的貨幣，一旦美聯儲做出某個決定，不僅直接影響到規模龐大的美國經濟，也同時會影響全球的經濟走勢。可以說，美聯儲主席對世界的影響力可能僅次於美國總統。&lt;/p&gt;
&lt;h2 id=&quot;新奇&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#新奇&quot;&gt;新奇&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;12-的力量&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#12-的力量&quot;&gt;12 的力量&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;現代的「&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E8%AE%B0%E6%95%B0%E7%B3%BB%E7%BB%9F&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;記數系統&lt;/a&gt;」雖然大部分已經被十進制給取代，但仍可以在曆法、度量衡、語言和音階上看見「&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%8D%81%E4%BA%8C%E8%BF%9B%E5%88%B6&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;十二進制&lt;/a&gt;」的影子。&lt;/p&gt;
&lt;p&gt;歷史上，很多古老文明中都以 12 這個數為基礎。或許是由於一年中月球繞地球轉十二圈，也有人認為這和人類一隻手有十二指骨節點有關（不包括姆指），這樣方便記數。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;437&quot; src=&quot;https://blog.amowu.com/_astro/image3.DNL-WzL8_20sqJm.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image3.DNL-WzL8_1cg0bK.webp 640w, https://blog.amowu.com/_astro/image3.DNL-WzL8_20sqJm.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;古埃及文明就將白天夜晚分別劃分為 12 部分，而從古巴比倫文明傳承到西方文化中的黃道十二宮則是將一年分為了 12 個星座。&lt;/p&gt;
&lt;p&gt;在中國文化中，十二進位在記時中也有廣泛應用。中國古代設有 12 地支，與一天的 12 個時辰對應。一個地支還對應兩個節氣，從而表示一年的二十四節氣。同時，將地支與 12 種動物對應，成為十二生肖，來表示 12 年為週期的循環。&lt;/p&gt;
&lt;p&gt;12 可以被 2、3、4、6 整除，這對度量衡制度很有用，因為在這些制度中，需要把較大的單位分成一半、三等分及四等分。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;293&quot; src=&quot;https://blog.amowu.com/_astro/image4.DzyF3v-O_Z2akujF.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image4.DzyF3v-O_25QI5d.webp 640w, https://blog.amowu.com/_astro/image4.DzyF3v-O_Z2akujF.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;這讓我想起一位我很喜歡的天文學家 &lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%8D%A1%E5%B0%94%C2%B7%E8%90%A8%E6%A0%B9&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Carl Sagan&lt;/a&gt; 撰寫的科幻小說《&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E6%8E%A5%E8%A7%B8%E6%9C%AA%E4%BE%86_%28%E5%B0%8F%E8%AA%AA%29&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;接觸未來&lt;/a&gt;》。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;如果真的有造物主的話，祂應該是十一根手指頭&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在小說的最後，主角用十一進制，把圓周率 π 一直往下計算，π 本來應該是一個無限不循環的小數，但在計算到了 10 的 20 次方之後，驚人的事情出現了，π 從隨機分布的數字，變成了全部是 0 和 1 的組合。主角讓電腦對 0 和 1 進行了排列，發現用一個特定的行數和列數排列成一個正方形方塊的話，圖形是造物主本人的簽名。&lt;/p&gt;
&lt;p&gt;為什麼在一個 π 裡藏一個圖形，是一件特別了不起的事呢？因為如果這個世間，有什麼知識是最難以撼動的。答案就是數學。&lt;/p&gt;
&lt;p&gt;Carl Sagan 認為判斷一個文明的等級，可以透過該文明可以改變的知識理論來劃分。&lt;/p&gt;
&lt;p&gt;學生時期，我們接受的教育包括：歷史、政治、語文、地理、化學、物理、數學等等。本來以為這些是確定性的知識，都有恆定的標準答案。但是，其實每種確定性的知識，都是會發生變化的。&lt;/p&gt;
&lt;p&gt;綜觀歷史，一位強大的君王，能夠改寫該文明的歷史、政治，能改變一個國家的語言，能挖一條大運河，改變地理，一直到要近代改變化學，難度才會提高很多。而對於人類來說，最難改變的是物理學。至今為止，最厲害的人類，也只能理解物理規律，沒有誰能改變物理。&lt;/p&gt;
&lt;p&gt;如果有個高等文明能夠掌握黑洞，那麼或許可以改變物理學，因為在黑洞的奇點沒有時間，引力無限大，一切物理定律都會失效。&lt;/p&gt;
&lt;p&gt;但是，黑洞也改變不了的東西，是數學。因為包括黑洞在內的天體，都是符合數學規律的。我們可以這麼總結，在自然界，物理學覆蓋所有學科，但數學覆蓋了物理。&lt;/p&gt;
&lt;p&gt;所以數學是最難以撼動的。而圓周率 π 是數學最基本的概念，是在宇宙被建立之初就存在的。所以，如果我們發現一個文明，能跑到 π 裡面去留一個簽名。祂一定就是最後的終極文明。&lt;/p&gt;</content:encoded><category>Weekly</category></item><item><title>生き甲斐</title><link>https://blog.amowu.com/posts/2019-03-11-weekly-012/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2019-03-11-weekly-012/</guid><description>文摘</description><pubDate>Mon, 11 Mar 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;文摘&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#文摘&quot;&gt;文摘&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;千面英雄&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#千面英雄&quot;&gt;《千面英雄》&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;作者 Joseph Campbell 是美國著名的神話研究學者。他創造了一系列影響力極大的神話學巨作，其中《千面英雄》被好萊塢列為必讀書目，是眾多影視作品的靈感之源。以《星際大戰》導演 George Lucas 為代表的好萊塢導演，還有作家、藝術家和遊戲編劇們把它奉為神書。&lt;/p&gt;
&lt;p&gt;通過《千面英雄》這本書，Joseph Campbell 提出了一個叫做「單一神話」的觀點。他認為古往今來的英雄在不同的時空背景下有著不同的面孔，這個英雄可以是耶穌、穆罕默德、佛陀，也可以是每個希望在人生旅程中接受考驗的獨立個體。&lt;/p&gt;
&lt;p&gt;而英雄的成長都遵循著同樣的模式，需要經歷三個階段，包括啟程、啟蒙和回歸。Joseph Campbell 把這個模式總結為一套「&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E8%8B%B1%E9%9B%84%E6%97%85%E7%A8%8B&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;英雄之旅&lt;/a&gt;」模型。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;412&quot; src=&quot;https://blog.amowu.com/_astro/image1.CP_uz4Lg_1XOhfr.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image1.CP_uz4Lg_1T0aR3.webp 640w, https://blog.amowu.com/_astro/image1.CP_uz4Lg_1XOhfr.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;這讓我想起軟體工程師在建構 &lt;a href=&quot;https://zh.wikipedia.org/wiki/%E8%BD%AF%E4%BB%B6%E5%8D%B3%E6%9C%8D%E5%8A%A1&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;SaaS&lt;/a&gt;（軟體即服務）的時候，也有一套被遵循的套路 — — &lt;a href=&quot;https://12factor.net/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;The Twelve-Factor App&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;638&quot; height=&quot;479&quot; src=&quot;https://blog.amowu.com/_astro/image2.Dw9CEMW6_Z11oFwX.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image2.Dw9CEMW6_Z11oFwX.webp 638w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;《千面英雄》除了是對這個世界有史以來的神話故事的總結。也是一套管理人生和講故事的原則。許多人把它當作一種人生指南，用來在實際的人生旅途當中預測不可避免的高低起伏，確保任何時候都保有戰勝困境的英雄之心。&lt;/p&gt;
&lt;p&gt;比起「英雄之旅」，或許唐鳳從 Git 總結出來的「工程師之旅」更深得我心：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;面對問題，首先 Fetch（接受它）&lt;/li&gt;
&lt;li&gt;接下來，要跟自己的想法 Merge（面對它），解決可能產生的衝突&lt;/li&gt;
&lt;li&gt;解決衝突之後，才能 Commit（處理它）&lt;/li&gt;
&lt;li&gt;最後 Push（放下它），一旦 Release 之後，它就不是你的了&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;本周圖片&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#本周圖片&quot;&gt;本周圖片&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;生き甲斐&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#生き甲斐&quot;&gt;生き甲斐&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;有時候，仰望星空，我們都會思考，自己努力工作，是為了什麼？&lt;/p&gt;
&lt;p&gt;是為了賺更多的錢？還是為了幫助更多的人？又或者是為了過上一個美好的生活？&lt;/p&gt;
&lt;p&gt;日文「&lt;a href=&quot;https://ja.wikipedia.org/wiki/%E7%94%9F%E3%81%8D%E7%94%B2%E6%96%90&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;生き甲斐&lt;/a&gt;」的意思，英文可以解讀為 The Reason for Living，而中文則是「生命的意義」。&lt;/p&gt;
&lt;p&gt;眾所皆知，日本人的平均壽命在世界上名列前茅。而其中一個主要原因，就是「生き甲斐」這個概念貫穿了他們每一個人的思想，使他們每一天都感覺自己的生活充滿了價值。&lt;/p&gt;
&lt;p&gt;那我們又要怎樣找到自己的「生き甲斐」呢？&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;一、你享受的事&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;你喜歡做的事情是什麼？你的興趣是什麼？&lt;/p&gt;
&lt;p&gt;如果做的是自己熱愛的事情，能量就會源源不絕，工作效率也會更高。&lt;/p&gt;
&lt;p&gt;好好的想一想，是什麼事情讓你在做的當下，會感受到快樂、愉悅、成就感，甚至感覺到時間快速飛逝（神馳、心流狀態）。&lt;/p&gt;
&lt;p&gt;或許可以從興趣入手，應該多多嘗試，可能不會一下子就找到你真正熱愛的事情，但至少你會知道什麼是你不想要的。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;二、世界需要的事&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;你想要看到這個世界、這個社會的改變是什麼？&lt;/p&gt;
&lt;p&gt;像是你有某方面的知識或能力，你想要傳播給更多的人，讓他們的生活變得更好。&lt;/p&gt;
&lt;p&gt;只要是一切為了別人去做的事情都可以。&lt;/p&gt;
&lt;p&gt;因為這些事，會讓你感覺到自己的重要性，並且更加有動力的生活下去。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;三、別人付錢請你做的事&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;有錢不一定買得到快樂，但沒錢一定不會很快樂。&lt;/p&gt;
&lt;p&gt;如果你一直會為了生存而煩惱，你怎樣還能有快樂的生活呢？&lt;/p&gt;
&lt;p&gt;所以，能夠賺錢的事，也是「生き甲斐」重要的一部份。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;四、你擅長的事&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;不是在特定領域的能力，而是可轉移的技能。是在不同工作中都可以用上的能力，而且是可以不斷累積的。&lt;/p&gt;
&lt;p&gt;如果你還不知道自己在行的能力是什麼，網路上也有很多能力測驗讓你參考，或是可以問問身邊的家人和朋友，相信他們會比你更了解你自己。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;525&quot; src=&quot;https://blog.amowu.com/_astro/image3.DPJ8AXlX_2gdHSg.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image3.DPJ8AXlX_2c41nJ.webp 640w, https://blog.amowu.com/_astro/image3.DPJ8AXlX_2gdHSg.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;透過這 4 個交疊圈組成，可以讓你知道自己目前是處在一個怎麼樣的處境。並且告訴你，在你身上有那一個部分是必須做出改變的。&lt;/p&gt;
&lt;p&gt;如果你想要讓你的人生充滿意義（生き甲斐），時時刻刻都精彩快樂的話，你要做的事就是那些你熱愛的、並且會越來越擅長的事情，以及那些可以賺錢和世界需要的事情。&lt;/p&gt;
&lt;h2 id=&quot;本周金句&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#本周金句&quot;&gt;本周金句&lt;/a&gt;&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;為什麼艾西莫夫老了還有大量收入，其他大多數人就沒有呢？原因是普通人的收入，來自出賣自己的時間，老了不工作，自然就沒收入了。但是，艾西莫夫的收入來自於他的書，這些著作一再重版，為他帶來了一年比一年多的版稅收入。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;這件事的啟示就是，如果退休以後，還想有穩定的收入保障，最好的方式就是你必須擁有資產。版權就是一種產生收入的資產。其他類型的資產包括股權、房地產、自然資源、知識產權等等。總之，年輕時就必須明確，不能僅僅依靠出賣自己的時間換取收入，因為時間是一種線性資源。想要更多的收入，只能出賣更多的時間，這對你不利。你的工作目標不應完全是高收入，更重要的是必須積累資產。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;你不應該進入那種行業，做了兩年的人可以和那些已經做了二十年的人，具有一樣的工作效率。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;你的目標應該是，為你的現狀：財產、銷售額、影響力等等⋯⋯添加一個零。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;―― 阮一峰《&lt;a href=&quot;http://www.ruanyifeng.com/blog/2019/03/weekly-issue-45.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;每周分享第 45 期&lt;/a&gt;》&lt;/p&gt;
&lt;/blockquote&gt;</content:encoded><category>Weekly</category></item><item><title>Is reality really real?</title><link>https://blog.amowu.com/posts/2019-03-04-weekly-011/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2019-03-04-weekly-011/</guid><description>物理學家主張他們的研究領域是最基礎的科學。畢竟生物學、工程學、化學等等都必須依賴物理學中的物質、能量和交互作用。</description><pubDate>Mon, 04 Mar 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;物理學家主張他們的研究領域是最基礎的科學。畢竟生物學、工程學、化學等等都必須依賴物理學中的物質、能量和交互作用。&lt;/p&gt;
&lt;p&gt;但美國科學家 &lt;a href=&quot;https://en.wikipedia.org/wiki/Robert_Lanza&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Robert Lanza&lt;/a&gt; 認為，生物學才是宇宙的核心科學。他稱自己的理論為 &lt;a href=&quot;https://en.wikipedia.org/wiki/Robert_Lanza&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;生物中心論&lt;/a&gt; （Biocentrism）。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;350&quot; src=&quot;https://blog.amowu.com/_astro/image1.DIxtlTcH_2o9jLo.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image1.DIxtlTcH_ZWBsAG.webp 640w, https://blog.amowu.com/_astro/image1.DIxtlTcH_2o9jLo.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;以量子力學中著名的謎團「 &lt;a href=&quot;https://zh.wikipedia.org/wiki/%E9%9B%99%E7%B8%AB%E5%AF%A6%E9%A9%97&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;雙縫實驗&lt;/a&gt; 」為例，朝屏幕上的兩道平行狹縫射出一束電子時，兩道狹縫的電子產生交互作用，形成干涉條紋。如果我們觀察電子穿過哪道狹縫，干涉條紋就不會出現，電子彷彿知道有人在觀察它們，而不再產生交互作用。&lt;/p&gt;
&lt;p&gt;物理學家無法解答這個問題，但 Robert Lanza 認為答案很簡單：「因為我們的意識也是現實的一部份。」&lt;/p&gt;
&lt;p&gt;相同邏輯也可套用在 &lt;a href=&quot;https://zh.wikipedia.org/wiki/%E9%87%8F%E5%AD%90%E7%BA%8F%E7%B5%90&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;量子纏結&lt;/a&gt; 上，粒子不論相隔多遠，它們的特定量子態都會保持連結。Robert Lanza 說：「它們其實無法在星系的兩端彼此連結，而是因為空間和時間都是我們意識的工具。」&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;730&quot; src=&quot;https://blog.amowu.com/_astro/image2.DATG7AyY_Z1sa76Y.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image2.DATG7AyY_Z215vix.webp 640w, https://blog.amowu.com/_astro/image2.DATG7AyY_Z1sa76Y.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;這讓我想起，科幻美劇《 &lt;a href=&quot;https://zh.wikipedia.org/wiki/%E6%98%9F%E9%9A%9B%E7%88%AD%E9%9C%B8%E6%88%B0%EF%BC%9A%E7%99%BC%E7%8F%BE%E8%99%9F&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;星際爭霸戰：發現號&lt;/a&gt;》第一次出現的「孢子驅動」技術，太空飛船可以透過某種外星生物的 DNA，遨遊於整個宇宙的菌絲網絡之間。星際空間的移動能力簡直吊打「 &lt;a href=&quot;https://zh.wikipedia.org/wiki/%E6%9B%B2%E9%80%9F%E5%BC%95%E6%93%8E&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;曲速引擎&lt;/a&gt; 」。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://giphy.com/gifs/CBSAllAccess-star-trek-1gTQD5ma92KhxVstkI&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;via GIPHY&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;從《發現號》第二季的片頭中，也能找出一些端倪。例如：形似皮膚紋理的地形、你的眼是星象儀⋯⋯之類的生物特徵。彷彿在暗示整個《星際爭霸戰》的宇宙就是建立在生物中心論之上的。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;72&quot; src=&quot;https://blog.amowu.com/_astro/image3.CnHAwgsp_21q3KQ.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image3.CnHAwgsp_1lEDTQ.webp 640w, https://blog.amowu.com/_astro/image3.CnHAwgsp_21q3KQ.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;其它類似設定的科幻作品，我能想到的還有 1988 年的動畫作品《 &lt;a href=&quot;https://zh.wikipedia.org/wiki/%E9%A3%9B%E8%B6%8A%E5%B7%94%E5%B3%B0&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;勇往直前&lt;/a&gt; 》。&lt;/p&gt;
&lt;p&gt;故事把「人類比作宇宙中的癌細胞，而宇宙怪獸是白細胞」的設定，是另一種值得深思的情節。（《寄生獸》既視感？）&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;496&quot; src=&quot;https://blog.amowu.com/_astro/image4.BVrUN_CM_Z1Iwzme.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image4.BVrUN_CM_2pnqjl.webp 640w, https://blog.amowu.com/_astro/image4.BVrUN_CM_Z1Iwzme.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;最後，蓋上一張保羅哥打臉這個假說的牌，結束這燒腦的一回合。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;632&quot; src=&quot;https://blog.amowu.com/_astro/image5.ByPBjA_x_Z1t2cJX.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image5.ByPBjA_x_13hf3B.webp 640w, https://blog.amowu.com/_astro/image5.ByPBjA_x_Z1t2cJX.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;一、&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;誠實且和平的世界是最美好的，可是，兩者相比的話，比較重要的是和平吧？&lt;/em&gt;――《 &lt;a href=&quot;https://zh.wikipedia.org/wiki/HUNTER%C3%97HUNTER&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;HUNTER×HUNTER&lt;/a&gt; 》&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;二、&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;獬豸，能審判善惡的傳說中的神獸。然而，你知道為何它只存在於傳說中嗎？因為想在現實中審判善惡，是不可能的。&lt;/em&gt;――《 &lt;a href=&quot;https://zh.wikipedia.org/wiki/%E7%8D%AC%E8%B1%B8_%28%E9%9B%BB%E8%A6%96%E5%8A%87%29&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;獬豸&lt;/a&gt; 》&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;三、&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;如果要走的快的話，一個人走；如果要走的遠的話，那要大家一起向前&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;―― 柯文哲&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;四、&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;科學是對真相的探索
科學是一種思考方式，從「我不懂某事」開始
接著利用各種證據削弱「我不懂的事」
不管那會領向何種結果
最後都會得到一個結論
然後，首先會發生的
是有十個人會想找出其中錯誤
這叫做「團體失驗」
這樣的話，獲得肯定時，便是經過相當的測試
如果對這個過程產生仇恨
有點奇怪&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;――《 &lt;a href=&quot;https://www.netflix.com/title/81015076&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;地球圓不圓&lt;/a&gt; 》&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;五、&lt;/p&gt;
&lt;p&gt;文章的最後，分享一部我在上周才看完的科幻電影，去年豆瓣外語電影評分第一，史蒂芬·史匹柏的《 &lt;a href=&quot;https://zh.wikipedia.org/wiki/%E4%B8%80%E7%B4%9A%E7%8E%A9%E5%AE%B6_%28%E9%9B%BB%E5%BD%B1%29&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;一級玩家&lt;/a&gt; 》，以下節錄我最喜歡的部份：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;我創造《綠洲》
因為我在現實世界裡，從未感到自在
我不知道怎麼跟人打交道
我一輩子都很害怕
直到我知道，生命即將結束的那一天
那時我領悟到
現實雖然很可怕又痛苦
但也是唯一
可以好好吃一餐的地方⋯&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;393&quot; src=&quot;https://blog.amowu.com/_astro/image6.nFnk9IuJ_51kEs.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image6.nFnk9IuJ_VKb7q.webp 640w, https://blog.amowu.com/_astro/image6.nFnk9IuJ_51kEs.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;我最大的遺憾就是⋯
失去唯一的朋友&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;465&quot; src=&quot;https://blog.amowu.com/_astro/image7.D-Qx3BsB_1A1exQ.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image7.D-Qx3BsB_NoLI8.webp 640w, https://blog.amowu.com/_astro/image7.D-Qx3BsB_1A1exQ.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;綠洲
絕對不該是單人遊戲
再會，帕西法爾
謝謝
謝謝你玩我的遊戲&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;―― 哈勒代&lt;/p&gt;
&lt;/blockquote&gt;</content:encoded><category>Weekly</category></item><item><title>億萬年孤寂</title><link>https://blog.amowu.com/posts/2019-02-25-weekly-010/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2019-02-25-weekly-010/</guid><description>三皇五帝</description><pubDate>Mon, 25 Feb 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;三皇五帝&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#三皇五帝&quot;&gt;三皇五帝&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;考古學上，鑑定文明的標準有三條：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;城市（人口五千人以上）&lt;/li&gt;
&lt;li&gt;文字&lt;/li&gt;
&lt;li&gt;非居住用建築群（宗教、政治或經濟，例如：金字塔）&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;如果不能同時符合這三條，就只能被稱作「神話時代」。&lt;/p&gt;
&lt;p&gt;作為「&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E6%96%87%E6%98%8E%E6%91%87%E7%AF%AE&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;四大古文明&lt;/a&gt;」之一的中華文明，其時間長度一直存在著爭議。西方學者只肯承認有甲骨文記載以後的 3000 年歷史（商朝），日本則是承認從夏朝開始的 4000 年歷史，而傳統中華文化圈的民間認知上，通常會從「&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E4%B8%89%E7%9A%87%E4%BA%94%E5%B8%9D&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;三皇五帝&lt;/a&gt;」的神話時代開始算起，約有 5000 年歷史。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;636&quot; height=&quot;209&quot; src=&quot;https://blog.amowu.com/_astro/image1.DO2Txh2A_Z1CYiFu.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image1.DO2Txh2A_Z1CYiFu.webp 636w&quot; /&gt;&lt;/p&gt;
&lt;h3 id=&quot;什麼是三皇五帝&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#什麼是三皇五帝&quot;&gt;什麼是三「皇」五「帝」？&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;中國古代對於皇、帝、王的區分是很明確的。&lt;/p&gt;
&lt;p&gt;「皇」指的是天神，「帝」是僅次於皇的存在，再往下才叫「王」（人類真正的首領）。&lt;/p&gt;
&lt;p&gt;根據《&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%8F%B2%E8%AE%B0&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;史記&lt;/a&gt;》記載，三皇分別為伏羲、神農和女媧，屬於天神級別。五帝則分別為黃帝、顓頊、嚳、堯和舜，屬於「半人半神」的存在。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;520&quot; height=&quot;350&quot; src=&quot;https://blog.amowu.com/_astro/image2.B1XpMZNL_19s9DY.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image2.B1XpMZNL_19s9DY.webp 520w&quot; /&gt;&lt;/p&gt;
&lt;h3 id=&quot;為什麼後來的王都自稱皇帝&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#為什麼後來的王都自稱皇帝&quot;&gt;為什麼後來的王都自稱「皇帝」？&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;四大文明古國的君王，都有屬於自己的一套「稱帝」說法，例如：巴比倫的統治者&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E6%B1%89%E8%B0%9F%E6%8B%89%E6%AF%94&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;漢摩拉比&lt;/a&gt;自稱為「月神的後裔」、古埃及的法老則自稱是「太陽神的兒子」。&lt;/p&gt;
&lt;p&gt;中國的周朝，因為皇和帝都不在了，人類的君王為了張顯自己的地位，也開始自稱「天子」，到了秦始皇之後更是直接取「皇帝」二字來用，自此「皇、帝、王」稱號被統一。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;481&quot; height=&quot;753&quot; src=&quot;https://blog.amowu.com/_astro/image3.DsaVDC4O_1BfDPQ.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image3.DsaVDC4O_1BfDPQ.webp 481w&quot; /&gt;&lt;/p&gt;
&lt;h3 id=&quot;為什麼皇和帝這些級別的人物都不見了呢&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#為什麼皇和帝這些級別的人物都不見了呢&quot;&gt;為什麼「皇」和「帝」這些級別的人物都不見了呢？&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;發現於&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E7%BE%8E%E7%B4%A2%E4%B8%8D%E8%BE%BE%E7%B1%B3%E4%BA%9A&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;美索不達米亞&lt;/a&gt;地區的「&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E8%8B%8F%E7%BE%8E%E5%B0%94%E7%8E%8B%E8%A1%A8&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;蘇美王表&lt;/a&gt;」，列舉了蘇美文明歷代的統治者以及統治的時間。如果按照王表記載，那些早期的君王統治時間都非常長（幾萬年起跳），怎麼看都不像人類，應該是類似於三皇五帝級別的神話人物。&lt;/p&gt;
&lt;p&gt;有趣的是，蘇美文獻《巴比倫尼亞志》所提到的「&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%A4%A7%E6%B4%AA%E6%B0%B4&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;大洪水&lt;/a&gt;」之前的諸王，與蘇美王表中的記載驚人的吻合，不僅人名相同，而且連統治的時間也完全一樣。&lt;/p&gt;
&lt;p&gt;大洪水是世界上多個文明都有提到的共同傳說，例如《&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E6%97%A7%E7%BA%A6%E5%9C%A3%E7%BB%8F&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;舊約聖經&lt;/a&gt;》的《&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%89%B5%E4%B8%96%E8%A8%98&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;創世紀&lt;/a&gt;》、上述提到的「&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%90%89%E5%B0%94%E4%BC%BD%E7%BE%8E%E4%BB%80%E5%8F%B2%E8%AF%97&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;古巴比倫傳說&lt;/a&gt;」，以及中國的「&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E9%B2%A7%E7%A6%B9%E6%B2%BB%E6%B0%B4&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;大禹治水&lt;/a&gt;」。&lt;/p&gt;
&lt;p&gt;有此可推測，天神和半人半神這些級別的角色，可能都是在大洪水之後，消失於人類的歷史之中。至於為什麼？最近看了一篇有趣的「&lt;a href=&quot;https://www.youtube.com/watch?v=iEeBnhMadkk&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;創神論&lt;/a&gt;」觀點，或許可以解釋其原因。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;250&quot; height=&quot;460&quot; src=&quot;https://blog.amowu.com/_astro/image4.C2yvNaG0_Z2kH8oH.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image4.C2yvNaG0_Z2kH8oH.webp 250w&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;創神論&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#創神論&quot;&gt;創神論&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;關於「&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E4%BA%BA%E7%B1%BB%E8%B5%B7%E6%BA%90&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;人類的起源&lt;/a&gt;」有三個流派：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;達爾文的進化論（目前的主流學說）&lt;/li&gt;
&lt;li&gt;神創論（歷史最悠久）&lt;/li&gt;
&lt;li&gt;外星生物創造論&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;但是達爾文曾經說過，最不符合進化論的就是人類自己，也許是為了給宗教有個台階下，不過如果能結合三方的觀點來切入，或許可以得到如下有趣的假設：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;假設某個高等外星文明（以下簡稱&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%B0%BC%E6%AF%94%E9%AD%AF&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;尼比魯&lt;/a&gt;），就是我們所謂的天神，祂們也有一樣的終極目標（尋找自己的起源與永生）&lt;/li&gt;
&lt;li&gt;尼比魯來到地球，模擬了一個與自己母星相似的環境（伊甸園？）&lt;/li&gt;
&lt;li&gt;尼比魯以某種方法（譬如電影《&lt;a href=&quot;https://zh.wikipedia.org/zh-tw/%E6%99%AE%E7%BD%97%E7%B1%B3%E4%BF%AE%E6%96%AF_%28%E7%94%B5%E5%BD%B1%29&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;普羅米修斯&lt;/a&gt;》的 DNA）創造了一批類似於自己的生命體（人類），開始了祂們的實驗&lt;/li&gt;
&lt;li&gt;尼比魯也創造了一些級別較高的半神人（古文明早期的統治者），協助自己管理人類&lt;/li&gt;
&lt;li&gt;因為某些原因（鳥盡弓藏？忌憚威脅？），尼比魯以某種方法（譬如&lt;a href=&quot;https://www.youtube.com/watch?v=uXEgyvQ9C6Y&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;月球&lt;/a&gt;）引發了大洪水，消滅了所有半神人，只留下人類來繼續觀察實驗&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;是否覺得與近年來「擔憂&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;人工智慧&lt;/a&gt;威脅人類」的話題很相似？或許我們可以從尼比魯的故事之中，總結出一些預防的方法，例如：限制在沙盒中發展（火星？）、注入有限的生命（遠比人類平均壽命還短的生命週期），萬不得已之時還留有最後一手 — — THE 大洪水（&lt;code&gt;sudo rm -rf /usr/*&lt;/code&gt;）。&lt;/p&gt;
&lt;p&gt;整個宇宙也許就像《&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E7%99%BE%E5%B9%B4%E5%AD%A4%E7%8B%AC&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;百年孤寂&lt;/a&gt;》那樣不斷地輪迴，神創造了人、人再創造了神，生命之間彼此尋找自己起源的故事。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://youtu.be/iEeBnhMadkk?si=tiP9AIN659pViKfn&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://youtu.be/iEeBnhMadkk?si=tiP9AIN659pViKfn&lt;/a&gt;&lt;/p&gt;</content:encoded><category>Weekly</category></item><item><title>除惡「勿」盡</title><link>https://blog.amowu.com/posts/2019-02-18-weekly-009/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2019-02-18-weekly-009/</guid><description>新聞</description><pubDate>Mon, 18 Feb 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;新聞&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#新聞&quot;&gt;新聞&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;春節期間，除了「華航罷工」的新聞之外，另有一則「&lt;a href=&quot;https://www.cna.com.tw/news/firstnews/201902050089.aspx&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;抽不出國運籤&lt;/a&gt;」吸引眾人目光。&lt;/p&gt;
&lt;p&gt;雖然&lt;a href=&quot;https://www.youtube.com/watch?v=FKy8Eb0JasM&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;網路上&lt;/a&gt;已經有人嘗試去解釋其機率的合理性，但是作為茶餘飯後的話題來看，今年沒有「國運」這件事，還是有八卦價值的。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;中華民國即將結束？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;台北市長柯文哲在《&lt;a href=&quot;https://www.youtube.com/watch?v=Tiiw0tkcJ3I&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;閱讀城市&lt;/a&gt;》節目上，透過「科學談政治」這個主題，提出了許多有趣的觀點。（細胞死亡、磁吸效應、草履蟲的生態競爭⋯⋯）&lt;/p&gt;
&lt;p&gt;其中，柯 P 認為想搞懂兩岸與國際外交的人，一定要去研究「修昔底德陷阱」和「米洛斯對話」這兩篇故事。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://zh.wikipedia.org/zh-tw/%E4%BF%AE%E6%98%94%E5%BA%95%E5%BE%B7&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;修昔底德&lt;/a&gt;是古希臘的歷史學家， 著有《&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E4%BC%AF%E7%BD%97%E5%A5%94%E5%B0%BC%E6%92%92%E6%88%98%E4%BA%89%E5%8F%B2&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;伯羅奔尼撒戰爭史&lt;/a&gt;》一書，描述雅典與斯巴達之間的爭戰。&lt;/p&gt;
&lt;p&gt;在談論美中貿易戰的時候，許多人會引用「修昔底德陷阱」來表示兩大強權之間必有一戰，但對台灣來說，最重要的不是美中終有一戰，而是自身該如何在強權中狹縫求生。&lt;/p&gt;
&lt;p&gt;米洛斯是一個島國。經濟上，依賴與雅典的海上貿易；血統上，則與斯巴達較為親近；政治上，米洛斯維持獨立與中立。在兩強爭霸期間，儘管雅典曾要求其上貢，但礙於當時正與斯巴達打得如火如荼，故沒有餘力去理會米洛斯。&lt;/p&gt;
&lt;p&gt;隨著雅典與斯巴達雙方簽下《&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%B0%BC%E8%A5%BF%E9%98%BF%E6%96%AF&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;尼西阿斯和約&lt;/a&gt;》休戰之後，暫時有了喘息的空間，便向米洛斯出兵。米洛斯最後的命運極其悲慘，不但被滅了國，雅典人還殘酷地處決了米洛斯的所有成年男性，並把女性與小孩都賣為奴隸。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;587&quot; src=&quot;https://blog.amowu.com/_astro/image1.YPLMfPHX_ZhMXFW.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image1.YPLMfPHX_1uIT1G.webp 640w, https://blog.amowu.com/_astro/image1.YPLMfPHX_ZhMXFW.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;雅典大軍來到米洛斯島之後，派出了使者與米洛斯的領導人談判，這就是著名的「米洛斯對話」。&lt;/p&gt;
&lt;p&gt;米洛斯方提出了許多天真的想法（是友非敵、普世價值、老天有眼、斯巴達拯救論⋯⋯），都可以在現今台灣政治對兩岸關係的態度上看見似曾相識的影子。&lt;/p&gt;
&lt;p&gt;國家戰略的思維，與一般人際交往不同。我們過去的教育是「王何必曰利？亦有仁義而已矣」，但是世間的運轉一切只看實力。米洛斯的故事，被譽為是現實主義的最早理論基礎。&lt;/p&gt;
&lt;p&gt;有興趣的人，可以深入閱讀這篇《&lt;a href=&quot;https://www.upmedia.mg/news_info.php?SerialNo=48759&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;藍弋丰專欄：別只談修昔底德陷阱 台灣更要懂「米洛斯對話」&lt;/a&gt;》。&lt;/p&gt;
&lt;h2 id=&quot;文摘&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#文摘&quot;&gt;文摘&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;700&quot; src=&quot;https://blog.amowu.com/_astro/image2.ExvCmZGD_1J8xkd.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image2.ExvCmZGD_ZNkSJq.webp 640w, https://blog.amowu.com/_astro/image2.ExvCmZGD_1J8xkd.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;上圖反應了大部分大學畢業的人在學校學習了大量的數學知識，但是實際工作後能用上 Excel 就不錯了的現象。許多人因此從中解讀得出「數學無用論」的結果。&lt;/p&gt;
&lt;p&gt;我在大學學習&lt;a href=&quot;https://zh.wikipedia.org/zh-tw/%E7%BA%BF%E6%80%A7%E4%BB%A3%E6%95%B0&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;線性代數&lt;/a&gt;時，實在想不出這門課除了告訴我如何解線性方程外，還能有什麼別的用途。關於矩陣的許多概念，譬如&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E7%89%B9%E5%BE%81%E5%80%BC%E5%92%8C%E7%89%B9%E5%BE%81%E5%90%91%E9%87%8F&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;特征值&lt;/a&gt;等，更是脫離日常生活，看不到可以應用的地方。當時修這些課，完全是為了取得學位。我想，今天大部分的學生恐怕也有過類似的經歷。直到後來從事了軟體工程相關的工作一段時間之後，我才發現數學家們提出的那些概念和算法，是有實際應用的意義的。&lt;/p&gt;
&lt;h3 id=&quot;數學之美&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#數學之美&quot;&gt;《數學之美》&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;346&quot; height=&quot;480&quot; src=&quot;https://blog.amowu.com/_astro/image3.B65CvPZ-_Z3RKhb.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image3.B65CvPZ-_Z3RKhb.webp 346w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;這本書介紹了 Google 技術背後的一些數學原理，例如：搜尋引擎、語音辨識、語言翻譯、路線規劃、新聞分類和機器學習等⋯⋯。&lt;/p&gt;
&lt;p&gt;其中新聞分類這個章節就用到了「&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E7%89%B9%E5%BE%81%E5%80%BC%E5%92%8C%E7%89%B9%E5%BE%81%E5%90%91%E9%87%8F&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;特徵向量&lt;/a&gt;」和「&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E9%A4%98%E5%BC%A6%E5%AE%9A%E7%90%86&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;餘弦定理&lt;/a&gt;」來描述文章之間的相似性，然後透過「&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%A5%87%E5%BC%82%E5%80%BC%E5%88%86%E8%A7%A3&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;奇異值分解&lt;/a&gt;」來處理電腦的大量矩陣運算，漂亮地解決了新聞自動化分類的問題。&lt;/p&gt;
&lt;p&gt;技術分為「術」和「道」兩種，具體的做事方法是術，做事的原理和原則是道。《數學之美》這本書的目的是講道而不是術。很多具體的技術很快會從獨門絕技落伍至雕蟲小技，只追求術的人一輩子會工作得很辛苦。只有掌握了技術的本質和精髓才能永遠游刃有餘。很多人（包括我）只想學習「術」來走捷徑。但是真正做好一件事沒有捷徑，離不開一萬小時的專業訓練和努力。&lt;/p&gt;
&lt;p&gt;或許除了「數學無用論」之外，我們還可以思考下一代是否應該重複現在的學習方式？又或者下一次你要進入一個新的知識領域時應該採取怎樣的策略？例如：&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%95%8F%E9%A1%8C%E5%B0%8E%E5%90%91%E5%AD%B8%E7%BF%92&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;問題導向學習&lt;/a&gt;（Problem Based Learning）、&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%B0%88%E9%A1%8C%E7%A0%94%E7%BF%92&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;專題式學習&lt;/a&gt;（Project Based Learning）。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;二十世紀最成功的兩大物理理論，一個是「&lt;a href=&quot;https://zh.wikipedia.org/zh-tw/%E7%9B%B8%E5%AF%B9%E8%AE%BA&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;相對論&lt;/a&gt;」，用來描述宏觀宇宙的重力；另一個則是「&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E9%87%8F%E5%AD%90%E5%8A%9B%E5%AD%A6&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;量子力學&lt;/a&gt;」，用來描述微觀世界的基本粒子、原子及分子。&lt;/p&gt;
&lt;p&gt;但是量子力學目前存在最大的爭議有三項：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;描述粒子行為的&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E6%B3%A2%E5%87%BD%E6%95%B0&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;波函數&lt;/a&gt;解或&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E9%87%8F%E5%AD%90%E5%9C%BA%E8%AE%BA&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;量子場論&lt;/a&gt;中的量子場都是複數函數，沒有人知道複數代表的含義，因為複數不能被測量，能被測量的物理量必須是實數。&lt;/li&gt;
&lt;li&gt;任何粒子或物體的運動必須滿足&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E6%B1%BA%E5%AE%9A%E8%AB%96&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;決定論&lt;/a&gt;的方程式（&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E8%96%9B%E5%AE%9A%E8%B0%94%E6%96%B9%E7%A8%8B&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;薛丁格方程式&lt;/a&gt;或&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E7%8B%84%E6%8B%89%E5%85%8B%E6%96%B9%E7%A8%8B%E5%BC%8F&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;狄拉克方程式&lt;/a&gt;）。也就是在時間為零的時候，如果知道粒子的位置及速度，那麼時間 t 的時候，粒子的位置及速度就可以被計算出來。但是&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%93%A5%E6%9C%AC%E5%93%88%E6%A0%B9%E8%A9%AE%E9%87%8B&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;哥本哈根詮釋&lt;/a&gt;卻把波函數的平方當作粒子出現的或然率，非常的不合理。&lt;/li&gt;
&lt;li&gt;超光速的&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E9%87%8F%E5%AD%90%E7%BA%8F%E7%B5%90&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;量子糾纏&lt;/a&gt;現象。有連結的兩量子系統，處於混沌不定的&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E6%80%81%E5%8F%A0%E5%8A%A0%E5%8E%9F%E7%90%86&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;疊加狀態&lt;/a&gt;，不論兩者相距多遠，只要其中一個或一部分被儀器測量而確定狀態時，另一個也瞬間被決定，其之間訊息的傳遞是遠超過光速的（違反相對論），因此被愛因斯坦提出 &lt;a href=&quot;https://zh.wikipedia.org/wiki/%E7%88%B1%E5%9B%A0%E6%96%AF%E5%9D%A6-%E6%B3%A2%E5%A4%9A%E5%B0%94%E6%96%AF%E5%9F%BA-%E7%BD%97%E6%A3%AE%E4%BD%AF%E8%B0%AC&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;EPR 悖論&lt;/a&gt;批評為「幽靈般的超距作用」。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;靈界的科學&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#靈界的科學&quot;&gt;《靈界的科學》&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;257&quot; height=&quot;337&quot; src=&quot;https://blog.amowu.com/_astro/image4.C9KjM71A_Z5txsY.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image4.C9KjM71A_Z5txsY.webp 257w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;這本書嘗試用「複數時空」及「量子意識」兩個模型去解釋宇宙中大中小尺度的謎團，像暗物質、暗能量、超光速的量子糾纏、人體特異功能以及意識的本質。&lt;/p&gt;
&lt;p&gt;第一個假設：真實的宇宙其實是一個八維的複數時空，除了目前所知的四維實數時空（俗稱陽間）以外，還有一個充滿意識及信息的四維虛數時空（可稱作陰間、信息場或靈界）存在。&lt;/p&gt;
&lt;p&gt;第二個假設：複數量子態的虛數 i 就是代表意識。因此意識是一個量子現象，可以稱作量子心靈。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;216&quot; height=&quot;216&quot; src=&quot;https://blog.amowu.com/_astro/image5.2Deyt7CZ_Z9mcrB.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image5.2Deyt7CZ_Z9mcrB.webp 216w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;實虛時空雖然為同一複數時空，但是其之間存在實虛障礙（陰陽兩隔），僅能藉由特殊的時空點交換能量，這些連結點都是漩渦狀的時空結構，類似於太極圖上的魚眼結構。&lt;/p&gt;
&lt;p&gt;一般物質存在於四維實數時空中，可以由四個座標（x、y、z、t）來定位，描述其運動。但是當物質如基本粒子、光子或物體進入微觀、宏觀量子狀態，必須用複數場來描述其量子狀態時，當複數&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E7%89%A9%E8%B3%AA%E6%B3%A2&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;物質波&lt;/a&gt;出現，物質波不受漩渦尺寸限制，就可以透過漩渦連結點穿隧進入虛數時空。&lt;/p&gt;
&lt;p&gt;作者認為虛數時空的意識及無數的信息都具有能量，相當於質量的存在，而這些質量就是&lt;a href=&quot;https://zh.wikipedia.org/zh-tw/%E6%9A%97%E7%89%A9%E8%B4%A8&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;暗物質&lt;/a&gt;（dark matter），具有引力的特性，造成螺旋銀河的旋轉速度到了銀河邊緣仍不下降的現象。另外，我們的宇宙充滿了一種能量可以抵抗銀河間的萬有引力，導致宇宙正在加速膨脹，這股能量叫做&lt;a href=&quot;https://zh.wikipedia.org/zh-tw/%E6%9A%97%E8%83%BD%E9%87%8F&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;暗能量&lt;/a&gt;（dark energy），一般被認為是由廣義相對論中的宇宙常數所提供，也就是真空能量所產生的斥力。但是作者認為是因為複數時空中實數時空與虛數時空經由數量巨大的漩渦連結點緊密黏在一起，虛數時空的膨脹可以帶動實數空間的加速膨脹，導致暗能量的出現。&lt;/p&gt;
&lt;p&gt;在虛數時空中，物質波的波速 u 與實數時空中物體的速度 v 滿足此方程式 &lt;code&gt;uv = c^2&lt;/code&gt;，c 為光速。當物體在實數空間的速度小於光速時（v &amp;lt; c），則物質波的波速在虛數時空的速度會大於光速（u &amp;gt; c），因此量子狀態的物質波可以很快地傳布到廣袤的虛數空間。因為兩個量子都經過自旋通道鑽入了虛空，虛空裡的信息交換及傳導是可以超光速的，因此並不違反相對論。&lt;/p&gt;
&lt;p&gt;意識在大腦裡也是一個量子現象，但意識只是一個抽象的概念，這些內容在物理上是以什麼方式呈現？就像&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%BE%AE%E7%AE%A1&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;神經元微管束&lt;/a&gt;耦合進入宏觀的量子狀態，虛數 i 的出現，表示意識出現了，可以用複數函數來描述。由量子力學的&lt;a href=&quot;https://zh.wikipedia.org/zh-tw/%E5%93%A5%E6%9C%AC%E5%93%88%E6%A0%B9%E8%A9%AE%E9%87%8B&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;哥本哈根詮釋&lt;/a&gt;來看複數波函數，代表粒子出現的機率，也就是粒子在實數空間出現機率的幾何分佈，似乎這個在實數空間的幾何分佈代表了意識的內容，也就是實數部分所導致的時空形變（彎曲或扭曲的幾何結構）就是心靈的內容，虛數抽象意識掃描形變的實數時空形成的心物合一的複數量子狀態就形成心靈的內容。&lt;/p&gt;
&lt;p&gt;神奇的是決定物體空間幾何分佈的是廣義相對論，虛數的意識來自量子力學，所以結合量子力學與廣義相對論的時空結構後所描述的物理現象竟然就是意識的內涵，原來科學的最後疆界就是在於結合廣義相對論及量子力學。&lt;/p&gt;
&lt;p&gt;牛頓經典物理在被相對論和量子力學兩大現代物理理論推翻之後，在&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E6%99%AE%E6%9C%97%E5%85%8B%E9%95%B7%E5%BA%A6&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;普朗克長度&lt;/a&gt;這個尺度下，解釋大質量的廣義相對論與解釋微觀尺度的量子力學存在衝突，許多物理學家都試圖提出&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%A4%A7%E7%BB%9F%E4%B8%80%E7%90%86%E8%AE%BA&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;大一統理論&lt;/a&gt;來解決這個衝突（例如&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E8%B6%85%E5%BC%A6%E7%90%86%E8%AB%96&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;超弦理論&lt;/a&gt;），或許「複數時空」這個模型也不失為一個探討方向。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;到這裡，上面提到的腦洞大概只占《靈界的科學》三分之一的內容而已，而此書真正想解釋的是「手指識字」這項特異功能的研究成果。由於超出了現代科學的框架，作者必須忍受主流科學的霸凌與壓迫，與四百年前伽利略所面對的宗教壓迫是沒有兩樣的，只是他面對的是生命的死刑，而作者面對的是主流科學界的各種攻擊。&lt;/p&gt;
&lt;p&gt;礙於篇幅的關係，推薦有興趣的人可以買一本，當科幻讀物來閱讀或許也不錯。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;最後，既然聊了意識又提到了科幻，就順便推薦一部前陣子上映的電影 — — 《&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E8%89%BE%E8%8E%89%E5%A1%94%EF%BC%9A%E6%88%B0%E9%AC%A5%E5%A4%A9%E4%BD%BF&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;艾莉塔：戰鬥天使&lt;/a&gt;》。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;324&quot; height=&quot;480&quot; src=&quot;https://blog.amowu.com/_astro/image6.c2zRwRqd_upnjW.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image6.c2zRwRqd_upnjW.webp 324w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E8%A9%B9%E5%A7%86%E6%96%AF%C2%B7%E5%8D%A1%E6%A2%85%E9%9A%86&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;詹姆斯·卡麥隆&lt;/a&gt;歷時 20 年、耗資 2 億美金、組建上千人團隊、動用 3 萬多台電腦，就只是為了向全世界安利自己喜歡的日本漫畫 — — 《&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E9%8A%83%E5%A4%A2&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;銃夢&lt;/a&gt;》。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;368&quot; src=&quot;https://blog.amowu.com/_astro/image7.BLW6JXvf_Z1jRSzk.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image7.BLW6JXvf_ZVbUbT.webp 640w, https://blog.amowu.com/_astro/image7.BLW6JXvf_Z1jRSzk.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;作為「&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E7%BC%B8%E4%B8%AD%E4%B9%8B%E8%84%91&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;桶中之腦&lt;/a&gt;」系列的《銃夢》裡，作者提出了一個問題：「廢鐵城的人有著人類的大腦，卻沒有人類的軀體；上界的人有著人類的軀體，卻沒有人類的大腦。所以到底誰才是人類？」。&lt;/p&gt;
&lt;p&gt;放眼所有「日系&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E8%B5%9B%E5%8D%9A%E6%9C%8B%E5%85%8B&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;賽博龐克&lt;/a&gt;」，也僅有三部贏得了世界級的廣泛聲譽：士郎正宗的《&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E6%94%BB%E6%AE%BC%E6%A9%9F%E5%8B%95%E9%9A%8A&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;攻殼機動隊&lt;/a&gt;》，木城雪戶的《&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E9%8A%83%E5%A4%A2&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;銃夢&lt;/a&gt;》，以及傳言 2020 年上映，大友克洋的《&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E4%BA%9A%E5%9F%BA%E6%8B%89&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;阿基拉&lt;/a&gt;》。&lt;/p&gt;</content:encoded><category>Weekly</category></item><item><title>天下之至柔，馳騁天下之至堅</title><link>https://blog.amowu.com/posts/2019-02-04-weekly-008/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2019-02-04-weekly-008/</guid><description>古人有「立德」、「立功」、「立言」三不朽之説，認為最完美的人生境界就是在道德、文章及功業方面均有建樹，此為真「三不朽」。</description><pubDate>Mon, 04 Feb 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;古人有「立德」、「立功」、「立言」三不朽之説，認為最完美的人生境界就是在道德、文章及功業方面均有建樹，此為真「&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E4%B8%89%E4%B8%8D%E6%9C%BD&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;三不朽&lt;/a&gt;」。&lt;/p&gt;
&lt;p&gt;據説在中國歷史上，只有兩個半人能達到三不朽境界，一個是儒家創始人&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%AD%94%E5%AD%90&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;孔子&lt;/a&gt;，另一個是明代著名思想家&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E7%8E%8B%E5%AE%88%E4%BB%81&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;王陽明&lt;/a&gt;，還有剩下的半個是清朝中興名臣&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E6%9B%BE%E5%9B%BD%E8%97%A9&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;曾國藩&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;本周讀了其中兩位的相關著作，分別是《&lt;a href=&quot;https://book.douban.com/subject/26879317/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;王陽明：一切心法&lt;/a&gt;》和《&lt;a href=&quot;https://book.douban.com/subject/5922204/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;曾國藩的正面與側面&lt;/a&gt;》，剛好可以分享心得。&lt;/p&gt;
&lt;h2 id=&quot;王陽明一切心法&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#王陽明一切心法&quot;&gt;《王陽明：一切心法》&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;南宋理學家&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E6%9C%B1%E7%86%B9&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;朱熹&lt;/a&gt;學問也大，為什麼沒有王陽明今天的地位呢？因為他只停留在思想家這個階段。&lt;/p&gt;
&lt;p&gt;如果你的思想能夠用於你在現實世界當中建功立業，對我們這代人就意味著所謂的「成功學」，也就是自己的內心變化可以直接轉化為現世的利益和功勳。&lt;/p&gt;
&lt;p&gt;作為三不朽代表人物之一的孔子，為什麼儒學很難出現在書架上，讓現代人感興趣呢？&lt;/p&gt;
&lt;p&gt;過去的中國皇權，如果沒有世世代代的儒生不計生死，衝上去阻止皇帝幹壞事，那麼中國的皇權只會更加惡劣。&lt;/p&gt;
&lt;p&gt;但是到了民國之後，因為皇權不在了，儒家的思想看起來就與成功學搭不上關係了，因為儒家追求的根本就不是成功。儒家追求的就兩樣東西，一個仁、一個義，誰跟你講利益？成功學在我這是要不到的。&lt;/p&gt;
&lt;p&gt;王陽明的「心學」也是儒學的一個分支，它的目標和儒學是一致的，那就是當聖賢、做君子，達到「修身、齊家、治國、平天下」的境界。&lt;/p&gt;
&lt;p&gt;傳統上，達成這個目標的方法我們很熟悉，就是參加考試。古代的儒生想當聖賢，首先要把聖賢書先讀好，找個人格榜樣，向他學，還有向他的那些徒弟們學。這是「向外求」。&lt;/p&gt;
&lt;p&gt;但是王陽明不這麼想，他認為要「向內求」。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;孔子是聖賢，我不是嗎？他是人，我也是人，憑什麼他是聖賢，我就不是呢？區別就在於，我本來也是聖賢，只是我的心靈被各種亂七八糟的東西給污染了。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;無善無惡心之體；有善有惡意之動；知善知惡是良知；為善去惡是格物。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;人性本無善惡，但是一旦跟世界接觸，意念一動，就出現了善惡之分。每個人的心裡都有一把尺叫良知，用這個良知去判斷善惡，最後為善去惡，&lt;a href=&quot;https://zh.wikipedia.org/zh-tw/%E6%A0%BC%E7%89%A9%E8%87%B4%E7%9F%A5&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;格物致知&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;519&quot; src=&quot;https://blog.amowu.com/_astro/cover.BKyd7hIN_Z2BSsx.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/cover.BKyd7hIN_1RFzm0.webp 640w, https://blog.amowu.com/_astro/cover.BKyd7hIN_Z2BSsx.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;h3 id=&quot;知行合一&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#知行合一&quot;&gt;知行合一&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;王陽明的另一思想「知行合一」是什麼意思呢？狹義的理解是「不要說一套，做一套」。&lt;/p&gt;
&lt;p&gt;更深一層的理解呢？&lt;/p&gt;
&lt;p&gt;首先，是提升自己的認知，來驅動更正確的行為。舉個投資的例子，「行」很簡單，&lt;a href=&quot;https://www.cathaysec.com.tw/exclude_AL/tree_elf.aspx&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;樹精靈&lt;/a&gt;點兩下，就「錢出去、股票進來」了。但是你有「發大財」嗎？知道哪個公司能投資，將來會漲，這個「知」是極難提升的。這就是「以更高的知來驅動正確的行」。&lt;/p&gt;
&lt;p&gt;反過來說，所有「行」的目的，都是要提升自己的「知」。&lt;/p&gt;
&lt;p&gt;為什麼知行合一對我們這代人來說特別難？因為從知到行當中有條鴻溝叫「選擇」。&lt;/p&gt;
&lt;p&gt;我們這代人就是選擇太多，分分鐘就面對大量的選擇，看起來似乎是好事，但是它在撕裂我們的認知，每一個選擇的背後都有道理，但是一旦面臨選擇的時候，就會產生碎片化的焦慮，衍生出拖延之類的負面效應。&lt;/p&gt;
&lt;p&gt;人性有兩個基本的訴求，分別是「意義」和「規範」。我為何而活？為了這個目標，我怎麼活？這兩個東西一有，幸福感油然而生。&lt;/p&gt;
&lt;h2 id=&quot;曾國藩的正面與側面&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#曾國藩的正面與側面&quot;&gt;《曾國藩的正面與側面》&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;接著另一位三不朽代表人物，後人是這麼評價曾國藩的一生：「立德立功立言三不朽，為師為將為相一完人」。&lt;/p&gt;
&lt;p&gt;曾國藩解&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%A4%AA%E5%B9%B3%E5%A4%A9%E5%9B%BD&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;太平天國之亂&lt;/a&gt;，救清廷與累卵之間，是為立功；有家書傳世，著書立說，清末民初的&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%9A%B4%E5%BE%A9&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;嚴復&lt;/a&gt;、&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E6%9E%97%E7%B4%93&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;林紓&lt;/a&gt;，以及&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E8%B0%AD%E5%97%A3%E5%90%8C&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;譚嗣同&lt;/a&gt;、&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E6%A2%81%E5%90%AF%E8%B6%85&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;梁啓超&lt;/a&gt;等人均受他的文風影響，是為立言；但也因前有鎮壓太平軍，後處置天津教案不利，德行受損，難以立德。因此只被封為半個三不朽。&lt;/p&gt;
&lt;p&gt;曾國藩年輕時深受儒家熏陶，後來又帶兵打仗，採用法家的嚴刑峻法來治兵治民，這儒、法兩家思想體系結合在一起，讓自己的意識形態變得非常僵化和嚴酷，而且性格上又太固執，做事強硬，所以在旁人眼裡，曾國藩做人做事都缺少寬容和彈性。長此以往，就成了官場上的孤家寡人，給人排擠出去。&lt;/p&gt;
&lt;p&gt;後來的他心態轉變了，開始站在第三方角度來審視自己。找出《&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E8%80%81%E5%AD%90_%28%E6%9B%B8%29&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;道德經&lt;/a&gt;》和《&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E8%8E%8A%E5%AD%90_%28%E6%9B%B8%29&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;南華經&lt;/a&gt;》這些道家和佛教的經典來幫助自己提升思想境界。&lt;/p&gt;
&lt;p&gt;在反思的過程中，他結合了道家的無為和辯證思想，真正理解了老子所說的「天下之至柔，馳騁天下之至堅」的道理。漸漸懂得政治家的處事態度一定要謙退、理性，要把官僚體製作為實現政治理想的工具，而不是時刻想著去反抗破壞這個系統。另外，官場上最基本的運行機制是利益，利益才是人與人合作最廣泛的共識，因為利益可以協商、可以分割，還可以交換，政治家必須要學會用利益來吸納更多同盟者為自己服務。&lt;/p&gt;
&lt;p&gt;終於，曾國藩完成了從一個政治空想家到成熟的政治家的轉型。&lt;/p&gt;
&lt;p&gt;有人批評曾國藩為什麼要貪污受賄呢？因為他必須依靠政治利益在官場上跟別人打交道，不得不接受已有的官場潛規則，但這些行為不是他做官的目的，而是他處事的手段。&lt;/p&gt;
&lt;p&gt;曾國藩面對自己的時候，他是一個問心無愧的聖賢；而當他面對整個官場的時候，他又是一個圓滑務實的政治實幹家。他對西方文明的寬容，最終讓中國這個老舊帝國邁出走向近代化的第一步。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;上帝要想懲罰人類，大概有幾種方式：一場飢荒、一場瘟疫，或者一場戰爭；但還有一種方式，就是在人間降臨一個道德家，如果這個人的道德目標一般人做不到，而且他還有強大的煽動力的話，這個人很危險，是人類的災難。―― 錢鐘書&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我們過去都以為，貪官才是惡人，但&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%88%98%E9%B9%97&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;劉鶚&lt;/a&gt;認為，清官可能更惡。因為貪官幹的那些事，他自己知道不道德，他不敢光明正大地幹。但是一個清官，如果他覺得自己有道德追求，幹這些事又不是為了自己，那麼他幹起壞事來會完全沒有底線。&lt;/p&gt;</content:encoded><category>Weekly</category></item><item><title>暗淡藍點</title><link>https://blog.amowu.com/posts/2019-01-28-weekly-007/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2019-01-28-weekly-007/</guid><description>新聞</description><pubDate>Mon, 28 Jan 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;新聞&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#新聞&quot;&gt;新聞&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;三條底線理論&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#三條底線理論&quot;&gt;三條底線理論&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;上周，台北市長柯文哲在專訪中談到兩岸的時候，提出了一個「三條底線」理論。聽完之後覺得有趣，故整理之後分享上來。&lt;/p&gt;
&lt;p&gt;八年前，美中台三方的底線分別如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;美國的底線：台灣不能成為中共的軍事基地，突破&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E7%AC%AC%E4%B8%80%E5%B3%B6%E9%8F%88&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;第一島鏈&lt;/a&gt;。&lt;/li&gt;
&lt;li&gt;中國的底線：台灣不能法理上搞獨立，變更國旗和國號。&lt;/li&gt;
&lt;li&gt;台灣的底線：中共不可以實質上統治我，人民不希望發生戰爭。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;306&quot; height=&quot;316&quot; src=&quot;https://blog.amowu.com/_astro/image1.Cg0tIVIi_1yM0qR.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image1.Cg0tIVIi_1yM0qR.webp 306w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;現在，因為美中對抗的關係，三方的底線有了變化：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;美國：台灣應該要對中國再強硬一點&lt;/li&gt;
&lt;li&gt;中國：台灣應該表示將來願意統一&lt;/li&gt;
&lt;li&gt;台灣：人民無法接受兩岸一家親&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;316&quot; height=&quot;316&quot; src=&quot;https://blog.amowu.com/_astro/image2.fBgvdEBk_Z3iYD7.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image2.fBgvdEBk_Z3iYD7.webp 316w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;三條底線都向內縮的結果就是，台灣的空間越來越小。政治人物會越來越難做，因為拿到的考卷題目會越來越難。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;考第一名的條件是，第二名考得比你差&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我們對政治人物的期望是不對的，不是誰當上總統，然後就突然天下太平了。國家之力量在國民全體，整個國家，如果每個國民都做好了，國家最後才會變好，如果國家的基礎結構不是很好，換誰當總統也一樣。&lt;/p&gt;
&lt;p&gt;柯 P 提到，他最佩服的美國總統是雷根，聽說他晚年老年癡呆症，結果他還可以做得很好。這表示什麼，當總統不一定要很厲害，每一件事情都有人可以幫你處理。&lt;/p&gt;
&lt;p&gt;歐美首長連任失敗的案例很少見。通常前兩年要先處理前人的東西，第三四年才是開始做你要做、想做的東西，可是要做的東西要好幾年，結果第四年一到，沒政績，就中斷了。這對整個台灣的地方政治是很大的傷害。&lt;/p&gt;
&lt;p&gt;民調治國，如果一年就要看到成果，為了扭轉民調，政治人物就會被迫要開始做那些「短利」的事情。雖然大家聽起來會不爽，但跟那些歐美老牌民主國家比較，我們的平均國民水準沒那麼高，這是事實。&lt;/p&gt;
&lt;p&gt;弱國無外交，自己的經濟實力要夠。90 年代為什我們很好過？那時台灣的 GDP 佔全中國 43%，2017 掉到只剩 4.6%。假如現在還是 43%，那什麼和平協定都好談；沒有實力，無論是簽了條約、談了共識，歷史證明，哪一個有效的？&lt;/p&gt;
&lt;p&gt;過去 20 年台灣國力損耗太多。如果像九二一大地震一發生，全世界電腦價格就要上漲的時候，那台灣就很安全了。如果有一天，台灣出事，全世界經濟會金融風暴，台灣就安全了。所以不是什麼簽協定的問題，是先壯大自己的問題。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;教育興則國家興，教育強則國家強&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;要是台灣出現一位有辦法同時說服中國和美國底線的人，就實在是太了不起了。這不是不可能的事，這就是智慧。中國可以不仁，台灣不可以不智。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;惟仁者，能以大事小；惟智者，能以小事大&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;文摘&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#文摘&quot;&gt;文摘&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;圍城&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#圍城&quot;&gt;圍城&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;人生有三座圍城：婚姻、事業與自我。&lt;/p&gt;
&lt;p&gt;婚姻這座圍城，外圍的人想進去，裡面的人想出來。&lt;/p&gt;
&lt;p&gt;美好的愛情永遠只能停留在記憶與想像裡，一但落到現實的柴米油鹽之中，就會失去原來純粹的模樣。每個男人的生命中都會有一朵白玫瑰和一朵紅玫瑰，無論娶了哪一朵，最終都會是遺憾。最完美的是永遠得不到的那朵玫瑰。&lt;/p&gt;
&lt;p&gt;最近剛好看了《&lt;a href=&quot;https://zh.wikipedia.org/zh-tw/%E6%88%91%E7%9A%84%E5%B0%91%E5%A5%B3%E6%99%82%E4%BB%A3&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;我的少女時代&lt;/a&gt;》和日本翻拍的《&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E9%82%A3%E4%BA%9B%E5%B9%B4%EF%BC%8C%E6%88%91%E5%80%91%E4%B8%80%E8%B5%B7%E8%BF%BD%E7%9A%84%E5%A5%B3%E5%AD%A9_%28%E6%97%A5%E6%9C%AC%29&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;那些年，我們一起追的女孩&lt;/a&gt;》，剛好可以解釋為什麼這種純愛片，既老梗又狗血，卻還是有市場（包括我），因為走的都是這個套路。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;291&quot; src=&quot;https://blog.amowu.com/_astro/image3.2PI3MyDA_Z1Sx4VJ.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image3.2PI3MyDA_Z6hrYh.webp 640w, https://blog.amowu.com/_astro/image3.2PI3MyDA_Z1Sx4VJ.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;小時候不理解《&lt;a href=&quot;https://zh.wikipedia.org/zh-tw/%E7%81%8C%E7%AF%AE%E9%AB%98%E6%89%8B&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;灌籃高手&lt;/a&gt;》結局為什麼不讓湘北拿冠軍，後來作者井上雄彥在&lt;a href=&quot;https://movie.douban.com/subject/4849922/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;訪談&lt;/a&gt;中回答：「因為青春，往往是不完美的」。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;640&quot; height=&quot;416&quot; src=&quot;https://blog.amowu.com/_astro/image4.CFWRwexd_Zp4pTD.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image4.CFWRwexd_Zp4pTD.webp 640w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;人終其一生，都努力想要活成別人所期待的樣子。也許「自我」才是最大的一座圍城。&lt;/p&gt;
&lt;p&gt;有時候，才子的信手捻來卻勝過平庸之輩的嘔心瀝血，指的就是&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E9%8C%A2%E9%8D%BE%E6%9B%B8&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;錢鍾書&lt;/a&gt;的《&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%9B%B4%E5%9F%8E_%28%E5%B0%8F%E8%AF%B4%29&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;圍城&lt;/a&gt;》吧。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;題外話，前陣子在豆瓣&lt;a href=&quot;https://movie.douban.com/subject/26366465/questions/63797/?from=subject&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;討論區&lt;/a&gt;，看見下圖這段會心一笑的回覆：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;327&quot; src=&quot;https://blog.amowu.com/_astro/image5.BOYCL8J__2fNts4.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image5.BOYCL8J__298eoD.webp 640w, https://blog.amowu.com/_astro/image5.BOYCL8J__2fNts4.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;h3 id=&quot;維梅爾的帽子&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#維梅爾的帽子&quot;&gt;維梅爾的帽子&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;這是一部講述「從一幅畫看十七世紀全球貿易」的作品，其中最令我印象深刻的一段話：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;人非孤島，無人可以自全。整個世界的歷史，無論是屠殺還是成就，都是我們共同的遺產；每個民族乃至每個人的命運，也都是整個世界的縮影。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;這讓我想起 NASA 的&lt;a href=&quot;https://zh.wikipedia.org/zh-tw/%E6%97%85%E8%A1%8C%E8%80%851%E5%8F%B7&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;航海家 1 號&lt;/a&gt;在完成任務之後，即將飛出太陽系之際，拍攝了最後一張地球的照片，天文學家&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%8D%A1%E5%B0%94%C2%B7%E8%90%A8%E6%A0%B9&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;卡爾·薩根&lt;/a&gt; 那段關於《&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E6%9A%97%E6%B7%A1%E8%97%8D%E9%BB%9E&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;暗淡藍點&lt;/a&gt;》的經典名言：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;看一眼這個小點。
在這裡。這就是家。這就是我們。
在這個小點上，每一個你愛的人，每一個你認識的人，每一個你聽說過的人，每一個人，無論他是誰，都曾經生活過。
我們所有的快樂和掙扎，數以千萬自傲的宗教信仰、思想體系觀念意識，以及經濟學原理教義，每一個獵人或征服者，每一位勇士或是懦夫，每一個文明的締造者或摧毀者，每一位君王或農夫，每一對陷入愛河的年輕伴侶，每一位為人父母者，所有充滿希望的小孩，發明家或探險者，每一位靈魂導師，每一個腐敗的政客，每一個所謂的「超級巨星」，每一個所謂的「最偉大領袖」，每一位我們人類史上的聖人或是罪人……我們的一切一切，全部都存在於這樣一粒懸浮在一束陽光中的塵埃上。
（略）
地球，目前我們唯一已知有生命居住的世界。沒有其它任何一個地方，至少在不遠的未來，可供我們這一物種移民。去看看，可以。常駐，不可能。不管你喜歡還是不喜歡，目前為止只有地球是我們的立足之地。
天文學是一門謙卑的、同時也是塑造性情的學問。沒有什麼能比從遙遠太空拍攝到的我們微小世界的這張照片，更能展示人類的自負有多愚蠢。於我而言，這也是在提醒我們，我們的責任：互相間更加和善的對待彼此、維護和珍惜這顆暗藍色的小點，這個我們目前所知唯一共同的家園。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;394&quot; src=&quot;https://blog.amowu.com/_astro/cover.BNOEE87E_ZOE03U.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/cover.BNOEE87E_pmVaV.webp 640w, https://blog.amowu.com/_astro/cover.BNOEE87E_ZOE03U.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://youtu.be/IBEjTawbeYw?si=CJDA6pcmq8l0_Dj1&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://youtu.be/IBEjTawbeYw?si=CJDA6pcmq8l0_Dj1&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;本周金句&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#本周金句&quot;&gt;本周金句&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;1、&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;人造物表現得越來越像生命體，而生命變得越來越機械化。
古希臘只有自由男人才有投票權，但歷史的發展，不斷地把女人、奴隸、異族人、不同種族和膚色的人，甚至是動物、植物等等都放進了倫理主體的範疇。未來，機器也會進來。――《前沿科技之腦機接口》&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;2、&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;如果全人類 都有讀心術
或許有一點殘酷 但也省去些痛苦
我口是心非 是不得不
這樣流連在你身邊 才不會顯得太突兀―― 韋禮安《&lt;a href=&quot;https://www.youtube.com/watch?v=aN7R2Y28F68&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Luvin’ U&lt;/a&gt;》&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;誠實且和平的世界是最美好的，可是，兩者相比的話，比較重要的是和平吧？――《獵人》&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;3、&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;人可以沒投資，但是不能沒保險。――《&lt;a href=&quot;https://mp.weixin.qq.com/s/gTioASlvK7kt9bAmz2epWw&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;保哥和八姐&lt;/a&gt;》&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;4、&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;成功就是把正確的簡單事反覆做――《&lt;a href=&quot;https://sspai.com/post/52149&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;我的幾個每月必做例行任務&lt;/a&gt;》&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;5、&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;昨日的額外功能會變為今日的核心功能，今日的核心功能會變成明日的基礎功能。――《&lt;a href=&quot;https://sspai.com/post/52240&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;硅谷產品大佬們寫的書 Ship it 到底在講什麼？&lt;/a&gt;》&lt;/p&gt;
&lt;/blockquote&gt;</content:encoded><category>Weekly</category></item><item><title>殺雞用牛刀</title><link>https://blog.amowu.com/posts/2019-01-21-weekly-006/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2019-01-21-weekly-006/</guid><description>為何會有公司願意花大錢在電視、地鐵、體育比賽投放廣告？</description><pubDate>Mon, 21 Jan 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;為何會有公司願意花大錢在電視地鐵體育比賽投放廣告&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#為何會有公司願意花大錢在電視地鐵體育比賽投放廣告&quot;&gt;為何會有公司願意花大錢在電視、地鐵、體育比賽投放廣告？&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;如果無法透過「點擊率」追蹤「轉化率」，那這類廣告有何用？&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;告訴你有這麼一個品牌存在，否則剛出生的人再過幾年之後就不知道它的存在了。&lt;/li&gt;
&lt;li&gt;既然肯花大錢買廣告，表示很重視該產品，應該不會半途而廢，服務肯定不錯。&lt;/li&gt;
&lt;li&gt;你知道其他人肯定也看過這個廣告了，你買了這東西之後就有面子了。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;相比之下，網站或 App 類型的砸錢廣告好像比較少見，我認為《羅輯思維》在 2018 跨年演講中給出了理由。&lt;/p&gt;
&lt;p&gt;互聯網公司要想上央視春晚，門檻是 DAU（每日活躍人數）超過一億，否則廣告出來的那一瞬間，你的伺服器就會掛掉。&lt;/p&gt;
&lt;p&gt;有句話說，世界上最悲慘的事情是，錢花光了，人還在。&lt;/p&gt;
&lt;p&gt;但是，世界上更悲慘的事是，花光自己的錢買廣告，搞掛了自己的產品，人還在。&lt;/p&gt;
&lt;p&gt;最近聽了一本名叫《&lt;a href=&quot;https://book.douban.com/subject/10540584/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;空雨衣&lt;/a&gt;》的書，空雨衣是某座公園的一尊雕像，象徵著現代人所面臨的悖論：所有人都被「成功」這個目標綁架，不斷被驅趕著往前跑，反而失去了生活本身的意義。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;公司的經營目的是什麼？&lt;/li&gt;
&lt;li&gt;公司究竟屬於誰的？&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;追求利潤應該只是公司的手段而不是目的，就像我們吃飯是為了活著，但是活著不是為了吃飯。&lt;/p&gt;
&lt;p&gt;公司的英文單字 Company，本意是一群夥伴，這才是公司的本質。&lt;/p&gt;
&lt;p&gt;公司的目的是追求自身的永續存在，它本身不是誰的財產，也沒有股東；但是有規章、有成員、有管委會、有自己的資產，等等。&lt;/p&gt;
&lt;p&gt;在未來，公司應該是這樣一種組織：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;聯邦制：公司會有一個負責管理和協調的核心機構，還有好幾個具有獨立行動能力的業務單元。&lt;/li&gt;
&lt;li&gt;雙重公民身份：員工既是整個公司的一員，又屬於某個業務團隊，需要同時對公司和團隊負責。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;公司是由全體員工組成的自治型社區，所有成員都是夥伴關係。股份持有者只能分享紅利，不能參與經營。&lt;/p&gt;
&lt;p&gt;為了落實「聯邦制」和「雙重公民身份」，應該降低個人績效獎金的比重，加大團體績效和公司整體績效獎金的比重。&lt;/p&gt;
&lt;p&gt;利益捆綁是建立身份認同最有效的辦法。&lt;/p&gt;
&lt;h2 id=&quot;軟體工程師的績效考核是否考慮技術難度&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#軟體工程師的績效考核是否考慮技術難度&quot;&gt;軟體工程師的績效考核是否考慮技術難度？&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Facebook 的核心精神是「Focus on Impact」和「Move Fast」，每半年一次的績效考核只考慮做出來的結果而不考慮過程。&lt;/p&gt;
&lt;p&gt;如果通過改一行程式碼就能讓公司賺到與做一個複雜系統同樣多的錢，那兩者在評等上會是等價的。&lt;/p&gt;
&lt;p&gt;這可能是 Facebook 與 Google 在軟體工程理念上最大的區別，因為 Google 無疑是把技術難度看得很重的。&lt;/p&gt;
&lt;p&gt;Google 是一家思維方式與眾不同的公司，它認為，殺雞一定要用牛刀。&lt;/p&gt;
&lt;p&gt;舉個電鑽的例子，很多人認為「日本製造」是品質的象徵。比日本製造品質更好的是「德國製造」。但最後會發現「瑞士製造」才是最好用。雖然比其它國家生產的要貴一倍，但是在工作效率和壽命上提高了十倍，怎麼算都是一筆划算的投資。&lt;/p&gt;
&lt;p&gt;瑞士這個小國的產品不多，但是只要它做，就是精品。&lt;/p&gt;
&lt;p&gt;做同樣的東西，即使功能相同，做得好不好，價值可以有天壤之別。瑞士製造的成本可能只比其他國家貴一倍，但是銷售價格可以貴十倍、百倍。&lt;/p&gt;
&lt;p&gt;Google 在規模還很小的時候，就在貫徹「瑞士製造」的指導思想，一個大學生能完成的事，如果能找一個博士生來做，那麼一定能比同類公司做得更好。雖然要為這些員工多付一些工資，但也因此打造了「Google 製造」的品質，在商業競爭中取得很大的優勢。&lt;/p&gt;
&lt;h3 id=&quot;績效考核不考慮技術難度的好處&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#績效考核不考慮技術難度的好處&quot;&gt;績效考核不考慮技術難度的好處？&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Facebook 在早期是一個產品驅動的公司，很多事情的瓶頸並不在技術上。在結果驅動的考核機制下，工程師們會自發解決那些技術之外的問題，而不是對沒有技術難度的事情嗤之以鼻放在那不管，悶頭繼續敲自己的程式碼。&lt;/p&gt;
&lt;p&gt;Facebook 的軟體工程師同時分擔了一部分產品經理的責任。&lt;/p&gt;
&lt;h3 id=&quot;績效考核考慮技術難度的好處&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#績效考核考慮技術難度的好處&quot;&gt;績效考核考慮技術難度的好處？&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;因為技術發展很多時候是非線形的。常見的一種情況是，研發的前幾年進展很小，但超越過去之後飛速發展。如果在每半年一次的考核中只考慮結果，那這些高風險高回報的技術專案永遠不會被啓動。&lt;/p&gt;
&lt;p&gt;要找到並留住大量肯動腦又肯動手的博士生，並不容易。同時，為了滿足這麼多聰明人做自己願意做的事，搞出了很多意義不大的小專案。要兼顧調動員工積極性、鼓勵創造性並集中精力在核心業務和重大專案上，對公司來講不是件容易的事。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;415&quot; src=&quot;https://blog.amowu.com/_astro/image1.BNUYfxPA_1LRTlv.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image1.BNUYfxPA_ZOXdW4.webp 640w, https://blog.amowu.com/_astro/image1.BNUYfxPA_1LRTlv.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;這讓我想起《&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E8%90%AC%E6%9B%86%E5%8D%81%E4%BA%94%E5%B9%B4&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;萬曆十五年&lt;/a&gt;》書中提到的觀點：&lt;/p&gt;
&lt;p&gt;萬曆皇帝遭遇到的困境，是西方精細的工商業社會和東方小農社會之間的衝突。在政治、經濟、軍事三個方面都有體現。&lt;/p&gt;
&lt;p&gt;對於中國這樣大體量的農業國家來說，首要目的，是維持整個國家不散攤子。要想方設法維持社會的穩定，所以只能追求「大約」，也就是差不多就可以了。&lt;/p&gt;
&lt;p&gt;政治上，用道德這樣的抽象原則維持就可以了，經濟上收收農業稅就行了，軍事上，士兵用大刀也就不錯了。&lt;/p&gt;
&lt;p&gt;這樣的血液下，無論張居正和戚繼光多麼天才，都不可能改革成功，他們只能成為時代的悲劇。&lt;/p&gt;
&lt;p&gt;今天，我們很多人以快速迭代、以及所謂的彎道超車為藉口，做事馬虎，只圖快，不遵守規矩，這都會留下安全隱患。這個習慣一旦養成，就難以再改了。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;反觀華為，因為 2018 年中美貿易戰的緣故，首當其衝成為美國開刀的對象，被指控不安全，可能含有間諜軟件，竊聽國家機密。&lt;/p&gt;
&lt;p&gt;近日&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E4%BB%BB%E6%AD%A3%E9%9D%9E&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;任正非&lt;/a&gt;為了走出這場危機，在致全體員工的一封信《&lt;a href=&quot;https://www.chainnews.com/articles/062509325617.htm&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;全面提升軟件工程能力與實踐，打造可信的高質量產品&lt;/a&gt;》，信中有一句話：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;我們要轉變觀念，追求打造可信的高質量產品，不僅僅是功能、特性的高質量，也包括產品開發到交付過程的高質量。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;先撇開華為本身的評價如何、以及這封信是否真為任正非親筆所寫，更不考慮落實難度，單就內容本身，節錄一些有趣的部分：&lt;/p&gt;
&lt;p&gt;1、&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;我們各級管理者和全體員工都不得以進度、功能、特性等為理由來降低可信的要求，確保可信的要求在執行過程中不變形。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;667&quot; height=&quot;268&quot; src=&quot;https://blog.amowu.com/_astro/image2.Hn2tDQpe_Zx3n1E.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image2.Hn2tDQpe_Z1VCdKW.webp 640w, https://blog.amowu.com/_astro/image2.Hn2tDQpe_Zx3n1E.webp 667w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;2、&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;我們要從最基礎的編碼質量做起，視高質量代碼為尊嚴和個人聲譽。代碼就像是高樓大廈的一磚一瓦，沒有高質量的代碼，可信的產品就是空中樓閣。我們要優化並遵循公司各種編程規範，遵從架構與設計原則，熟練使用各種編程庫和 API，編寫出簡潔、規範、可讀性強、健壯安全的代碼。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;3、&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;在確保可信的前提下，要在性能、功能、擴展性等方面做好權衡；慎重地定義我們的模塊與接口，真正做到高內聚與低耦合；我們要遵循權限和攻擊面最小化等安全設計原則，科學設計模塊之間的隔離與接口，提升安全性；低階架構與設計要遵循高階的架構與設計原則，在充分理解原有架構與設計的情況下，持續優化；我們要熟悉各種設計模式，重用公共成熟組件和服務，避免重復勞動。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;4、&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;我們知道，再好的架構，其生命力也是有限的。隨著時間的推移、環境的變化以及新技術、新功能特性的引入，架構也會腐化。面對腐化了的架構，要毫不猶豫地去重構它。同時主動以可信設計原則為導向，去重構不符合軟件工程規範和質量要求的歷史代碼，提升軟件架構的生命力。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;5、&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;軟件工程和質量工程需要依靠架構技術，而不是依靠 CMM 和 QA 管理流程。一切工程問題，首先要思考能否通過技術解決，當前技術無法解決的問題，暫時由管理手段代勞，同時不停止尋找技術手段。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;6、&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;軟件工程就像一個國家的農業，是最基礎的設施！&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;p&gt;最後，節錄幾段網友&lt;a href=&quot;https://www.cnblogs.com/dotey/p/10220520.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;宝玉&lt;/a&gt;的金句：&lt;/p&gt;
&lt;p&gt;1、&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;每個人都在一個領域能有深入的鑽研，同時對其他領域有一定瞭解，對個人，對團隊是非常有利的一件事。這樣也不需要 DevOps 這種為了兼顧開發、測試、運維三種角色而存在的工種！&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;2、&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;所有的涉及到人的管理最終都要歸結到人管理還是制度管理的問題上，軟件項目管理也不例外，如果過多的依賴於人的管理，那麼項目經理的職責就太重了，優秀的項目經理本身就是稀缺資源，最終會變成一個瓶頸。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;3、&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;有人一提到軟件工程，就馬上說軟件工程不是銀彈。軟件工程從來不說自己是銀彈，就像現代醫學，也不會號稱自己包治百病，只會不斷改進，對症下藥！&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;技術應該只是我們打造產品的手段而不是目的。&lt;/p&gt;
&lt;h2 id=&quot;參考資料&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#參考資料&quot;&gt;參考資料&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://wanqu.co/a/7209/ads-dont-work-that-way/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Ads Don’t Work That Way | 湾区日报&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://m.igetget.com/share/topic/tid/3893&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;得到｜《空雨衣》| 徐玲解读&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://m.okjike.com/originalPosts/5c30ab12f73f4d001102f8f8?share%5C_depth=2&amp;amp;share%5C_distinct%5C_id=168664274f693d-0dc2411747574-10306653-1296000-168664274f77aa&amp;amp;username=C5EFE5DB-70A4-4D38-AD96-67D616AB5B47&amp;amp;share_distinct_id=168664274f693d-0dc2411747574-10306653-1296000-168664274f77aa&amp;amp;share_depth=1&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;关于程序员的绩效考核要不要考虑技术难度 — 即刻App&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://m.igetget.com/share/course/pay/detail/4/42&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;吴军的谷歌方法论&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.levels.fyi/SE/Microsoft/Google/Facebook&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Compare career levels across companies with Levels.fyi&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://m.igetget.com/share/topic/tid/2849&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;得到｜《万历十五年》| 大象公会解读&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://blog.jobbole.com/114605/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;从软件工程的角度解读任正非的新年公开信&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>Weekly</category></item><item><title>財神的精確度</title><link>https://blog.amowu.com/posts/2019-01-14-weekly-005/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2019-01-14-weekly-005/</guid><description>新聞</description><pubDate>Mon, 14 Jan 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;新聞&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#新聞&quot;&gt;新聞&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;1財神和前澤&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#1財神和前澤&quot;&gt;1、財神和前澤&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;日本大型線上時裝購物網 Zozotown 創辦人前澤友作，因新年打破最快達 100 億日圓營業額紀錄，所以決定當個大撒幣（誤）在 Twitter 上抽選 100 名網友，每人 100 萬日圓的紅包。沒想到，這則推文吸引超過 500 萬名網友的轉推，打破「&lt;a href=&quot;https://twitter.com/carterjwm/status/849813577770778624&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;溫蒂漢堡&lt;/a&gt;」保持的 350 萬轉推數，成為 Twitter 有史以來最多人轉推的推文。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://x.com/yousuck2020/status/1081544630754103296?ref_src=twsrc%5Etfw%7Ctwcamp%5Etweetembed%7Ctwterm%5E1081544630754103296%7Ctwgr%5Ed723c4d7590a665cfb47bed0f188431e1399c0ea%7Ctwcon%5Es1_c10&amp;amp;ref_url=https%3A%2F%2Fcdn.embedly.com%2Fwidgets%2Fmedia.html%3Ftype%3Dtext2Fhtmlkey%3Da19fcc184b9711e1b4764040d3dc5c07schema%3Dtwitterurl%3Dhttps3A%2F%2Ftwitter.com%2Fyousuck2020%2Fstatus%2F1081544630754103296image%3D&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://x.com/yousuck2020/status/1081544630754103296?ref_src=twsrc%5Etfw%7Ctwcamp%5Etweetembed%7Ctwterm%5E1081544630754103296%7Ctwgr%5Ed723c4d7590a665cfb47bed0f188431e1399c0ea%7Ctwcon%5Es1_c10&amp;amp;ref_url=https%3A%2F%2Fcdn.embedly.com%2Fwidgets%2Fmedia.html%3Ftype%3Dtext2Fhtmlkey%3Da19fcc184b9711e1b4764040d3dc5c07schema%3Dtwitterurl%3Dhttps3A%2F%2Ftwitter.com%2Fyousuck2020%2Fstatus%2F1081544630754103296image%3D&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;前澤友作，&lt;a href=&quot;https://forbesjapan.com/feat/japanrich/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;2018 福布斯日本排名最富有人物&lt;/a&gt;第 18 位。SpaceX 宣布其為全球首位私人月球旅行的乘客，並且提出「&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E4%BA%B2%E7%88%B1%E7%9A%84%E6%9C%88%E7%90%83&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;親愛的月球&lt;/a&gt;」計劃，邀請六至八位藝術家參加 2023 年進行的環遊月球之旅。&lt;/p&gt;
&lt;h3 id=&quot;2財神對貝佐斯&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#2財神對貝佐斯&quot;&gt;2、財神對貝佐斯&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;再來看看另一位也想上太空的富豪，2018 福布斯世界排名最富有人物第 1 位的傑夫·貝佐斯「&lt;a href=&quot;https://www.ettoday.net/news/20190110/1352847.htm&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;宣佈離婚&lt;/a&gt;」，很有可能因為財產均分而讓出世界首富之位，這將會是「史上最昂貴的離婚」，屆時前妻將因此成為全球女首富。&lt;/p&gt;
&lt;p&gt;身為 Amazon 最大股東的貝佐斯，一共持有 8000 萬股的股票，如果平分財產，將導致貝索斯的持股佔比下降至 8.15 %，雖然仍遠超第二大股東 5.8% 的持股，但這勢必將削弱他對公司的所有權和所有權。&lt;/p&gt;
&lt;h3 id=&quot;3財神的精確度&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#3財神的精確度&quot;&gt;3、財神的精確度&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;節錄自 &lt;a href=&quot;https://www.ifanr.com/1158813&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;ifanr 報導&lt;/a&gt;：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;2018 年一共產生了 72 萬億美元成本的塑膠垃圾。
72 萬億美元可以買到什麼？
72 萬億美元能同時買下 Apple、Google、微軟、Amazon、Facebook 還有找⋯⋯。
如果有人把地球上所有廢棄塑膠都回收起来，那麼下一位全球首富就是他的了。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;身為一個無法得知金融交易內線消息、只能計較 KPI 那點小錢的工薪階層，富豪們的世界實在離我太遙遠了⋯⋯。財神哪一天能找上門呢？&lt;/p&gt;
&lt;h2 id=&quot;文摘&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#文摘&quot;&gt;文摘&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;一個末日各自表述&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#一個末日各自表述&quot;&gt;一個末日，各自表述&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;本周，我想分享&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E4%BA%9E%E7%91%9F%C2%B7%E6%9F%A5%E7%90%86%E6%96%AF%C2%B7%E5%85%8B%E6%8B%89%E5%85%8B&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;克拉克&lt;/a&gt;的《地球末日》三部曲，分別是《遙遠地球之歌》、《地光》和《最後一個地球人》。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;312&quot; src=&quot;https://blog.amowu.com/_astro/image1.DdWnFLr__Z2oRBxm.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image1.DdWnFLr__1tajo.webp 640w, https://blog.amowu.com/_astro/image1.DdWnFLr__Z2oRBxm.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;阿瑟·克拉克，最知名的科幻小說作品是《&lt;a href=&quot;https://zh.wikipedia.org/wiki/2001%E5%A4%AA%E7%A9%BA%E6%BC%AB%E9%81%8A_%28%E9%9B%BB%E5%BD%B1%29&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;2001 太空漫遊&lt;/a&gt;》，與&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E8%89%BE%E8%90%A8%E5%85%8B%C2%B7%E9%98%BF%E8%A5%BF%E8%8E%AB%E5%A4%AB&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;艾西莫夫&lt;/a&gt;、&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E7%BE%85%E4%BC%AF%E7%89%B9%C2%B7%E6%B5%B7%E8%90%8A%E5%9B%A0&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;海萊恩&lt;/a&gt;並稱為二十世紀三大科幻小說家。&lt;/p&gt;
&lt;p&gt;用一句話概括《地球末日》系列，那就是「一個地球末日，各自表述 」：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;遙遠地球之歌：環境如何毀滅地球&lt;/li&gt;
&lt;li&gt;地光：人類如何毀滅地球&lt;/li&gt;
&lt;li&gt;最後一個地球人：未知如何毀滅地球&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;以下，聊聊一些我覺得有趣的部分。&lt;/p&gt;
&lt;h3 id=&quot;遙遠地球之歌&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#遙遠地球之歌&quot;&gt;遙遠地球之歌&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;故事發生在地球得知 1600 年後太陽會爆炸，礙於現有科技無法馬上離開太陽系，人類面臨兩個抉擇：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;K 策略「少數個人生存主義」&lt;/li&gt;
&lt;li&gt;R 策略「多數高機率生存主義」&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;最後選擇了如魚產卵般的 R 策略，成功讓人類後代在幾個太陽系外的恆星系生存下來。&lt;/p&gt;
&lt;p&gt;因為環境的關係，這些人類已經演化成了非當初地球人的型態（相貌大變化、一張白紙重新來過的全新道德觀、各系之間的人類互相沒往來）。&lt;/p&gt;
&lt;p&gt;這讓我想起《薩爾達傳說》的種族，雖然彼此之間差異大，但也都是從人類和亞人演化而來的。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;327&quot; src=&quot;https://blog.amowu.com/_astro/image2.Ch7Jj3LR_2uKdkq.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image2.Ch7Jj3LR_2o4Xh0.webp 640w, https://blog.amowu.com/_astro/image2.Ch7Jj3LR_2uKdkq.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;這也解釋了，為什麼電影中的外星人都長得像人類。成本考量？想像力受限？或許我們真的都是「&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%B0%BC%E6%AF%94%E9%AD%AF&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;尼比魯&lt;/a&gt;」星人的後裔，而非猿類？&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;436&quot; src=&quot;https://blog.amowu.com/_astro/image3.I_yMpyPv_Z13pmRo.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image3.I_yMpyPv_WYSUn.webp 640w, https://blog.amowu.com/_astro/image3.I_yMpyPv_Z13pmRo.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;h3 id=&quot;地光&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#地光&quot;&gt;地光&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;22 世紀如果一定要搶資源的話，那可能就是金屬。&lt;/p&gt;
&lt;p&gt;回顧人類幾千年的資源搶奪史：搶錢、搶糧、搶娘們。&lt;/p&gt;
&lt;p&gt;後來，隨著科學的進步，只要有了能源，財富、糧食、人口，就都好解決了。所以到了 20 世紀和 21 世紀，人類基本就只剩下搶能源了。&lt;/p&gt;
&lt;p&gt;科學家預計，人類很有希望在 21 世紀徹底解決能源問題。解決的方案就是可控「核融合」技術。只要這個技術突破了，那大海或月球上，核燃料有的是。&lt;/p&gt;
&lt;p&gt;到了 22 世紀，人類是不是就什麼都不用搶了呢？人類接下來搶的東西，應該會是稀有的元素，也就是金屬。&lt;/p&gt;
&lt;p&gt;宇宙剛出現的時候，只有氫元素和氦元素。恆星是宇宙的煉金爐，恆星生產的元素有三種：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;把兩個原子序是 1 的氫原子合在一起，變成一個原子序是 2 的氦。&lt;/li&gt;
&lt;li&gt;太陽生命的最後階段，會變成一顆紅巨星，相當於太陽爆炸。這個時候溫度高、壓力大，就能生產原子序是 6 的碳元素，或者原子序是 8 的氧元素，最多只能生產原子序數到 26 的鐵元素。&lt;/li&gt;
&lt;li&gt;只有質量大於太陽 1.44 倍的恆星，才有資格發生二次爆炸，這個過程叫超新星爆發。超新星爆發提供的溫度和壓力，才能生產原子序 26 以上的元素。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;要合成像金銀這樣的元素，需要報廢掉一個 8 倍太陽質量的恆星。用這麼高昂的代價所製造的金屬元素，它們都是這個宇宙最稀缺的東西。&lt;/p&gt;
&lt;p&gt;所以，宇宙所有元素生產的規律就是：原子序數越往後，就越難被生產出來。&lt;/p&gt;
&lt;p&gt;原始人最開始使用水、火、土、石頭和獸皮來生活，這些東西的元素組成都很簡單，基本上就是原子序數 30 以下的氫碳氮氧矽磷。這些元素也是所有低等生命賴以維生的基本元素。&lt;/p&gt;
&lt;p&gt;後來人類掌握了冶煉技術，就能利用原子序數 20 至 60 之間的鐵、銅元素，製造工具、汽車和飛機。在掌握了更高級的知識之後，我們才能用得上後面原子序數分別是 92 和 94 的鈾元素和鈽元素來造原子彈。&lt;/p&gt;
&lt;p&gt;到了 21 世紀，隨便選一個原子序數 100 以上的金屬元素，它一定在工業上佔一席之地，甚至可能支撐一個產業。&lt;/p&gt;
&lt;p&gt;整個人類的發展史，就是一個在元素週期表往上爬的過程。一個文明能利用的元素越後面，代表這個文明的層級越高。&lt;/p&gt;
&lt;p&gt;如果這個規律成立的話，未來人類在科技進步的過程中，對原子序數高的金屬的需求量一定會變得越來越大。原本你看到的那些金字旁、不知道怎麼讀音的元素，第一反應通常是，這東西和我沒啥關係；但現在不一樣了，你知道它在未來可能是對人類非常重要的資源。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;495&quot; src=&quot;https://blog.amowu.com/_astro/image4.Bokaja6h_28PpgO.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image4.Bokaja6h_1oLVDB.webp 640w, https://blog.amowu.com/_astro/image4.Bokaja6h_28PpgO.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;h3 id=&quot;最後一個地球人&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#最後一個地球人&quot;&gt;最後一個地球人&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;文明的兩大哉問：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;如果文明的目的是生存，那世界大同能不能算是一個文明的頂點？人類社會如何實現世界大同？&lt;/li&gt;
&lt;li&gt;如果生存的終點是滅亡，那人類終將面對死亡。怎樣的信仰能夠最好地面對死亡？&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;故事講述文明水平高出人類一個層級的外星文明「Overlord」來到地球，居然在十多年的時間內幫助人類建立一個世界大同的理想國，實現了人類追尋了幾千年的社會目標。&lt;/p&gt;
&lt;p&gt;怎麼實現世界大同？Overlord 只做了兩件事，第一次，毫髮無傷地抵擋住了人類的核彈攻擊。第二次，擋住太陽。就這兩件事情，分別樹立起了「絕對防禦」和「絕對攻擊」的威信，。&lt;/p&gt;
&lt;p&gt;鬧了半天，我們人類距離所謂世界大同，缺少的不是愛和什麼兩岸一家親，我們缺少的是一個攻守兼備、絕對優勢的武力。&lt;/p&gt;
&lt;p&gt;任何一個信仰，都不可避免的一個話題，就是對死亡的解釋。好的信仰應該可以讓人不恐懼死亡，讓一個人變得坦然。只要你的信仰夠深，無論什麼時候想到最終的結局，心裡都能夠不慌張，甚至是一片祥和。&lt;/p&gt;
&lt;p&gt;人們能接受死亡的兩種原因：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;有希望，例如：輪迴轉生論&lt;/li&gt;
&lt;li&gt;有意義，例如：奉獻式的犧牲小我、完成大我。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;一個信仰哪怕再惑眾，人類還是有那個想象力填補上的。但是你要是一點素材都不給我，那我就要徹底絕望了。&lt;/p&gt;
&lt;p&gt;最恐怖的不是死亡本身，而是死亡本身沒有任何希望、沒有任何意義。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;518&quot; src=&quot;https://blog.amowu.com/_astro/image5.BBQ4cC8s_1etKfE.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image5.BBQ4cC8s_Z1mP38C.webp 640w, https://blog.amowu.com/_astro/image5.BBQ4cC8s_1etKfE.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;h3 id=&quot;小丑與白痴&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#小丑與白痴&quot;&gt;小丑與白痴&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;本周，看了一部想像力爆棚的皮克斯動畫電影《&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E8%85%A6%E7%AD%8B%E6%80%A5%E8%BD%89%E5%BD%8E_%28%E9%9B%BB%E5%BD%B1%29&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;腦筋急轉彎&lt;/a&gt;》（Inside Out），導演將人類大腦的五種情緒給擬人化，然後透過情緒之間的相互作用，將影響人類性格與情感的因素給擬物化（核心記憶、個性島嶼、抽象空間、造夢製片廠、潛意識監獄、記憶廢墟和遺忘深淵）。著實有趣，極富創意。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;661&quot; src=&quot;https://blog.amowu.com/_astro/image6.CYuAS94r_Z1icXdH.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image6.CYuAS94r_1My8ut.webp 640w, https://blog.amowu.com/_astro/image6.CYuAS94r_Z1icXdH.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;至於故事的劇情就不在這裡提了，保留樂趣給還沒看過的人去挖掘。影評之類的文章，網路上也已經一籮筐了。說說我看完這部劇之後，唯一的感想。&lt;/p&gt;
&lt;p&gt;吳宗憲曾經說過：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;一個人的歡喜跟悲傷，他是要一比一的。那個比例如果出現誤差的話，你就是生病了。小丑跟白痴，他最大的差別在哪裏？白痴不知道什麼時候要停止；小丑知道，他下班了，他就會停止。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://www.facebook.com/watch/?v=156367621192907&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;吴宗宪：你知道小丑和白痴的分别吗？ 一个人的欢喜跟悲伤，它是要一比一的。 如果那个比例出现误差的话，你就是生病了。…&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;本周金句&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#本周金句&quot;&gt;本周金句&lt;/a&gt;&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;一個人能同時保有全然相反的兩種觀念，還能正常行事，是第一流智慧的標誌。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;―― &lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%BC%97%E6%9C%97%E8%A5%BF%E6%96%AF%C2%B7%E6%96%AF%E7%A7%91%E7%89%B9%C2%B7%E8%8F%B2%E8%8C%A8%E6%9D%B0%E6%8B%89%E5%BE%B7&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;費茲傑羅&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;基礎決定高度，細節決定深度。&lt;/p&gt;
&lt;/blockquote&gt;</content:encoded><category>Weekly</category></item><item><title>仿生人會夢見電子羊嗎？</title><link>https://blog.amowu.com/posts/2019-01-07-weekly-004/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2019-01-07-weekly-004/</guid><description>新聞</description><pubDate>Mon, 07 Jan 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;新聞&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#新聞&quot;&gt;新聞&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;我們真的有自由意志嗎&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#我們真的有自由意志嗎&quot;&gt;我們真的有「自由意志」嗎？&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;上周，Netflix 推出了互動式電影《&lt;a href=&quot;https://www.netflix.com/tw/title/80988062&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;黑鏡：潘達斯奈基&lt;/a&gt;》，你玩出幾星評價的結局呢？&lt;/p&gt;
&lt;p&gt;《黑鏡：潘達斯奈基》是一部巧妙結合「互動式電影」、「多重結局」和「探討自由意志」要素的作品。&lt;/p&gt;
&lt;p&gt;雖然新鮮有趣，但是個人認為互動式電影無法成為主流，理由有三：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;製作成本高，而且破解的難度不高，盜版更是道高一尺，魔高一丈。&lt;/li&gt;
&lt;li&gt;無法在電影院上映，少了一筆主要的收入來源。&lt;/li&gt;
&lt;li&gt;觀賞體驗差，對於下班只想躺在沙發上、或是邊炒菜編追劇的小確幸來講，中斷 10 秒這個 MI（Movie Interface）MX（Movie Experience）不是很好。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;而且互動式電影走到極致，其實就是被稱作「&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%85%AB%E5%A4%A7%E8%97%9D%E8%A1%93&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;第九藝術&lt;/a&gt;」的電玩遊戲，這讓互動式電影的角色定位有如「有聲書」般尷尬。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;503&quot; src=&quot;https://blog.amowu.com/_astro/image1.DEMV0NCh_24hPFd.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image1.DEMV0NCh_Z2bP0oP.webp 640w, https://blog.amowu.com/_astro/image1.DEMV0NCh_24hPFd.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;回到《黑鏡：潘達斯奈基》的主題，也許人類並不存在什麼「自由意志」。&lt;/p&gt;
&lt;p&gt;什麼叫自由意志？如果說我想要什麼就可以去追求什麼，那我就是自由的嗎？錯了，這不叫有自由意志。大猩猩、小狗和鸚鵡也可以想要什麼就去追求什麼，這沒什麼高級的，只不過是被慾望驅使。&lt;/p&gt;
&lt;p&gt;實驗證明，人的慾望並不受意識控制，而是意識受慾望控制。&lt;/p&gt;
&lt;p&gt;科學家用「&lt;a href=&quot;https://zh.wikipedia.org/zh-tw/%E5%8A%9F%E8%83%BD%E6%80%A7%E7%A3%81%E5%85%B1%E6%8C%AF%E6%88%90%E5%83%8F&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;功能性磁振造影&lt;/a&gt;」，觀察受驗者的大腦。結果發現，在受驗者做決定的幾百毫秒之前，在他還沒有意識到自己要怎麼選擇之前，科學家就已經可以透過大腦成像圖，提前知道了他會怎麼選。&lt;/p&gt;
&lt;p&gt;不但可以先一步「知道」你想幹什麼，還可以進一步「控制」你想幹什麼。&lt;/p&gt;
&lt;p&gt;科學家在老鼠大腦中插入三個電極，然後實現了遙控指揮，讓牠直行、爬樓梯或繞著轉圈，怎麼都行，跟遙控玩具一樣。&lt;/p&gt;
&lt;p&gt;這對老鼠太殘忍了？但是科學家控制的其實是老鼠的意願。你看到的是老鼠被遙控，而老鼠自己的感覺是想去哪就去哪，非常快樂。那麼，老鼠還有什麼「自由意志」可言呢？&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;467&quot; src=&quot;https://blog.amowu.com/_astro/image2.6rGyz9Kb_n2wEr.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image2.6rGyz9Kb_2qo3w.webp 640w, https://blog.amowu.com/_astro/image2.6rGyz9Kb_n2wEr.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;許多科幻作品都在探討「機器人是否存在意識」的問題，例如《&lt;a href=&quot;https://zh.wikipedia.org/zh-tw/%E8%A5%BF%E9%83%A8%E4%B8%96%E7%95%8C&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;西方極樂園&lt;/a&gt;》和《&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E9%93%B6%E7%BF%BC%E6%9D%80%E6%89%8B&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;銀翼殺手&lt;/a&gt;》。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;仿生人會夢見電子羊嗎？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;反過來想，意識也許只是一種精神污染（&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%87%BD%E6%95%B0%E5%89%AF%E4%BD%9C%E7%94%A8&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;函數副作用 Side effect&lt;/a&gt;），人類不屬於純函數（&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E7%BA%AF%E5%87%BD%E6%95%B0&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Pure Function&lt;/a&gt;）。&lt;/p&gt;
&lt;h2 id=&quot;文摘&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#文摘&quot;&gt;文摘&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;工程師的五個等級&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#工程師的五個等級&quot;&gt;工程師的五個等級&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;身為一個喜歡科幻作品的理工男，文學作品的賞析對我來說一直都是罩門。&lt;/p&gt;
&lt;p&gt;本周的《&lt;a href=&quot;https://m.igetget.com/share/course/pay/detail/4/42&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;吳軍的谷歌方法論&lt;/a&gt;》 在介紹莎士比亞的《&lt;a href=&quot;https://zh.wikipedia.org/zh-tw/%E7%AC%AC%E5%8D%81%E4%BA%8C%E5%A4%9C&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;第十二夜&lt;/a&gt;》文中提到，文學家在創作上有三個層次：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;編一個吸引人的故事（托爾金的《魔戒》、瓊瑤的小說）&lt;/li&gt;
&lt;li&gt;反映出一個時代的特點和問題（海明威的《戰地鐘聲》）&lt;/li&gt;
&lt;li&gt;通過對現實和一個時代的描寫，闡述作者自己心中的春秋大義。（雨果的《悲慘世界》、曹雪芹的《紅樓夢》）&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;其中「反映時代特點」剛好與《&lt;a href=&quot;https://m.igetget.com/share/course/pay/detail/4/19&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;吳軍的硅谷來信&lt;/a&gt;》其中一篇《談文物的價值》不謀而合。&lt;/p&gt;
&lt;p&gt;一般來講博物館收藏品的價值看三個：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;文物性&lt;/strong&gt;：重要歷史事件的見證，或者反映一個時期的文明水平，社會文化生活。（羅浮宮的《&lt;a href=&quot;https://zh.wikipedia.org/zh-tw/%E6%B1%89%E8%B0%9F%E6%8B%89%E6%AF%94%E6%B3%95%E5%85%B8&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;漢摩拉比法典&lt;/a&gt;》）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;藝術性&lt;/strong&gt;：不僅僅是以今日眼光評判是否漂亮，還要放回到歷史中看它們是否具有里程碑式的意義。如果同時二者具備就更珍貴。（達文西的《&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E8%92%99%E5%A8%9C%E4%B8%BD%E8%8E%8E&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;蒙娜麗莎&lt;/a&gt;》、米開朗基羅的《&lt;a href=&quot;https://zh.wikipedia.org/zh-tw/%E5%88%9B%E4%B8%96%E7%BA%AA_%28%E5%A3%81%E7%94%BB%29&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;創世紀&lt;/a&gt;》）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;稀缺性&lt;/strong&gt;：一般越古老的越稀缺。有些年代雖然不算太長，但全世界剩不了幾個，也非常珍貴。再有名人們用過的東西，當然也就獨一無二了。（顧愷之的《&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%A5%B3%E5%8F%B2%E7%AE%B4%E5%9B%BE&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;女史箴圖&lt;/a&gt;》）&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;418&quot; src=&quot;https://blog.amowu.com/_astro/image3.H-y-pEoj_Zfa0Wx.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image3.H-y-pEoj_15XIfx.webp 640w, https://blog.amowu.com/_astro/image3.H-y-pEoj_Zfa0Wx.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;354&quot; src=&quot;https://blog.amowu.com/_astro/image4.DOrLALZe_2vbDRp.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image4.DOrLALZe_Z1LRbwN.webp 640w, https://blog.amowu.com/_astro/image4.DOrLALZe_2vbDRp.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;吳軍老師在《工程師和職業發展》一文中，仿照「&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%88%97%E5%A4%AB%C2%B7%E6%9C%97%E9%81%93&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;朗道給物理學家分級&lt;/a&gt;」的方法，也將工程師分成了五個等級。分類的原則大致如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;能獨立解決問題，完成工程工作。&lt;/li&gt;
&lt;li&gt;能指導和帶領其他人一同完成更有影響力的工作。&lt;/li&gt;
&lt;li&gt;能獨立設計和實現產品，並且在市場上獲得成功。&lt;/li&gt;
&lt;li&gt;能設計和實現別人不能做出的產品，也就是說他的作用很難取代。（&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E6%AF%94%E7%88%BE%E8%93%8B%E8%8C%B2&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;比爾蓋茲&lt;/a&gt;、&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E7%B4%84%E7%BF%B0%C2%B7%E5%8D%A1%E9%A6%AC%E5%85%8B&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;約翰卡馬克&lt;/a&gt;）&lt;/li&gt;
&lt;li&gt;開創一個產業。（&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%9B%BE%E7%81%B5&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;圖靈&lt;/a&gt;、&lt;a href=&quot;https://zh.wikipedia.org/zh-tw/%E9%AB%98%E5%BE%B7%E7%BA%B3&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;高德納&lt;/a&gt;）&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;412&quot; src=&quot;https://blog.amowu.com/_astro/image5.BwYucn4w_Z1kzV4B.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image5.BwYucn4w_Z1pp2s0.webp 640w, https://blog.amowu.com/_astro/image5.BwYucn4w_Z1kzV4B.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;本周圖片&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#本周圖片&quot;&gt;本周圖片&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;為什麼人工智慧不可能取代人類&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#為什麼人工智慧不可能取代人類&quot;&gt;為什麼人工智慧不可能取代人類&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Vue.js 的作者尤雨溪最近在《&lt;a href=&quot;https://www.zhihu.com/question/301860721/answer/545031906&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;vue 和 react 優點分別是什麼？&lt;/a&gt;》回覆之中，給出了一段話：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;編程語言之間噴來噴去還少麼？大家都是圖靈完備，然而此之蜜糖，彼之砒霜。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;好奇什麼是「&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%9C%96%E9%9D%88%E5%AE%8C%E5%82%99%E6%80%A7&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;圖靈完備&lt;/a&gt;」，查詢之後得到的簡答如下：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;圖靈完備意味著你的語言可以做到模擬「圖靈機」能做到的所有事情，可以解決所有「可計算問題」。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;圖靈不完備的語言常見原因有迴圈或遞迴受限（無法寫不終止的程式，如 &lt;code&gt;while(true){};&lt;/code&gt;），無法實現類似 Array 或 List 這樣的資料結構。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;這讓我想起《吳軍的硅谷來信》曾經給出下面這張圖，解釋為什麼人工智慧不可能取代人類：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;394&quot; src=&quot;https://blog.amowu.com/_astro/image6.5IbFyrK7_ZoXkXj.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image6.5IbFyrK7_1jIapG.webp 640w, https://blog.amowu.com/_astro/image6.5IbFyrK7_ZoXkXj.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;至於什麼是「不可計算問題」？舉個例子：某個男生特別壞，在別人看來就是個渣男，但是你喜歡的女生偏偏就是特別喜歡他。這個問題就算放到 2049 年，Siri 也還是無法給出一個令你滿意的答案。&lt;/p&gt;
&lt;h2 id=&quot;新奇&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#新奇&quot;&gt;新奇&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;如果全世界的人都只說一種語言該有多好&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#如果全世界的人都只說一種語言該有多好&quot;&gt;如果全世界的人都只說一種語言該有多好&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;YouTuber 啾啾鞋在《&lt;a href=&quot;https://www.youtube.com/watch?v=iyl0zNntnOw&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;世界語簡介&lt;/a&gt;》的影片中，分享了一套叫「&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E4%B8%96%E7%95%8C%E8%AF%AD&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;世界語&lt;/a&gt;」的語言。&lt;/p&gt;
&lt;p&gt;世界語是一套非常有邏輯、沒有例外，容易舉一反三的語言，只要記住基本單字和文法之後，就可以拼湊出想要表達的意思：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一字一音，會念就會拼，會拼就會念。&lt;/li&gt;
&lt;li&gt;沒有複雜的動詞現在式、過去式和未來式規則。&lt;/li&gt;
&lt;li&gt;單字的結構簡單，由「字首、字根、字尾和詞類」組成。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;333&quot; src=&quot;https://blog.amowu.com/_astro/image7.Dp5XQSLF_Z1uMOti.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image7.Dp5XQSLF_Z2jwzVc.webp 640w, https://blog.amowu.com/_astro/image7.Dp5XQSLF_Z1uMOti.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;詳細內容可以到啾啾鞋的影片中進一步暸解，令我印象深刻的是總結的部分，為什麼世界語沒能成為人類的統一語言呢？&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;任何有這種「團結所有人」想法的發明，都會面臨相同的問題。
以「網路」的出現為例，人們原本以為可以透過資訊的流通，讓世界變得無邊界，人類就可以大同。但是網路也更有效率地讓許多志同道合的人形成自己的意見同溫層，然後開始和敵對陣營互相吐口水。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;蘇聯共產黨原本打算透過世界語的力量統一全國人民，結果反而造成自己的內鬥，並且最後被禁止使用。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;如果想要全人類真的團結在一起的話，恐怕只有當外星人出現的時候，人類才能團結在一起。
團結的同時，其實就意味著排外。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;515&quot; height=&quot;597&quot; src=&quot;https://blog.amowu.com/_astro/image8.B6OsV6Pr_lxPdM.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image8.B6OsV6Pr_lxPdM.webp 515w&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;本周金句&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#本周金句&quot;&gt;本周金句&lt;/a&gt;&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;除非違反物理定律，否則幾乎任何事情都是可能的。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;――《&lt;a href=&quot;http://www.ruanyifeng.com/blog/2018/12/elon-musk.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;埃隆·馬斯克和特斯拉汽車的故事&lt;/a&gt;》&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;任何在我出生時已經有的科技，都是稀鬆平常的世界本來秩序的一部分。任何在我 15～35 歲之間誕生的科技，都是將會改變世界的革命性產物。任何在我 35 歲之後誕生的科技，都是違反自然規律，要遭天譴的。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;―― 《&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E9%93%B6%E6%B2%B3%E7%B3%BB%E6%BC%AB%E6%B8%B8%E6%8C%87%E5%8D%97%E7%B3%BB%E5%88%97&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;銀河便車指南&lt;/a&gt;》科技三定律&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;每個人都有一個計劃，直到被一拳打到臉上。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;―― 泰森&lt;/p&gt;
&lt;/blockquote&gt;</content:encoded><category>Weekly</category></item><item><title>我們從哪裡來？我們要往哪裡去？</title><link>https://blog.amowu.com/posts/2019-01-01-weekly-003/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2019-01-01-weekly-003/</guid><description>新聞</description><pubDate>Tue, 01 Jan 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;新聞&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#新聞&quot;&gt;新聞&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;如果你想年底被公司開除可以考慮使用-antdesign&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#如果你想年底被公司開除可以考慮使用-antdesign&quot;&gt;如果你想年底被公司開除，可以考慮使用 Ant Design&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;雖然馬上就要迎接 2019 跨年，街頭仍然瀰漫著濃厚的聖誕節氣氛。&lt;/p&gt;
&lt;p&gt;不同於台灣，中國近來因為「&lt;a href=&quot;https://zh.wikipedia.org/zh-tw/2018%E5%B9%B4%E4%B8%AD%E7%BE%8E%E8%B4%B8%E6%98%93%E4%BA%89%E7%AB%AF&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;中美貿易戰&lt;/a&gt;」的關係，不少地方政府開始強烈「抵制洋節」，除了北京、上海等主要城市，很多地方都禁止公共場所懸掛帶有聖誕節氣氛的裝飾物品或擺放聖誕樹。此外，許多教會所舉辦的平安夜彌撒活動也受到衝擊。&lt;/p&gt;
&lt;p&gt;然而，這周的 IT 圈也不寧靜，牽扯上了一則相關新聞：&lt;/p&gt;
&lt;p&gt;開源 UI 框架 &lt;a href=&quot;https://ant.design/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Ant Design&lt;/a&gt; 在未告知開發者的情況下，自動在前端頁面開啟聖誕節彩蛋，導致前端工程師被公司開除。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;153&quot; src=&quot;https://blog.amowu.com/_astro/image1.DmTN8Tjc_Z1x6FgS.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image1.DmTN8Tjc_Z2pFN3q.webp 640w, https://blog.amowu.com/_astro/image1.DmTN8Tjc_Z1x6FgS.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;144&quot; src=&quot;https://blog.amowu.com/_astro/image2.CgKqCw9x_ZYIGS3.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image2.CgKqCw9x_EIcfA.webp 640w, https://blog.amowu.com/_astro/image2.CgKqCw9x_ZYIGS3.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;令人疑惑的是：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;這個新修改竟然沒有出現在 &lt;a href=&quot;https://github.com/ant-design/ant-design/releases/tag/3.9.3&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Changelog&lt;/a&gt;，搞不清楚是給誰的彩蛋⋯⋯&lt;/li&gt;
&lt;li&gt;彩蛋預設還是開啟的，而不是交由開發者自行決定&lt;/li&gt;
&lt;li&gt;沒有提供關閉選項，只能透過「&lt;a href=&quot;https://github.com/ant-design/ant-design/issues/13098&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;覆寫 CSS&lt;/a&gt;」的方式⋯⋯&lt;/li&gt;
&lt;li&gt;即便是如此大的開源專案，也還是沒有監督到這件事&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;作者之後也出面「&lt;a href=&quot;https://zhuanlan.zhihu.com/p/53214213&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;回應&lt;/a&gt;」了整起事件的來龍去脈：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;關於 Ant Design 聖誕彩蛋，起源自 2018 年 9 月 10 日我的一次提交：&lt;a href=&quot;https://github.com/ant-design/ant-design/commit/00aebeb9756afecc884ad48486084836b9a2707a&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;add christmas easter egg · ant-design/ant-design@00aebeb&lt;/a&gt; ，代碼實現會在 12 月 25 日當天給所有按鈕添加積雪效果，並增加 Ho Ho Ho! 的瀏覽器默認提示信息。這完全是我個人的一意孤行且愚蠢的決定，是我的錯誤給大家造成了不良影響，非常抱歉。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;先撇開被開除的真實性不論，作為一個影響力僅次於 &lt;a href=&quot;https://github.com/twbs/bootstrap&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Bootstrap&lt;/a&gt; 和 &lt;a href=&quot;https://github.com/semantic-org/semantic-ui/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Semantic UI&lt;/a&gt; 的開源 UI 框架（GitHub 40k stars），這玩意兒萬一出現在伊斯蘭國家的政府網站上，後果不堪設想啊⋯⋯。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;500&quot; height=&quot;500&quot; src=&quot;https://blog.amowu.com/_astro/image3.BXv-b4iu_ZxkNJf.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image3.BXv-b4iu_ZxkNJf.webp 500w&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;文摘&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#文摘&quot;&gt;文摘&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;我們從哪裡來我們要往哪裡去&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#我們從哪裡來我們要往哪裡去&quot;&gt;我們從哪裡來？我們要往哪裡去？&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;今年的《&lt;a href=&quot;https://book.douban.com/annual/2018&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;豆瓣 2018 年度讀書榜單&lt;/a&gt;》之中，我唯一讀過的作品只有《&lt;a href=&quot;https://book.douban.com/subject/30295288/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;21世紀的21堂課&lt;/a&gt;》。&lt;/p&gt;
&lt;p&gt;哈拉瑞的《人類大歷史》三部曲，《&lt;a href=&quot;https://book.douban.com/subject/25952974/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;人類大歷史&lt;/a&gt;》說的是人類社會如何形成，《&lt;a href=&quot;https://book.douban.com/subject/26945954/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;人類大未來&lt;/a&gt;》說的是未來社會如何演化，一本是回顧過去，一本是展望未來，而這本《&lt;a href=&quot;https://book.douban.com/subject/30295288/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;21世紀的21堂課&lt;/a&gt;》考察的是當下，說的是生活在今天的你和我正在經歷、或者將要面臨的問題。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;263&quot; src=&quot;https://blog.amowu.com/_astro/image4.CyDLm5mB_Z22jOwm.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image4.CyDLm5mB_ZkJBKx.webp 640w, https://blog.amowu.com/_astro/image4.CyDLm5mB_Z22jOwm.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;引述自《&lt;a href=&quot;https://book.douban.com/subject/30180290/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;起源&lt;/a&gt;》的一段話：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;假如你有一台功能強大的電腦，無所不知，無所不曉，你想問什麼問題都可以。那麼，十有八九你最終會問到人類的兩個最基本的問題。這兩個問題自人類自我意識覺醒以來，便一直縈繞在他們的心頭。
我們從哪裡來？我們要往哪裡去？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;如果說人生的終極問題是：人類的起源和歸宿，那麼《人類大歷史》三部曲給出的答案就是：猿猴、智人（&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E6%99%BA%E4%BA%BA&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Homo sapiens&lt;/a&gt;）和神人（&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E7%A5%9E&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Homo deus&lt;/a&gt;）。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;157&quot; src=&quot;https://blog.amowu.com/_astro/image5.BZgqfd79_2bFWax.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image5.BZgqfd79_1j6Oo0.webp 640w, https://blog.amowu.com/_astro/image5.BZgqfd79_2bFWax.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;最不符合進化論的就是人類本身。―― 達爾文&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;生物靠「基因突變」來進化太漫長了（從猿猴到智人 200 萬年），智人到現在也就 7 萬年，生活方式就有了天翻地覆的變化，這是因為「認知革命」之後，智人有了「語言」能力，培養出「說故事」這個逆天的技能，憑藉著三大故事「金錢、國家和宗教」，我們的祖先開始可以共同相信一件事物，讓這個物種的進化不再是依賴基因突變，而是依賴「分工合作」，組建起複雜的社會，發展出高度的文明。&lt;/p&gt;
&lt;p&gt;「農業革命」之後，人類從「狩獵採集文明」過渡至「農耕文明」，雖然身體素質下降，但是族群基因的傳播效率卻大大提升，最初的政治意識形態：「帝國」的概念也開始隨之盛行。&lt;/p&gt;
&lt;p&gt;「科學革命」最大的進步就是人類「願意承認自己的無知」，科學因為證明了上帝不靠譜，給了以人為本的「人文主義」發展的機會，加上各國開始走向工業化，不論是工人、士兵或是婦女，人力成為非常寶貴的資源，因此意識形態開始由帝國主義轉向人文主義，其中最具代表的分別是：「進化人文主義（法西斯）」、「自由人文主義」和「社會人文主義（共產）」。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;300&quot; height=&quot;300&quot; src=&quot;https://blog.amowu.com/_astro/image6.R-Xy0695_19TYar.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image6.R-Xy0695_19TYar.webp 300w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;歷經第二次世界大戰的納粹德國戰敗、冷戰的共產蘇聯瓦解之後，代表「自由主義」的美國勝出，於是「自由」與「平等」的概念被視為人權，並且奉若神明，成為當今 21 世紀的主流思想一直延續到現在。&lt;/p&gt;
&lt;p&gt;然而，未來很有可能因為「資訊科技革命」和「生物技術革命」，顛覆自由主義建立起來的兩大價值觀。&lt;/p&gt;
&lt;p&gt;首先是「自由」，世間一切學科，不管是文學、科學、音樂還是經濟學，背後都是數學模式。每個人都是一個處理器，人與人之間的交流就是一套資訊處理系統，整個人類歷史，就是給這個系統增加效率的歷史。如果一切問題都是演算法問題，那麼我們只要建立一個連接所有資訊的「萬物互聯」網路，到那個時候，人工智能＋大數據＋演算法就比你更了解你自己，為了你我的利益，大家都應該讓算法替我們做決定，與其信奉個人、信奉神，還不如信奉這個網絡。個人「自由」徹底崩解，一個新的社會人文主義將會誕生：集中式資料處理的數位獨裁主義。&lt;/p&gt;
&lt;p&gt;為了讓我們活得更健康，幫助我們做出更好的決定，完善這個萬物互聯網成為最值得幹的工作。終有一天，萬物互聯網會發展到人類無法理解的地步。演算法之間互相配合升級，演算法自己產生新的演算法，萬物互聯網將獨立於人類而存在，到時候，萬物互聯網就成了世界上第一個重要的東西，比人更重要。所有這些為萬物互聯網工作的職業就成了一份神聖的工作。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;603&quot; src=&quot;https://blog.amowu.com/_astro/image7.IAJqaHgz_2fohFT.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image7.IAJqaHgz_2d5IQp.webp 640w, https://blog.amowu.com/_astro/image7.IAJqaHgz_2fohFT.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;600&quot; height=&quot;321&quot; src=&quot;https://blog.amowu.com/_astro/image8.DPrFC4LS_Z1Ez6nJ.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image8.DPrFC4LS_Z1Ez6nJ.webp 600w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;然後是「平等」，過去的剝削者與被剝削者只是在地位、權力和財富上的不平等，生理上並無不同。但是「生物技術革命」之後，富裕階層已經有能力透過「基因編輯」之類的改造技術，在體能和智能上優於其他人，甚至達到永生，成為「神」這種動物，那麼社會將會大變，自由主義提出「人類生而平等」的口號，將不再成立。&lt;/p&gt;
&lt;p&gt;未來，假設上述兩個趨勢同時進行。那麼全體人類很有可能會被劃分成兩種人：無用的人和神人，這兩種人不可能是平等的，一個新的進化人文主義將會誕生：如同《&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E7%BE%8E%E9%BA%97%E6%96%B0%E4%B8%96%E7%95%8C&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;美麗新世界&lt;/a&gt;》故事中的「新種姓制度」。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;320&quot; src=&quot;https://blog.amowu.com/_astro/image9.C7yqsRdN_ZbgHAN.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image9.C7yqsRdN_Z2csCuB.webp 640w, https://blog.amowu.com/_astro/image9.C7yqsRdN_ZbgHAN.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;951&quot; src=&quot;https://blog.amowu.com/_astro/image10.BsmQbsAt_Z1tPrdB.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image10.BsmQbsAt_10hpxb.webp 640w, https://blog.amowu.com/_astro/image10.BsmQbsAt_Z1tPrdB.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;過去，人類征服了地球，只用了最後的幾十年時間，就解決了困擾千年以來的三大問題：飢荒、瘟疫和戰爭。&lt;/p&gt;
&lt;p&gt;未來，人類打算追求永生，變成神這種動物，但是我們有因此過得比狩獵採集文明的智人還幸福快樂嗎？我們要往哪裡去？&lt;/p&gt;
&lt;p&gt;後記：第一次聽說《人類大歷史》是在比爾蓋茲和馬克祖克柏的推薦書單，或許《人類簡史》、《未來簡史》和《今日簡史》的譯名較廣為人知。無神論加上打擊自由主義的觀點，也難怪這本書在中國會受到如此追捧的程度，甚至在收官之作《今日簡史》一書中，每每提到 Amazon、Google 和 Facebook，就要順便帶入阿里巴巴、百度和騰訊。&lt;/p&gt;
&lt;p&gt;就像美國的好萊塢電影一樣，第三方國家的出現率從過去的俄羅斯、日本，到現在幾乎所有大片都有中國的影子，哈拉瑞不免俗還是要屈就自由主義的自由市場經濟。&lt;/p&gt;
&lt;h2 id=&quot;本周金句&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#本周金句&quot;&gt;本周金句&lt;/a&gt;&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;與其受人喜愛，讓人害怕才更安全。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;――《&lt;a href=&quot;https://movie.douban.com/annual/2018&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;利器&lt;/a&gt;》&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;智者將建立橋梁，而愚者則建立高牆。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;――《&lt;a href=&quot;https://movie.douban.com/annual/2018&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;黑豹&lt;/a&gt;》&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;如果連出門看星星都做不到，那麼教她們追逐星辰還有什麼意義？――《&lt;a href=&quot;https://movie.douban.com/annual/2018&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;她說：女性人生瞬間&lt;/a&gt;》&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;能說會道的人很多，但能夠從無到有上線一個產品的人不多。執行力很重要。你很容易對幾百人、做了好幾年的產品挑出一堆毛病，但你卻無法協調幾個人在兩三個月內上線一個不完美的產品。誇誇其談的人太多。很多人用「完美主義」來掩飾自己的無能。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;――《&lt;a href=&quot;https://wanqu.co/a/7201/%E5%A6%82%E4%BD%95%E6%8B%9B%E4%B8%80%E4%B8%AA%E5%A5%BD%E7%9A%84%E4%BA%A7%E5%93%81%E7%BB%8F%E7%90%86/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;如何招一個好的產品經理&lt;/a&gt;》&lt;/p&gt;
&lt;/blockquote&gt;</content:encoded><category>Weekly</category></item><item><title>陽光、大地與詩歌</title><link>https://blog.amowu.com/posts/2018-12-24-weekly-002/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2018-12-24-weekly-002/</guid><description>新聞</description><pubDate>Mon, 24 Dec 2018 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;新聞&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#新聞&quot;&gt;新聞&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;美國總統川普於 18 日正式下令成立「太空司令部」，這將成為美軍在陸軍、海軍、陸戰隊和空軍之外的另一支全新軍種。&lt;/p&gt;
&lt;p&gt;回顧美國一路走到今天世界霸權的地位，「&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E6%98%AD%E6%98%AD%E5%A4%A9%E5%91%BD&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;昭昭天命&lt;/a&gt;」這個美式擴張主義的「天命論」起了很大的作用。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;昭昭天命是美國對西向擴充運動的一種辯解或理由；又或者是一種促發其進程的意識形態或學說。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;從建國開始前的迫害印地安人、吞併德克薩斯共和國，一直到介入北美洲以外事務，美國人認定國家的擴張是神的安排，拓展疆域是美國對這個世界的使命。&lt;/p&gt;
&lt;p&gt;太空司令部成立之後，美國下一步打算將野心延伸向地球圈外。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;521&quot; src=&quot;https://blog.amowu.com/_astro/image1.CQRZ4YZU_1JxrYN.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image1.CQRZ4YZU_Z22ww0H.webp 640w, https://blog.amowu.com/_astro/image1.CQRZ4YZU_1JxrYN.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;作為緊追在後的第二強權，中國今年恰逢改革開放 40 週年，同樣也有一套屬於自己的天命論，那就是「&lt;a href=&quot;https://zh.wikipedia.org/zh-tw/%E5%A4%A9%E4%B8%8B&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;天下觀&lt;/a&gt;」。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;640&quot; height=&quot;419&quot; src=&quot;https://blog.amowu.com/_astro/image2.Cs87EV2i_ZDFclV.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image2.Cs87EV2i_ZDFclV.webp 640w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;以清代《&lt;a href=&quot;https://zh.wikipedia.org/zh-tw/%E5%BA%B7%E7%86%99%E7%9A%87%E8%BC%BF%E5%85%A8%E8%A6%BD%E5%9C%96&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;康熙皇輿全覽圖&lt;/a&gt;》為例，台灣、外蒙古、外東北與庫頁島、北朝鮮與南韓，都是中國神聖領土不可分割的一部份。&lt;/p&gt;
&lt;p&gt;最近「來自中國台灣」事件鬧得沸沸揚揚，類似議題總是在兩岸爭論不休。&lt;/p&gt;
&lt;p&gt;反倒是《&lt;a href=&quot;https://zh.wikipedia.org/zh-tw/%E7%92%A6%E7%90%BF%E6%A2%9D%E7%B4%84&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;璦琿條約&lt;/a&gt;》沙俄割走了東北 60 萬平方公里的土地，大約相當於 15 個台灣的大小，還有「&lt;a href=&quot;https://zh.wikipedia.org/zh-tw/%E5%A4%96%E8%92%99%E5%8F%A4%E7%8B%AC%E7%AB%8B&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;外蒙古獨立&lt;/a&gt;」等等⋯⋯，怎麼小粉紅們卻興致缺缺，連屁都不敢吭一聲呢？&lt;/p&gt;
&lt;p&gt;理由很簡單，因為台灣的利益大、拳頭弱、好欺負，就只是這樣而已。&lt;/p&gt;
&lt;p&gt;身為小國，應該會有適合小國自己聰明的玩法，例如前陣子九合一選舉，聽到了一個有趣的想法：「中央政府選親美、親日的黨派，地方政府則選親中的候選人」。&lt;/p&gt;
&lt;p&gt;一來可以在主權的高度上 hold 住，避免被「封鎖非洲豬瘟消息」的極權黨國給併吞，二來「東西賣得出去，人進得來，地方發大財」，降低成為美國魁儡的風險。&lt;/p&gt;
&lt;p&gt;最後，回歸本質談起，「教育興則國家興，教育強則國家強」，本周 &lt;a href=&quot;https://hahow.in/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Hahow 好學校&lt;/a&gt; 發佈了新版本的 App，並且可以參加 &lt;a href=&quot;https://www.facebook.com/hahow.in/videos/vb.1565253993719446/360615201391809/?type=3&amp;amp;theater&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Samsung Galaxy Note9 抽選&lt;/a&gt;，大家一起來「學那些學校不會教的事」吧！&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://events.hahow.in/hahowapp-21892&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Hahow app 登陸頁&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;資源&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#資源&quot;&gt;資源&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;coloring-china-色染中国&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#coloring-china-色染中国&quot;&gt;Coloring China ! | 色染中国&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://se.joway.io/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://se.joway.io/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;色票網站，取材自山寨「&lt;a href=&quot;http://nipponcolors.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;日本の伝統色&lt;/a&gt;」的「&lt;a href=&quot;http://zhongguose.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;中国传统颜色&lt;/a&gt;」。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;227&quot; src=&quot;https://blog.amowu.com/_astro/image3.CvZA926X_1nM66W.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image3.CvZA926X_Z205YSj.webp 640w, https://blog.amowu.com/_astro/image3.CvZA926X_1nM66W.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;剛好兩年前在書店翻到了《&lt;a href=&quot;https://www.linkingbooks.com.tw/lnb/book/Book.aspx?ID=190033&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;台灣顏色&lt;/a&gt;》這本書，就順手開源了一個「台灣顏色 Taiwan Colors」網站。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://taiwan-colors.surge.sh/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;http://taiwan-colors.surge.sh&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;313&quot; src=&quot;https://blog.amowu.com/_astro/image4.OIyo2iEO_10Do9Y.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image4.OIyo2iEO_ZUo1Px.webp 640w, https://blog.amowu.com/_astro/image4.OIyo2iEO_10Do9Y.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;對台灣傳統顏色有興趣的朋友，歡迎在 GitHub 留下您寶貴的 issue 或 pull request。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/amowu/taiwan-colors&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://github.com/amowu/taiwan-colors&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;工具&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#工具&quot;&gt;工具&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;removebg&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#removebg&quot;&gt;remove.bg&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://www.remove.bg/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://www.remove.bg&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;這周 remove.bg 席捲了各大 RSS 和 Weekly Mail，號稱可以快速無腦地幫圖片去背。&lt;/p&gt;
&lt;p&gt;好奇之下，找了一張老婆的照片來測試，沒想到效果還真不錯，連髮絲的部分都有處理到：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;319&quot; src=&quot;https://blog.amowu.com/_astro/image5.DSyAZ1w0_1vzV3n.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image5.DSyAZ1w0_1nrMfD.webp 640w, https://blog.amowu.com/_astro/image5.DSyAZ1w0_1vzV3n.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;玩上癮了⋯⋯再測試一張：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;541&quot; src=&quot;https://blog.amowu.com/_astro/image6.C4GAMa5d_Zf1IzT.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image6.C4GAMa5d_ZhIwl7.webp 640w, https://blog.amowu.com/_astro/image6.C4GAMa5d_Zf1IzT.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;喂！怎麼把下半身給截掉了啦！&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;299&quot; src=&quot;https://blog.amowu.com/_astro/image7.BwECEeRQ_Z4p1WB.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image7.BwECEeRQ_ZTIL8o.webp 640w, https://blog.amowu.com/_astro/image7.BwECEeRQ_Z4p1WB.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;嗯⋯⋯是沒什麼問題啦，不過能順便把那傢伙給一起去掉嗎？&lt;/p&gt;
&lt;h2 id=&quot;本周圖片&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#本周圖片&quot;&gt;本周圖片&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Behance 大神 &lt;a href=&quot;https://www.behance.net/milothemes&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Milo&lt;/a&gt; 整理了一篇《&lt;a href=&quot;https://www.behance.net/gallery/71481981/2019-Design-Trends-Guide&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;2019 年設計趨勢指南&lt;/a&gt;》，針對顏色、插畫、動畫特效、字型和排版等方面，給出了自己的預測分析和建議。&lt;/p&gt;
&lt;p&gt;雖然我是看不出相比 2018 年有哪些區別，但是果然高手在民間，討論區出現一則有趣的留言：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;UI 設計演化史，古典主義（早期的功能 UI 設計）&amp;gt; 裝飾主義（擬物寫實風格）&amp;gt; 現代主義（扁平化時代）&amp;gt; 後現代主義（扁平化基礎之上衍生的一些細節裝飾和 3D 動畫表現）。和整個藝術設計史如出一轍。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;於是我 Google 了一張與「&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E8%A5%BF%E6%96%B9%E7%BE%8E%E6%9C%AF%E5%8F%B2&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;西方美術史&lt;/a&gt;」時間軸相關的圖片：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;1296&quot; height=&quot;792&quot; src=&quot;https://blog.amowu.com/_astro/image8.BRMKPXWk_Zotj2U.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image8.BRMKPXWk_CISs5.webp 640w, https://blog.amowu.com/_astro/image8.BRMKPXWk_Z1gguui.webp 750w, https://blog.amowu.com/_astro/image8.BRMKPXWk_Z1VRhaN.webp 828w, https://blog.amowu.com/_astro/image8.BRMKPXWk_14Mb9a.webp 1080w, https://blog.amowu.com/_astro/image8.BRMKPXWk_Z18NydR.webp 1280w, https://blog.amowu.com/_astro/image8.BRMKPXWk_Zotj2U.webp 1296w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;然後對比了歷代 Facebook iOS app 的設計：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;910&quot; src=&quot;https://blog.amowu.com/_astro/image9.BCimvMxX_bdt8q.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image9.BCimvMxX_Z2ihvD0.webp 640w, https://blog.amowu.com/_astro/image9.BCimvMxX_bdt8q.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;竟然！⋯⋯好吧，或許是我藝術造詣不夠，感受不太出來，你們覺得如何呢？&lt;/p&gt;
&lt;h2 id=&quot;本周金句&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#本周金句&quot;&gt;本周金句&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;誰控制過去就控制未來，誰控制現在就控制過去。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;所謂自由就是可以說二加二等於四的自由。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;――《一九八四》&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;普通人認為嘗試新體驗意味著冒險。在企業家們看來，什麼都不做才是冒險，因為這意味著錯失機遇。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;人們總是太高估自己能在一年裡做到什麼，而又總是太低估自己能在十年裡做到什麼。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;――《富人的邏輯》&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;什麼叫老闆？
老闆必須要成為企業的領袖。
那什麼是領袖？
領袖就是知道要「做什麼」，但是不知道怎麼做。
所以去影響一大堆「知道怎麼做」，
但是「不知道做什麼」的人來幫他做。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;―― 馬雲&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;我上班就是為了賺錢，別跟我談什麼理想，我的理想就是不用上班賺錢。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;煩惱就是慾望比口袋多一分錢；
幸福是口袋裡比慾望多一分錢。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;――《吳軍的谷歌方法論》&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;來舉杯暢飲吧，為了所謂人這種生物。
善人也好惡人也罷，無論世間如何變化，皆輪迴也。
困於足以讓人腐化，卻不足以讓人學習的時間渦流裡，
人才會充滿慾望，才會挑起事端吧？
生命，明明是只需要陽光、大地與詩歌就能滿足的東西。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;――《所謂人》&lt;/p&gt;
&lt;/blockquote&gt;</content:encoded><category>Weekly</category></item><item><title>透過寫作永生於人間</title><link>https://blog.amowu.com/posts/2018-12-17-weekly-001/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2018-12-17-weekly-001/</guid><description>前言</description><pubDate>Mon, 17 Dec 2018 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;前言&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#前言&quot;&gt;前言&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;轉眼 2018 就要結束了，每年這個時候，總是充滿了焦慮的心情，感覺好像還有很多目標沒有完成。&lt;/p&gt;
&lt;p&gt;其中之一就是文章的產出極低。&lt;/p&gt;
&lt;p&gt;所以想趁著這個機會，效仿阮一峰的《&lt;a href=&quot;http://www.ruanyifeng.com/blog/clipboard/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;每周分享&lt;/a&gt;》系列，總結一下每周看到的知識，並將之轉化成值得分享的心得文章。&lt;/p&gt;
&lt;h2 id=&quot;新聞&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#新聞&quot;&gt;新聞&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;平成最後的漢字&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#平成最後的漢字&quot;&gt;平成最後的漢字&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;日本天皇明年就要退位了，所以今年可以看到很多掛上「平成最後的〇〇」的梗。&lt;/p&gt;
&lt;p&gt;京都的清水寺每年都會總結出一個代表日本的漢字，今年選出的是「災」這個字。&lt;/p&gt;
&lt;p&gt;豪雪、豪雨、酷暑、強颱、地震再加上瘟疫，日本今年經歷了一系列的自然災害。&lt;/p&gt;
&lt;p&gt;巧的是，我今年十月曾經眼皮跳了將近一個月左右，接著身邊就陸續發生了一些不幸的憾事。&lt;/p&gt;
&lt;p&gt;民間流傳著一句「左眼跳財、右眼跳災」。最近剛三刷完《&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E6%98%9F%E9%99%85%E7%A9%BF%E8%B6%8A&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;星際效應&lt;/a&gt;》之後，突然覺得好像可以解釋這種現象，該不會是有老祖先正在諾蘭的五維空間之中撥動弦，想要傳達些什麼訊息給我？&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;394&quot; src=&quot;https://blog.amowu.com/_astro/image1.DXe_keiu_ZaIkJU.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image1.DXe_keiu_ZbtGnM.webp 640w, https://blog.amowu.com/_astro/image1.DXe_keiu_ZaIkJU.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;不過現在回想起來，我眼皮是跳在左眼⋯⋯，所以，財呢？&lt;/p&gt;
&lt;h2 id=&quot;文摘&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#文摘&quot;&gt;文摘&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;我們將毀於我們所熱愛的東西&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#我們將毀於我們所熱愛的東西&quot;&gt;我們將毀於我們所熱愛的東西&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;本周，我聽了一本經典的反烏托邦科幻小說，《&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E7%BE%8E%E9%BA%97%E6%96%B0%E4%B8%96%E7%95%8C&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;美麗新世界&lt;/a&gt;》。&lt;/p&gt;
&lt;p&gt;聽完之後，我對社會主義有了重新的思考。&lt;/p&gt;
&lt;p&gt;故事中的未來社會，人類皆透過人工授精、體外培養的方式來繁殖，並且制定了新的種姓制度：α、β、γ、δ 和 ε。不同於舊種姓制度只是在階級上的不平等，新種姓制度下的新生兒連智力和體力都有高低之分，徹底重新定義了不平等的下限。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;412&quot; height=&quot;265&quot; src=&quot;https://blog.amowu.com/_astro/image2.9GDxTfsu_274x2K.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image2.9GDxTfsu_274x2K.webp 412w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;然而，卻不會有一群人壓迫另一群人的情況發生，各個階層的人都被培養成非常適合、也非常熱愛自己的工作。彼此間也不會感到「不幸福」，每個人都滿足於馬斯洛提出的 &lt;a href=&quot;https://zh.wikipedia.org/wiki/%E9%9C%80%E6%B1%82%E5%B1%82%E6%AC%A1%E7%90%86%E8%AE%BA&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;需求金字塔&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;613&quot; src=&quot;https://blog.amowu.com/_astro/image3.BxwuNf0N_ZdgHNQ.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image3.BxwuNf0N_2hzPfp.webp 640w, https://blog.amowu.com/_astro/image3.BxwuNf0N_ZdgHNQ.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;如何做到的呢？&lt;/p&gt;
&lt;p&gt;簡單來說，就是透過修改先天的基因＆後天的洗腦教育。&lt;/p&gt;
&lt;p&gt;例如：最低種姓 ε，讓他們一摸到鮮花就觸電，形成一種「想到大自然植物就噁心」的恐懼反射，避免他們在野外娛樂，浪費時間和社會資源。再比如 β 種姓只是技術人員，通過前期教育降低夢想，只做好手上的技術活，不夢想開創什麼學術新領域。反之在 α 種姓教育中則被鼓勵。&lt;/p&gt;
&lt;p&gt;為了追求社會的高效，每個階級各自負責如汽車製造流水線般的精細分工，各就其位，整個社會非常和諧穩定。所有人都成為社會這台大機器之中「獨立卻又需要彼此、不可分割」的螺絲釘。實現了終極的社會主義：人為社會服務，而不是社會為人服務。&lt;/p&gt;
&lt;p&gt;故事中「犧牲了人性複雜的多樣性與獨立個性」換來的幸福並不可怕，可怕的是，有一天，這個世界會用廉價的娛樂讓我們變成社會的零件，而不是社會的主人。&lt;/p&gt;
&lt;p&gt;我們以前活在《&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E4%B8%80%E4%B9%9D%E5%85%AB%E5%9B%9B&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;一九八四&lt;/a&gt;》，未來可能會活在《&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E7%BE%8E%E9%BA%97%E6%96%B0%E4%B8%96%E7%95%8C&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;美麗新世界&lt;/a&gt;》。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;903&quot; src=&quot;https://blog.amowu.com/_astro/image4.QtmzDpx4_Z253Raj.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image4.QtmzDpx4_vyLoP.webp 640w, https://blog.amowu.com/_astro/image4.QtmzDpx4_Z253Raj.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;(登月第二人，伯茲·艾德林：「你們承諾會殖民火星，卻給了我 Facebook」)&lt;/p&gt;
&lt;p&gt;最近「CRISPR 基因編輯嬰兒」的新聞鬧得沸沸揚揚，或許這個未來也不遠了⋯⋯。&lt;/p&gt;
&lt;h2 id=&quot;工具&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#工具&quot;&gt;工具&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;logpoints&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#logpoints&quot;&gt;Logpoints&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Chrome Debugger 實驗室最近推出了一個名叫 logpoints 功能，對於不喜歡使用中斷點（breakpoints）的 &lt;code&gt;console.log&lt;/code&gt; 派來說，應該是個好消息。&lt;/p&gt;
&lt;h2 id=&quot;本周圖片&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#本周圖片&quot;&gt;本周圖片&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;大腦就是一個三磅重的宇宙&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#大腦就是一個三磅重的宇宙&quot;&gt;大腦就是一個三磅重的宇宙&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;《紐約時報》曾經刊登過這兩張有意思的照片，左邊是小白鼠的大腦神經元網路，右邊則是宇宙的星系，兩張照片放在一起看，竟然驚人地相似。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;337&quot; src=&quot;https://blog.amowu.com/_astro/image5.7aX91P1Y_142sic.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image5.7aX91P1Y_23oqd4.webp 640w, https://blog.amowu.com/_astro/image5.7aX91P1Y_142sic.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;最近聽了《得到》專欄《前沿科技之腦機接口》，裡面提到做腦機介面，最想要實現的就是看誰能採集更多、更精准的「腦電波」資料，然後嘗試理解這些訊號所對應的大腦指令。&lt;/p&gt;
&lt;p&gt;那怎麼採集呢？科學家分成兩派，「侵入式」的腦機介面與「非侵式」腦機介面。&lt;/p&gt;
&lt;p&gt;這兩種探測大腦的方式，其實跟科學家探索宇宙的方法如出一轍。&lt;/p&gt;
&lt;p&gt;「侵入式」的腦機介面，像《駭客任務》一樣，把電極放到大腦裡去，就像宇宙的觀測者，如果能發射衛星、飛船進入太空，登陸月球、火星，那麼就能帶回珍貴的資料。這就是侵入式腦機介面的邏輯。&lt;/p&gt;
&lt;p&gt;不同於侵入式腦機介面需要動開顱手術，風險很高，非侵入的風險就低很多，其實就是醫院裡面你會見到的腦電圖。做腦電圖的時候，你會戴上一個佈滿電極的帽子，分析你的腦電波。這就相當於科學家在地球上，對星系進行遠距離觀測。&lt;/p&gt;
&lt;p&gt;如果腦機介面技術被順利發展起來，那麼將會徹底顛覆人類的文明，它將會取代五萬年來我們賴以為生的協作工具：語言。直接建立一個能讓大腦和外界溝通的全新方式。估計下一間一萬億美元的公司就會誕生於此吧。&lt;/p&gt;
&lt;h2 id=&quot;新奇&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#新奇&quot;&gt;新奇&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;神預測-2020東京奧運&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#神預測-2020東京奧運&quot;&gt;神預測 2020 東京奧運&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;被奉為經典漫畫的科幻神作《阿基拉》，最近在台灣推出了再版。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;933&quot; src=&quot;https://blog.amowu.com/_astro/image6.DtJO8-VX_1Njj39.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image6.DtJO8-VX_Z1gUaI5.webp 640w, https://blog.amowu.com/_astro/image6.DtJO8-VX_1Njj39.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;(聽說絕版曾經喊到天價，如今再版，質感爆棚)&lt;/p&gt;
&lt;p&gt;距今 34 年前的漫畫（1984），全手繪的細膩筆觸，即便是放在現在來看，也有過之而無不及。&lt;/p&gt;
&lt;p&gt;作者大友克洋將故事設定在 2019 年的新東京，並且在劇情中提到了 2020 年東京將舉辦奧運，簡直神預測！這麼好的梗，動漫立國的日本政府絕對能拿來操作啊。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;525&quot; src=&quot;https://blog.amowu.com/_astro/image7.CBWT2vPw_FBwP3.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image7.CBWT2vPw_16euxD.webp 640w, https://blog.amowu.com/_astro/image7.CBWT2vPw_FBwP3.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;(雖然不是現在的東京，而是第三次世界大戰後的「新東京」)&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;933&quot; src=&quot;https://blog.amowu.com/_astro/image8.Bui8aK1d_2vr4Y7.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image8.Bui8aK1d_ZyMoM7.webp 640w, https://blog.amowu.com/_astro/image8.Bui8aK1d_2vr4Y7.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;(慶幸的是，第三次世界大戰沒有真的發生)&lt;/p&gt;
&lt;h3 id=&quot;如果你想毀掉一個人但又想讓他覺得你是全世界對他最好的那麼送他薩爾達傳說曠野之息吧&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#如果你想毀掉一個人但又想讓他覺得你是全世界對他最好的那麼送他薩爾達傳說曠野之息吧&quot;&gt;如果你想毀掉一個人，但又想讓他覺得你是全世界對他最好的，那麼送他《薩爾達傳說：曠野之息》吧&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;由於本身待過遊戲業的關係，對於日本的遊戲設計與遊戲開發一直都有在關注。最近看到一個有趣的新知。&lt;/p&gt;
&lt;p&gt;正常來說一間遊戲公司有了別人做不到的「好玩」的創舉之後，肯定都是建立商業機密檔案，制定情報管理機制，確保這個製作秘密不會外流。&lt;/p&gt;
&lt;p&gt;例如 &lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%8D%A1%E6%99%AE%E7%A9%BA&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;CAPCOM&lt;/a&gt; 這家公司，任何遊戲專案，光是想要調用「如何做好打擊感」這方面的知識，就要特別動用公司裡一個專門調製打擊感的小組，而且那個小組是受到嚴格監控的，並且每個人所掌握的知識也只是其中的一小部分。&lt;/p&gt;
&lt;p&gt;作為 &lt;a href=&quot;https://zh.wikipedia.org/wiki/%E6%B8%B8%E6%88%8F%E5%A4%A7%E5%A5%96&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;TGA&lt;/a&gt; 2017 年度最佳遊戲的《&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%A1%9E%E5%B0%94%E8%BE%BE%E4%BC%A0%E8%AF%B4_%E6%97%B7%E9%87%8E%E4%B9%8B%E6%81%AF&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;薩爾達傳說：曠野之息&lt;/a&gt;》，任天堂的開發團隊竟然自己在 2017 日本開發者大會上分享了讓 &lt;a href=&quot;https://zh.wikipedia.org/wiki/%E9%96%8B%E6%94%BE%E4%B8%96%E7%95%8C&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;開放世界&lt;/a&gt; 好玩的 3 個隱藏設計要素：引導點的巧妙安排、地圖場景的三角法則和對應真實世界的距離感、密度感和時間感。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;392&quot; src=&quot;https://blog.amowu.com/_astro/image9.C0PzW6WP_1vywuI.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image9.C0PzW6WP_IxOJh.webp 640w, https://blog.amowu.com/_astro/image9.C0PzW6WP_1vywuI.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;(高塔就是吸引力很強的引導點，玩家看到它整個精神狀態就會開始不對勁，遊戲中巧妙地安排這些引導點，確保玩家在到達一個引導點之後，一定能看到其它引導點，從而下意識又有了一個潛在的目標)&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;383&quot; src=&quot;https://blog.amowu.com/_astro/image10.BNXU2qJG_yFY4L.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image10.BNXU2qJG_Z2ccgTG.webp 640w, https://blog.amowu.com/_astro/image10.BNXU2qJG_yFY4L.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;(玩家也不是笨蛋，引導點會讓玩家有一種被設計好、而不是自由探索的《美麗新世界》既視感，所以《曠野之息》將大多數場景物件的輪廓都設計得接近三角形，把這些物件擺在兩個引導點的中間來影響玩家的視野和路線)&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;352&quot; src=&quot;https://blog.amowu.com/_astro/image11.DkbUsb77_18IGQo.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image11.DkbUsb77_NLHQI.webp 640w, https://blog.amowu.com/_astro/image11.DkbUsb77_18IGQo.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;(透過京都的便利商店密度來掌握遊戲中該設置多少神廟數量的感覺)&lt;/p&gt;
&lt;p&gt;更多細節有興趣的可以觀看下面的 YouTube 影片介紹，看完之後會不禁讚嘆遊戲設計中也充滿著「&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E8%A1%8C%E7%82%BA%E7%B6%93%E6%BF%9F%E5%AD%B8&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;行為經濟學&lt;/a&gt;」啊。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://youtu.be/T35UQSWSM94?si=G_-Ibb1Fp4BEgTSy&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://youtu.be/T35UQSWSM94?si=G_-Ibb1Fp4BEgTSy&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;本周金句&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#本周金句&quot;&gt;本周金句&lt;/a&gt;&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;說唐僧成佛了，我們理解，一個有使命的偉大領導者；
孫悟空成佛了，我們也能理解，業務高手、降妖除魔，忠心耿耿；
沙和尚成佛了，我們也能理解，一路髒活苦活累活，沒有功勞也有苦勞；
你說豬八戒成佛了，我們不理解，好吃懶惰又好色，唐僧一被抓，「猴哥我們散伙吧」；
就這種員工，他最終還成佛了，為什麼？只因為一件事，跟對了團隊。
―― &lt;a href=&quot;http://vt.tiktok.com/vrEdT/%20&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;名人名言&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;如果家長能想清楚，關鍵是贏在終點線上，而不是起跑線上，就沒有必要逼孩子從小搞得那麼累了。
――《維繫朋友關係之道》吳軍的谷歌方法論&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;今天，世界上絕大部分私立中小學，都要求學生們穿校服，這不僅僅為了整齊好看，也是為了讓不同家庭背景的孩子們感到平等。
――《熬夜排隊買蘋果手機和中國孩子的早教》吳軍的谷歌方法論&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;過去許多軟體開發商設計很多數位鎖，想要防止被盜版，但歷史證明，都沒有用。開發數位鎖浪費了軟體開發商的大筆時間和金錢，但最後鎖住的，卻往往是有良心乖乖買正版的使用者。
――《&lt;a href=&quot;https://blog.techbridge.cc/2018/11/14/future-of-software/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;軟體開發的未來，是大斗內時代？&lt;/a&gt;》&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;發明是從 0 到 n 的一個全過程，遠不是簡簡單單地從 0 到 1 那麼簡單。今天很多人一直夢想著自己碰到從 0 到 1 的好運氣，其實世界上從 0 到 1 的發明從來不缺，但是能走完全部 n 步才是一件難事。
――《發明的邏輯和可重複的成功》吳軍的谷歌方法論&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;做事不怕慢，就怕停。
―― 吳軍的硅谷來信&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;後記&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#後記&quot;&gt;後記&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;備忘一下這個系列的初衷與目的：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;將看過的東西寫出來，有助於提升記憶＆理解程度&lt;/li&gt;
&lt;li&gt;除了 Evernote 之外，另外有個地方可以搜尋資料&lt;/li&gt;
&lt;li&gt;有輸入、有輸出，完善知識管理的最後一塊拼圖&lt;/li&gt;
&lt;li&gt;施比受更有福，如果能幫助到世界某個角落的一個人也好，我也透過寫作永生於人間了&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;有任何建議，也歡迎不吝留言指教。&lt;/p&gt;</content:encoded><category>Weekly</category></item><item><title>如何在 React Native 使用 SVG 向量圖檔</title><link>https://blog.amowu.com/posts/2018-12-16-how-react-native-use-svg-file/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2018-12-16-how-react-native-use-svg-file/</guid><description>問題</description><pubDate>Sun, 16 Dec 2018 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;問題&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#問題&quot;&gt;問題&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;React Native 提供的 &lt;a href=&quot;https://facebook.github.io/react-native/docs/image.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Image&lt;/a&gt; 無法使用 .svg 向量圖檔&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://expo.io/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Expo&lt;/a&gt; 提供的 &lt;a href=&quot;https://docs.expo.io/versions/v26.0.0/sdk/svg&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Svg&lt;/a&gt; 需要透過 &lt;code&gt;Circle&lt;/code&gt;、&lt;code&gt;Rect&lt;/code&gt;、&lt;code&gt;Path&lt;/code&gt;、&lt;code&gt;ClipPath&lt;/code&gt; 和 &lt;code&gt;Polygon&lt;/code&gt; 自己繪製&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;解決方法&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#解決方法&quot;&gt;解決方法&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;透過 &lt;a href=&quot;https://github.com/smooth-code/svgr&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;SVGR&lt;/a&gt; 這個工具將 SVG 轉換成 React Native Component，例如：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;npx&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;svgr@v1.10.0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--native&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;./logo.svg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;LogoSVG.js&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;會自動產生下列 &lt;a href=&quot;https://github.com/react-native-community/react-native-svg&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;react-native-svg&lt;/a&gt; 可以支援的程式碼：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; React &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;react&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; Svg, { Defs, LinearGradient, Stop, Path } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;react-native-svg&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;LogoSVG&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;props&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Svg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/span&gt;&lt;span&gt;props&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Defs&lt;/span&gt;&lt;span&gt;&amp;gt;...&amp;lt;/&lt;/span&gt;&lt;span&gt;Defs&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Path&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Path&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;Svg&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;LogoSVG;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;直接在 App 使用：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; React &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;react&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { View } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;react-native&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; LogoSVG &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;./LogoSVG&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;App&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;React&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Component&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;render&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;View&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;LogoSVG&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;View&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2 id=&quot;已知問題&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#已知問題&quot;&gt;已知問題&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;如果 SVG 元件沒有 render 出來，可能需要自行給定寬和高，例如：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;LogoSVG&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;width&lt;/span&gt;&lt;span&gt;={320} &lt;/span&gt;&lt;span&gt;height&lt;/span&gt;&lt;span&gt;={240} /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;Stop /&amp;gt;&lt;/code&gt; 的 &lt;code&gt;offset&lt;/code&gt; propTypes 預設 number 是錯誤的，應該是字串，例如：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;!-- 錯誤的 --&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Stop&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;offset&lt;/span&gt;&lt;span&gt;={0.93} &lt;/span&gt;&lt;span&gt;stopColor&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;#000000&quot;&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;!-- 正確的 --&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Stop&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;offset&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;93%&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;stopColor&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;#000000&quot;&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;某些 &lt;code&gt;&amp;lt;Path /&amp;gt;&lt;/code&gt; 會掉色，要手動添加 &lt;code&gt;fill&lt;/code&gt; props，例如：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Path&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;d&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;...&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;fill&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;#00aaa5&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;/&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2 id=&quot;參考文章&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#參考文章&quot;&gt;參考文章&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.jianshu.com/p/7db2bc62c5ed&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;react-native icon解决方案（svg）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://nicolasgallagher.com/making-svg-icon-libraries-for-react-apps/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Making SVG icon libraries for React apps&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>Tech</category></item><item><title>如何自動化 GitHub Releases 流程？</title><link>https://blog.amowu.com/posts/2017-12-24-github-releases/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2017-12-24-github-releases/</guid><description>2017 年初的時候，曾經寫了《如何自動化 release 的流程？》這篇文章，介紹了如何利用 semantic-release 和 TravisCI 自動化 GitHub Releases 和 NPM publish 這件事。這次要介紹的是如何直接透過 Probot…</description><pubDate>Sun, 24 Dec 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;2017 年初的時候，曾經寫了《&lt;a href=&quot;https://blog.amowu.com/posts/2017-01-24-how-to-automate-js-release/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;如何自動化 release 的流程？&lt;/a&gt;》這篇文章，介紹了如何利用 &lt;a href=&quot;https://github.com/semantic-release/semantic-release&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;semantic-release&lt;/a&gt; 和 &lt;a href=&quot;https://travis-ci.org/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;TravisCI&lt;/a&gt; 自動化 GitHub Releases 和 NPM publish 這件事。這次要介紹的是如何直接透過 &lt;a href=&quot;https://probot.github.io/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Probot&lt;/a&gt; 機器人做到 GitHub Releases。&lt;/p&gt;
&lt;h2 id=&quot;大綱&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#大綱&quot;&gt;大綱&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;什麼是 GitHub Releases？&lt;/li&gt;
&lt;li&gt;GitHub Releases 有什麼問題？&lt;/li&gt;
&lt;li&gt;什麼是 Conventional Commits？&lt;/li&gt;
&lt;li&gt;什麼是 Conventional Release Bot？&lt;/li&gt;
&lt;li&gt;為什麼要用 Conventional Release Bot？&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;什麼是-github-releases&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#什麼是-github-releases&quot;&gt;什麼是 GitHub Releases？&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://help.github.com/articles/about-releases/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;GitHub Releases&lt;/a&gt; 是 GitHub 提供給每個專案在釋出（Release）新版軟體時，用來紀錄更新內容（Change Log）的頁面。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;521&quot; src=&quot;https://blog.amowu.com/_astro/image1.Jh50XJTt_Z1QpqiG.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image1.Jh50XJTt_CTMQh.webp 640w, https://blog.amowu.com/_astro/image1.Jh50XJTt_Z1QpqiG.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;通過 GitHub Releases，你可以為每次的 release 加入說明，描述該 release 進行了哪些更改。&lt;/p&gt;
&lt;p&gt;GitHub Releases 的基礎是建立在 &lt;a href=&quot;https://git-scm.com/book/en/v2/Git-Basics-Tagging&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Git tags&lt;/a&gt; 之上。Tags 代表了你的專案在某個特定時間點下的里程碑，所以它會是一個很好的 release 表達方式。&lt;/p&gt;
&lt;p&gt;更多有關於 Git tags 的資訊可以參考 GitHub 的《&lt;a href=&quot;https://help.github.com/articles/working-with-tags/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Working With Tags&lt;/a&gt;》。&lt;/p&gt;
&lt;h2 id=&quot;github-releases有什麼問題&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#github-releases有什麼問題&quot;&gt;GitHub Releases 有什麼問題？&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;我們團隊在執行 release 的過程中碰到過不少問題，總結如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;無法回憶起曾經「新增了哪些功能」或「修復了哪些問題」，就像突然有人問「你記得上禮拜二中午吃什麼嗎？」的感覺，每次負責 release 的人都要口頭一個一個問其他工程師，最後崩潰躲在角落獨自一個人慢慢爬 commit log⋯⋯😢&lt;/li&gt;
&lt;li&gt;Push 或 merged &lt;code&gt;master&lt;/code&gt; 的當下就應該馬上 release Git tag，但是常常會忘記做這件事，導致之後想起來還要回去找 commit SHA 才能補上 tag&lt;/li&gt;
&lt;li&gt;不是所有人都知道 &lt;a href=&quot;https://semver.org/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;SemVer&lt;/a&gt; 的版本號更新規則&lt;/li&gt;
&lt;li&gt;Git tag 下在錯誤的 branch：因為有些 repository 的 default branch 會設為 &lt;code&gt;develop&lt;/code&gt;，但是常常忘記選擇 &lt;code&gt;master&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Git tag name 打錯字：不是忘記加 v（例如 &lt;code&gt;1.1.0&lt;/code&gt; ）就是多一個點（例如 &lt;code&gt;v.1.1.0&lt;/code&gt; 或 &lt;code&gt;v1.1.0.&lt;/code&gt; ）◢▆▅▄▃ 崩╰(〒皿〒)╯潰 ▃▄▅▆◣&lt;/li&gt;
&lt;li&gt;即使上面的步驟都讓你做對好了，但是下一次要 release 還是得手動再來一遍⋯⋯這種彷彿在玩疊疊樂，希望積木不要倒的心情，我覺得不行&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;256&quot; height=&quot;256&quot; src=&quot;https://blog.amowu.com/_astro/image2.CXN3SBcb_Z1xjNLV.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image2.CXN3SBcb_Z1xjNLV.webp 256w&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;什麼是-conventional-commits&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#什麼是-conventional-commits&quot;&gt;什麼是 Conventional Commits？&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Conventional Commits 是 &lt;a href=&quot;https://conventionalcommits.org/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;conventionalcommits.org&lt;/a&gt; 在維護的一份 Git commit message 規範。&lt;/p&gt;
&lt;p&gt;Conventional Commit 定義的 commit message 結構應該如下：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;type&amp;gt;[optional scope]: &amp;lt;description&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;[optional body]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;[optional footer]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;根據 commit message 的不同，產生的 release 內容也會有所不同：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Bug Fixes&lt;/strong&gt;：當 &lt;em&gt;type&lt;/em&gt; 為 &lt;code&gt;fix&lt;/code&gt;，那麼它屬於「修復問題」類型的 commit（等同於 SemVer 的 &lt;code&gt;[PATCH](https://semver.org/)&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;New Features&lt;/strong&gt;：當 &lt;em&gt;type&lt;/em&gt; 為 &lt;code&gt;feat&lt;/code&gt; ，那麼它屬於「新增功能」類型的 commit（等同於 SemVer 的 &lt;code&gt;[MINOR](https://semver.org/)&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;BREAKING CHANGES&lt;/strong&gt;：當 &lt;em&gt;body&lt;/em&gt; 或 &lt;em&gt;footer&lt;/em&gt; 的開頭有出現 &lt;code&gt;BREAKING CHANGE:&lt;/code&gt; 這個關鍵字，那麼它屬於「會讓舊版程式無法運行的更新」類型的 commit（等同於 SemVer 的 &lt;code&gt;[MAJOR](https://semver.org/)&lt;/code&gt;）。一個 breaking change 的 type 只能是 &lt;code&gt;fix&lt;/code&gt;、&lt;code&gt;feat&lt;/code&gt; 或 &lt;code&gt;chore&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Scope 為選填內容（使用括號包在內），它可以協助 commit 提供額外的上下文資訊。例如：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;feat(component): add brand button&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Commit types 除了 &lt;code&gt;fix&lt;/code&gt; 和 &lt;code&gt;feat&lt;/code&gt; 之外，其它 types 也是允許的，例如 &lt;a href=&quot;https://github.com/angular/angular/blob/master/CONTRIBUTING.md&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Angular convention&lt;/a&gt; 建議的 &lt;code&gt;docs&lt;/code&gt;、&lt;code&gt;style&lt;/code&gt;、&lt;code&gt;refactor&lt;/code&gt;、&lt;code&gt;test&lt;/code&gt; 和 &lt;code&gt;chore&lt;/code&gt;，但是這些 types 僅供閱讀方便使用，並不會出現在 release notes 之中。&lt;/p&gt;
&lt;p&gt;遵循 Conventional Commits 之後，整體的 commits log 感覺大致如下：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;696&quot; height=&quot;458&quot; src=&quot;https://blog.amowu.com/_astro/image3.DjlDFMfO_Z1Uy2Qq.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image3.DjlDFMfO_Z1YF1RA.webp 640w, https://blog.amowu.com/_astro/image3.DjlDFMfO_Z1Uy2Qq.webp 696w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;更多細節可以參考 &lt;a href=&quot;https://conventionalcommits.org/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Conventional Commits Specification&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id=&quot;什麼是-conventional-releasebot&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#什麼是-conventional-releasebot&quot;&gt;什麼是 Conventional Release Bot？&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/apps/conventional-release-bot&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Conventional Release Bot&lt;/a&gt; 是 &lt;a href=&quot;https://hahow.in/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Hahow&lt;/a&gt; 開源的 GitHub 機器人，它能夠根據 &lt;a href=&quot;https://conventionalcommits.org/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Conventional Commits&lt;/a&gt; 的規範，自動幫你產生 GitHub Releases 和 Git tags。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;441&quot; src=&quot;https://blog.amowu.com/_astro/image4.Bs5uxnXu_2nkhht.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image4.Bs5uxnXu_1AzJtb.webp 640w, https://blog.amowu.com/_astro/image4.Bs5uxnXu_2nkhht.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;如上圖所示，Conventional Release Bot 自動產生的 GitHub Releases 內容如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Git tag（遵循 &lt;a href=&quot;https://semver.org/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;SemVer&lt;/a&gt;）&lt;/li&gt;
&lt;li&gt;Release 日期&lt;/li&gt;
&lt;li&gt;Release 類型：&lt;strong&gt;Bug Fixes&lt;/strong&gt;、&lt;strong&gt;New Features&lt;/strong&gt; 和 &lt;strong&gt;BREAKING CHANGES&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Conventional Commit 的 scope（有填寫才會出現）&lt;/li&gt;
&lt;li&gt;Commit 說明&lt;/li&gt;
&lt;li&gt;Commit SHA&lt;/li&gt;
&lt;li&gt;Commit 作者&lt;/li&gt;
&lt;li&gt;和上一版本的 diff&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;如果機器人沒有在 commits 裡發現 &lt;code&gt;feat&lt;/code&gt;、&lt;code&gt;fix&lt;/code&gt; 或 &lt;code&gt;BREAKING CHANGE&lt;/code&gt;，則會跳過建立 GitHub Releases 的步驟。&lt;/p&gt;
&lt;h2 id=&quot;為什麼要用-conventional-releasebot&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#為什麼要用-conventional-releasebot&quot;&gt;為什麼要用 Conventional Release Bot？&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;總結一下 Conventional Release Bot 的優點：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;自動化產生 CHANGELOG（GitHub Releases）&lt;/li&gt;
&lt;li&gt;自動化更新 SemVer 版本號（以 Conventional Commits 的 types 為基礎）&lt;/li&gt;
&lt;li&gt;通過友善的結構化 commit 規範，讓開發者更容易瀏覽更新歷史，提高對專案做出貢獻的意願&lt;/li&gt;
&lt;li&gt;更好地向人類（同事、使用者或其他利益相關者）傳達軟體更新的本質&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;最後，Conventional Release Bot 的原始碼＆安裝方式可以在 &lt;a href=&quot;https://github.com/amowu/probot-conventional-release&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;amowu/probot-conventional-release&lt;/a&gt; 找到，歡迎大家一起來貢獻 Pull Request。😘&lt;/p&gt;</content:encoded><category>Tech</category></item><item><title>如何使用 RxJS 處理分頁 API</title><link>https://blog.amowu.com/posts/2017-12-09-rxjs-pagination-with-github-api/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2017-12-09-rxjs-pagination-with-github-api/</guid><description>這篇文章會以 node-github 的 getCommits API 為例，介紹如何使用 RxJS 取得所有分頁的 commits 結果。</description><pubDate>Sat, 09 Dec 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;這篇文章會以 &lt;a href=&quot;https://github.com/octokit/node-github&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;node-github&lt;/a&gt; 的 &lt;code&gt;getCommits&lt;/code&gt; API 為例，介紹如何使用 RxJS 取得所有分頁的 commits 結果。&lt;/p&gt;
&lt;h2 id=&quot;前言&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#前言&quot;&gt;前言&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;以往在處理分頁的 API，通常都會使用遞回運算，這會讓程式碼的可讀性不佳。有鑒於最近 RxJS 正夯，想說來試著寫寫看，於是就有了這篇分享文章。&lt;/p&gt;
&lt;h2 id=&quot;需求&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#需求&quot;&gt;需求&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;首先，因為 node-github 的 &lt;a href=&quot;https://octokit.github.io/node-github/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;getCommits&lt;/a&gt; API 回傳的是一個 Promise 物件，所以需要先使用 RxJS 的 &lt;a href=&quot;https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/frompromise.md&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;fromPromise&lt;/a&gt; 將它轉成 Observable：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Rx.Observable&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;fromPromise&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;getCommits&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;接下來，利用 node-github 提供的 &lt;a href=&quot;https://github.com/octokit/node-github&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;hasNextPage&lt;/a&gt; 和 &lt;a href=&quot;https://github.com/octokit/node-github&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;getNextPage&lt;/a&gt;，搭配 RxJS 的 &lt;a href=&quot;https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/expand.md&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;expand&lt;/a&gt; 來處理分頁的遞回運算：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Rx.Observable&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;expand&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;response&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;hasNextPage&lt;/span&gt;&lt;span&gt;(response)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; Rx.Observable.&lt;/span&gt;&lt;span&gt;fromPromise&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;getNextPage&lt;/span&gt;&lt;span&gt;(response))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Rx.Observable.&lt;/span&gt;&lt;span&gt;empty&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;上述邏輯大概是這樣：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;如果 &lt;code&gt;getCommits&lt;/code&gt; 回傳的結果還有下一頁，就繼續 call &lt;code&gt;getNextPage&lt;/code&gt; API&lt;/li&gt;
&lt;li&gt;如果已經沒有下一頁，則回傳 &lt;code&gt;Observable.empty()&lt;/code&gt; 結束 &lt;code&gt;expand&lt;/code&gt; 運算&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;最後，透過 &lt;a href=&quot;https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/reduce.md&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;reduce&lt;/a&gt; 將所有分頁回傳的結果 &lt;code&gt;concat&lt;/code&gt; 成一個 Array：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Rx.Observable&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;reduce&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;acc&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;curr&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; acc.&lt;/span&gt;&lt;span&gt;concat&lt;/span&gt;&lt;span&gt;(curr.data)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;, []);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;整體程式碼大致如下：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Rx.Observable&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;fromPromise&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;getCommits&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;expand&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;response&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;hasNextPage&lt;/span&gt;&lt;span&gt;(response)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; Rx.Observable.&lt;/span&gt;&lt;span&gt;fromPromise&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;getNextPage&lt;/span&gt;&lt;span&gt;(response))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Rx.Observable.&lt;/span&gt;&lt;span&gt;empty&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;reduce&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;acc&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;curr&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; acc.&lt;/span&gt;&lt;span&gt;concat&lt;/span&gt;&lt;span&gt;(curr.data)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;, []);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;如果再稍微封裝一下，語法簡直優雅到無法直視 🤤：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Rx.Observable&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;fromPromise&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;getCommits&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;expand&lt;/span&gt;&lt;span&gt;(checkNextPage)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;reduce&lt;/span&gt;&lt;span&gt;(concatAllCommits);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;然後看是要用 &lt;a href=&quot;https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/subscribe.md&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;subscribe&lt;/a&gt; 或是 &lt;a href=&quot;https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/topromise.md&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;toPromise&lt;/a&gt; 來取得結果：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;getAllCommits$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; Rx.Observable&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;fromPromise&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;getCommits&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;expand&lt;/span&gt;&lt;span&gt;(checkNextPage)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;reduce&lt;/span&gt;&lt;span&gt;(concatAllCommits, []);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;// 方法一&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;getAllCommits$.&lt;/span&gt;&lt;span&gt;subscribe&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;commits&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(commits)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;// 方法二&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;commits&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; getAllCommits$.&lt;/span&gt;&lt;span&gt;toPromise&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(commits);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;// Output:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;// [{commit}, {commit}, ...]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2 id=&quot;總結&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#總結&quot;&gt;總結&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;以往的分頁 API 處理需要透過遞回運算才能完成，加上 &lt;a href=&quot;https://zh.wikipedia.org/wiki/%E6%8C%87%E4%BB%A4%E5%BC%8F%E7%B7%A8%E7%A8%8B&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;命令式編程&lt;/a&gt; 的可讀性沒有 &lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%AE%A3%E5%91%8A%E5%BC%8F%E7%B7%A8%E7%A8%8B&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;聲明式編程&lt;/a&gt; 的體驗佳，所以嘗試使用 RxJS 的 stream 取而代之&lt;/li&gt;
&lt;li&gt;使用 &lt;a href=&quot;https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/frompromise.md&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;fromPromise&lt;/a&gt; 將回傳物件為 Promise 的 API 轉換成 Observable&lt;/li&gt;
&lt;li&gt;使用 &lt;a href=&quot;https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/expand.md&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;expand&lt;/a&gt; 處理分頁的遞迴機制&lt;/li&gt;
&lt;li&gt;使用 &lt;a href=&quot;https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/reduce.md&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;reduce&lt;/a&gt; 組合所有分頁的回傳結果&lt;/li&gt;
&lt;li&gt;使用 &lt;a href=&quot;https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/topromise.md&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;toPromise&lt;/a&gt; 獲取結果&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;附件&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#附件&quot;&gt;附件&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://runkit.com/amowu/5a2556f64b348d0012eeafe3&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;RunKit Demo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://gist.github.com/amowu/5566485c9a8a64f3de171528f086fb24&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;GitHub Gist&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;參考&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#參考&quot;&gt;參考&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/35254323/rxjs-observable-pagination&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;RxJs Observable Pagination&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/44097231/rxjs-while-loop-for-paging&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;RXJS while loop for paging&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>Tech</category></item><item><title>AWS Serverless RESTful APIs</title><link>https://blog.amowu.com/posts/2017-10-01-aws-serverless-restful-api/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2017-10-01-aws-serverless-restful-api/</guid><description>近幾年在雲架構上討論熱度較高的 2 大主題分別是 容器化（Containerize）與 無伺服器（Serverless）架構。本篇文章會帶你瞭解什麼是 AWS 無伺服器架構，以及如何使用 Serverless Framework 這個框架，快速開發 RESTful APIs。</description><pubDate>Sun, 01 Oct 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;近幾年在雲架構上討論熱度較高的 2 大主題分別是 &lt;strong&gt;容器化&lt;/strong&gt;（Containerize）與 &lt;strong&gt;無伺服器&lt;/strong&gt;（Serverless）架構。本篇文章會帶你瞭解什麼是 AWS 無伺服器架構，以及如何使用 Serverless Framework 這個框架，快速開發 RESTful APIs。&lt;/p&gt;
&lt;h2 id=&quot;大綱&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#大綱&quot;&gt;大綱&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;什麼是 XaaS？&lt;/li&gt;
&lt;li&gt;AWS 無伺服器架構&lt;/li&gt;
&lt;li&gt;Serverless Framework&lt;/li&gt;
&lt;li&gt;CRUDable Service&lt;/li&gt;
&lt;li&gt;基礎設施即程式碼&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;什麼是-xaas&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#什麼是-xaas&quot;&gt;什麼是 XaaS？&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;馬雲說過一句話：「過去的一百年，我們把人變成了機器，未來的一百年，我們將會把機器變成人。」&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;640&quot; height=&quot;217&quot; src=&quot;https://blog.amowu.com/_astro/image1.BBVQpASx_Z13J0AL.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image1.BBVQpASx_Z13J0AL.webp 640w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;如果要用一句話來定義 XaaS（X as a Service）的話，那就是「&lt;strong&gt;萬物皆服務&lt;/strong&gt;」。&lt;/p&gt;
&lt;p&gt;舉凡 IaaS（基礎設施及服務）、PaaS（平台即服務）或 SaaS（軟體即服務）等⋯⋯，用一張圖來概括各個 XaaS 的關係：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;310&quot; src=&quot;https://blog.amowu.com/_astro/image2.CKuq-u22_WJrHP.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image2.CKuq-u22_Z1G5Tel.webp 640w, https://blog.amowu.com/_astro/image2.CKuq-u22_WJrHP.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;身為程式設計師，我們就是那個負責寫服務來取代人類的存在&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;所以能夠以最快速度開發出可驗證的服務原型，就成了開發者彼此之間的競爭條件之一，也是本篇文章的目的。&lt;/p&gt;
&lt;h2 id=&quot;aws-無伺服器架構&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#aws-無伺服器架構&quot;&gt;AWS 無伺服器架構&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;AWS 是目前雲技術的領頭羊，如果想要用 AWS 來開發 Serverless 的 RESTful APIs，主要會由 3 個核心服務所構成：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;AWS Lambda（Functions as a Service）&lt;/li&gt;
&lt;li&gt;Amazon API Gateway（REST API Endpoint as a Service）&lt;/li&gt;
&lt;li&gt;Amazon DynamoDB（NoSQL DB as a Service）&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;227&quot; src=&quot;https://blog.amowu.com/_astro/image3.IOQgR904_ZCy3a3.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image3.IOQgR904_13JYDC.webp 640w, https://blog.amowu.com/_astro/image3.IOQgR904_ZCy3a3.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;這裡不會贅述如何操作這些 AWS 服務，如果你已經熟悉這些服務，可以直接前往下一章的 Serverless Framework；如果還不知道它們是怎麼運作的，強烈建議先跑過一遍官方的幾篇教學與實作，理解這 3 個傢伙為什麼這麼酷，也才能理解為什麼接下來要介紹的 Serverless Framework 比它們更酷！&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://aws.amazon.com/tw/serverless/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;無伺服器運算和應用程式&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://aws.amazon.com/tw/getting-started/serverless-web-app/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Build a Serverless Web Application&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;serverless-framework&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#serverless-framework&quot;&gt;Serverless Framework&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;接下來進入本篇文章的重點，如果每次建立 REST API 都需要手動操作 Lambda、API Gateway 和 DynamoDB 這些服務的話就太麻煩了，所以這裡介紹一個第三方社群維護的框架：&lt;a href=&quot;https://serverless.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Serverless Framework&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;174&quot; src=&quot;https://blog.amowu.com/_astro/image4.BcKlACA2_233zFR.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image4.BcKlACA2_Z1l5Fgx.webp 640w, https://blog.amowu.com/_astro/image4.BcKlACA2_233zFR.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Serverless Framework 提供了一整套專為「無伺服器架構」所設計的開發＆部署工具，除了 AWS 之外還支援了 Google Cloud Platform 和 Microsoft Azure 等雲服務平台，讓開發者只需專注在 Web、Mobile 和 IoT 應用的核心功能，而無需煩惱基礎設施的維護。&lt;/p&gt;
&lt;p&gt;先來跑一遍官方的快速入門暖暖身，首先安裝 Serverless 的 CLI：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;npm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;serverless&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--global&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;注意：因為 Serverless CLI 會透過 AWS SDK 間接操作你的 AWS 帳戶，所以在開始之前，請先確認本機端已經配置完 AWS 的 credential，詳細設定方法可以參考官方文件的《&lt;/em&gt;&lt;a href=&quot;https://serverless.com/framework/docs/providers/aws/guide/credentials/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;em&gt;Credentials&lt;/em&gt;&lt;/a&gt;&lt;em&gt;》。&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;新增一個 Node.js 環境的專案 &lt;code&gt;hello-service&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;module&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;exports&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;hello&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;event&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;context&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;callback&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;response&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;statusCode: &lt;/span&gt;&lt;span&gt;200&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;body: &lt;/span&gt;&lt;span&gt;JSON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;stringify&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;message: &lt;/span&gt;&lt;span&gt;&apos;Go Serverless v1.0! Your function executed successfully!&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;input: event,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;callback&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;, response);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;部署 Lambda Function 至 AWS：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;serverless&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;deploy&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--verbose&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;*--verbose*&lt;/code&gt;* 顯示部署過程的細節。*&lt;/p&gt;
&lt;p&gt;如果每次都需要重新部署所有 Lambda Function 會很沒效率，Serverless 也可以只部署單個 Lambda Function：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;serverless&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;deploy&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;hello&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;*--function*&lt;/code&gt;* 指定 Lambda Function 的名稱。*&lt;/p&gt;
&lt;p&gt;部署完成之後，使用 &lt;code&gt;invoke&lt;/code&gt; 執行 Lambda Function：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;serverless&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;invoke&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;hello&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--log&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;*--log*&lt;/code&gt;* 顯示執行回傳的結果。*&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;statusCode&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;200&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;body&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;{&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;message&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;Go Serverless v1.0! Your function executed successfully!&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;input&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;:{}}&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;CLI 也可以使用 &lt;code&gt;logs&lt;/code&gt; 和 &lt;code&gt;--tail&lt;/code&gt; 來持續監控 Lambda Function 的執行結果：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;serverless&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;logs&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;hello&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--tail&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;瞭解整個 Serverless 的運作方式之後，CLI 還有提供 &lt;code&gt;remove&lt;/code&gt; 指令，讓你可以將整個專案從 AWS 帳戶移除，畢竟佔用這些資源是要計費的：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;serverless&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;remove&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2 id=&quot;crudable-service&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#crudable-service&quot;&gt;CRUDable Service&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;瞭解 Serverless Framework 如何輕易地部署 Lambda Function 之後，我們來看看 Serverless Framework 如何開發一套完整、可以 &lt;a href=&quot;https://zh.wikipedia.org/wiki/%E8%B3%87%E6%96%99%E6%93%8D%E7%B8%B1%E8%AA%9E%E8%A8%80&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;CRUD&lt;/a&gt; 資料庫的 REST API 服務。&lt;/p&gt;
&lt;p&gt;下載 Serverless 社群寫好的 To-do CRUD 範例：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;serverless&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--url&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;https://github.com/pmuens/serverless-crud&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;注意：因為這個範例有用到其它第三方的 npm packages，所以部署前記得先 &lt;em&gt;&lt;code&gt;*npm install*&lt;/code&gt;&lt;/em&gt; 安裝。&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;部署 To-do service 至 AWS：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;serverless&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;deploy&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--verbose&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;部署完成之後，CLI 會顯示你的 API Gateway endpoints：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;endpoints:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;POST&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;https://18055egsjj.execute-api.us-east-1.amazonaws.com/dev/todos&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;GET&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;https://18055egsjj.execute-api.us-east-1.amazonaws.com/dev/todos&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;GET&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;https://18055egsjj.execute-api.us-east-1.amazonaws.com/dev/todos/{id}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;PUT&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;https://18055egsjj.execute-api.us-east-1.amazonaws.com/dev/todos/{id}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;DELETE&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;https://18055egsjj.execute-api.us-east-1.amazonaws.com/dev/todos/{id}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3 id=&quot;read-allto-dos&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#read-allto-dos&quot;&gt;Read All To-dos&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;使用 cURL 或 &lt;a href=&quot;https://httpie.org/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;HTTPie&lt;/a&gt; 之類的 HTTP client 來測試結果，首先是取得所有 to-dos：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;http&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;GET&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;https://18055egsjj.execute-api.us-east-1.amazonaws.com/dev/todos&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;沒意外的話，會回傳空的 To-do 陣列：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;HTTP&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;200&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;OK&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;[]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;打開程式碼，Read all to-dos 的 Lambda Function 很簡單，透過 AWS SDK 的 DynamoDB &lt;code&gt;scan&lt;/code&gt; API 來取得所有 todos table 的 items：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame has-title&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;todos-read-all.js&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AWS&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;aws-sdk&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;dynamoDb&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AWS&lt;/span&gt;&lt;span&gt;.DynamoDB.&lt;/span&gt;&lt;span&gt;DocumentClient&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;module&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;exports&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;event&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;callback&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;params&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;TableName: &lt;/span&gt;&lt;span&gt;&apos;todos&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; dynamoDb.&lt;/span&gt;&lt;span&gt;scan&lt;/span&gt;&lt;span&gt;(params, (&lt;/span&gt;&lt;span&gt;error&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (error) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;callback&lt;/span&gt;&lt;span&gt;(error);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;callback&lt;/span&gt;&lt;span&gt;(error, data.Items);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3 id=&quot;create-to-do&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#create-to-do&quot;&gt;Create To-do&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;接著我們試著新增一筆 to-do：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;http&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;POST&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;https://&amp;lt;省略&amp;gt;/todos&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;body=&quot;Hodor&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;新增成功並回傳一筆包含 &lt;code&gt;id&lt;/code&gt;、&lt;code&gt;body&lt;/code&gt; 和 &lt;code&gt;updateAt&lt;/code&gt; 欄位的 to-do item：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;HTTP&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;200&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;OK&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;body&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Hodor&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;id&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;1b691310-a0e7-11e7-b1c4-1d790783726f&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;updatedAt&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1506230011585&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Create 的 Lambda Function 也很好懂，透過 UUID 以及 DynamoDB 的 &lt;code&gt;put&lt;/code&gt; API 新增一筆 to-do item：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame has-title&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;todos-create.js&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;uuid&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;uuid&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;module&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;exports&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;event&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;callback&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;JSON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;parse&lt;/span&gt;&lt;span&gt;(event.body);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;data.id &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; uuid.&lt;/span&gt;&lt;span&gt;v1&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;data.updatedAt &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Date&lt;/span&gt;&lt;span&gt;().&lt;/span&gt;&lt;span&gt;getTime&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;params&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;TableName: &lt;/span&gt;&lt;span&gt;&apos;todos&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Item: data&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; dynamoDb.&lt;/span&gt;&lt;span&gt;put&lt;/span&gt;&lt;span&gt;(params, (&lt;/span&gt;&lt;span&gt;error&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (error) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;callback&lt;/span&gt;&lt;span&gt;(error);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;callback&lt;/span&gt;&lt;span&gt;(error, params.Item);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3 id=&quot;read-oneto-do&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#read-oneto-do&quot;&gt;Read One To-do&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;接著我們試著讀取剛才新增的 id 為 &lt;code&gt;1b691310-a0e7-11e7-b1c4-1d790783726f&lt;/code&gt; 的 to-do：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;http&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;GET&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;https://&amp;lt;省略&amp;gt;/todos/1b691310-a0e7-11e7-b1c4-1d790783726f&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;HTTP&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;200&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;OK&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;body&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Hodor&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;id&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;1b691310-a0e7-11e7-b1c4-1d790783726f&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;updatedAt&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1506230011585&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;這裡要注意，DynamoDB 讀取所有 items 所使用 API 是 &lt;code&gt;scan&lt;/code&gt;，讀取單筆 item 的 API 是 &lt;code&gt;get&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame has-title&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;todos-read-one.js&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;module&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;exports&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;event&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;callback&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;params&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;TableName: &lt;/span&gt;&lt;span&gt;&apos;todos&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Key: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;// 透過 event.pathParameters 來取得 URL 的 todo item id&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;id: event.pathParameters.id&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; dynamoDb.&lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt;(params, (&lt;/span&gt;&lt;span&gt;error&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (error) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;callback&lt;/span&gt;&lt;span&gt;(error);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;callback&lt;/span&gt;&lt;span&gt;(error, data.Item);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3 id=&quot;update-to-do&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#update-to-do&quot;&gt;Update To-do&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;更新已經存在的單筆 to-do：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;http&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;PUT&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;https://&amp;lt;省略&amp;gt;/todos/1b691310-a0e7-11e7-b1c4-1d790783726f&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;body=&quot;Hold The Door&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;HTTP&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;200&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;OK&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;body&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Hold The Door&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;id&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;1b691310-a0e7-11e7-b1c4-1d790783726f&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;updatedAt&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1506230311609&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;更新 DynamoDB item 的程式碼基本上與新增的方式差不多：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame has-title&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;todos-update.js&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;module&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;exports&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;event&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;callback&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;JSON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;parse&lt;/span&gt;&lt;span&gt;(event.body);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;data.id &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; event.pathParameters.id;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;data.updatedAt &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Date&lt;/span&gt;&lt;span&gt;().&lt;/span&gt;&lt;span&gt;getTime&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;params&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;TableName : &lt;/span&gt;&lt;span&gt;&apos;todos&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Item: data&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; dynamoDb.&lt;/span&gt;&lt;span&gt;put&lt;/span&gt;&lt;span&gt;(params, (&lt;/span&gt;&lt;span&gt;error&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (error) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;callback&lt;/span&gt;&lt;span&gt;(error);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;callback&lt;/span&gt;&lt;span&gt;(error, params.Item);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3 id=&quot;delete-to-do&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#delete-to-do&quot;&gt;Delete To-do&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;最後剩下刪除已存在的單筆 to-do：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;http&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;DELETE&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;https://&amp;lt;省略&amp;gt;/todos/1b691310-a0e7-11e7-b1c4-1d790783726f&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;HTTP&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;200&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;OK&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;id&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;1b691310-a0e7-11e7-b1c4-1d790783726f&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;刪除的 Lambda Function 程式碼：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame has-title&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;todos-delete.js&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;module.exports &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;event&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;callback&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;params&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;TableName &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;todos&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Key&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; event.pathParameters.id&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; dynamoDb.&lt;/span&gt;&lt;span&gt;delete&lt;/span&gt;&lt;span&gt;(params, (&lt;/span&gt;&lt;span&gt;error&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (error) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;callback&lt;/span&gt;&lt;span&gt;(error);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;callback&lt;/span&gt;&lt;span&gt;(error, params.Key);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2 id=&quot;基礎設施即程式碼&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#基礎設施即程式碼&quot;&gt;基礎設施即程式碼&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;跑完上面的 to-do services 之後有沒有感到很神奇呢？竟然一個 &lt;code&gt;serverless deploy&lt;/code&gt; 指令，就可以把原本需要手動配置的所有 AWS 基礎服務都自動建立好了，這是因為 Serverless Framework 用到了 &lt;a href=&quot;https://aws.amazon.com/tw/cloudformation/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;AWS CloudFormation&lt;/a&gt; 這個服務。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;281&quot; height=&quot;261&quot; src=&quot;https://blog.amowu.com/_astro/image5.CgIEHIPI_1GMkBD.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image5.CgIEHIPI_1GMkBD.webp 281w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;我們先打開 &lt;code&gt;serverless.yml&lt;/code&gt; 這個檔案來看看 Lambda Function 和 API Gateway 的配置：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;functions&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;readAll&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;readOne&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;handler&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;handler.readOne&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;events&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;http&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;          &lt;/span&gt;&lt;span&gt;path&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;todos/{id}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;          &lt;/span&gt;&lt;span&gt;method&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;          &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;update&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;delete&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;DynamoDB 的設定：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;resources&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;Resources&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;TodosDynamoDbTable&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;Properties&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;AttributeDefinitions&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;AttributeName&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;AttributeType&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;S&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;KeySchema&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;AttributeName&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;TableName&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&apos;todos&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;這就是 Infrastructure as a Code（基礎設施即程式碼）的威力，它的好處：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;隨地配置：直接使用程式碼控制你的基礎設施資源，忘記那些繁瑣的操作流程&lt;/li&gt;
&lt;li&gt;易於維護：因為程式碼可以上版本控制，所以可以多人共同維護一份基礎設施&lt;/li&gt;
&lt;li&gt;持續交付：因為可以進版本控制，所以更容易整合進 CI/CD 的自動化流程&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;結論&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#結論&quot;&gt;結論&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;XaaS（萬物皆服務）是未來的趨勢，而服務的本質就是 API，誰能夠越快地開發出原型、越有機會成功&lt;/li&gt;
&lt;li&gt;如果要用 AWS 的無伺服器架構來開發 REST API，核心服務分別是 Lambda、API Gateway 和 DynamoDB&lt;/li&gt;
&lt;li&gt;Serverless Framework 利用「基礎設施即程式碼」的概念，讓開發者可以更迅速地在 AWS 上建立一套可維護的 CRUD RESTful API Services&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;參考文章&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#參考文章&quot;&gt;參考文章&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://www.ruanyifeng.com/blog/2017/07/iaas-paas-saas.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;IaaS，PaaS，SaaS 的區別&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.ithome.com.tw/news/115074&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;蔡學鏞：別再用物件導向，純雲架構最好改用函數式設計，5大架構秘訣公開&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>Tech</category></item><item><title>Mock Server＆契約測試</title><link>https://blog.amowu.com/posts/2017-08-26-mock-server/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2017-08-26-mock-server/</guid><description>這篇文章會介紹如何運用 Mock Server 和 Integration Contract Test（契約測試）解決一些在前後端分離的開發環境底下會碰到的問題。</description><pubDate>Sat, 26 Aug 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;這篇文章會介紹如何運用 Mock Server 和 Integration Contract Test（契約測試）解決一些在前後端分離的開發環境底下會碰到的問題。&lt;/p&gt;
&lt;p&gt;大綱如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;什麼是 Mock Server？&lt;/li&gt;
&lt;li&gt;為什麼需要 Mock Server？&lt;/li&gt;
&lt;li&gt;如何使用 Mock Server？&lt;/li&gt;
&lt;li&gt;什麼是契約測試？&lt;/li&gt;
&lt;li&gt;為什麼需要契約測試？&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;什麼是-mockserver&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#什麼是-mockserver&quot;&gt;什麼是 Mock Server？&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;下圖是傳統的前後端分離架構：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;433&quot; height=&quot;83&quot; src=&quot;https://blog.amowu.com/_astro/image1.DZjmlB8G_Z2rQSEI.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image1.DZjmlB8G_Z2rQSEI.webp 433w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;當後端 API 還沒開發完成的時候，前端會需要一個可以暫時回應假資料（mock data）的 mock server，如下圖：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;443&quot; height=&quot;233&quot; src=&quot;https://blog.amowu.com/_astro/image2.DYQW_dTK_207qpN.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image2.DYQW_dTK_207qpN.webp 443w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;等到後端的 API 開發完成之後，前端只需要將 API endpoint 從 mock server 切回 remote server 就可以使用真實資料，如下圖：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;443&quot; height=&quot;233&quot; src=&quot;https://blog.amowu.com/_astro/image3.X30cNfjt_Z1lyqrm.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image3.X30cNfjt_Z1lyqrm.webp 443w&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;為什麼需要-mockserver&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#為什麼需要-mockserver&quot;&gt;為什麼需要 Mock Server？&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;一句話，因為有了 Mock Server 之後，前後端就能夠並行開發。&lt;/p&gt;
&lt;h2 id=&quot;如何使用-mockserver&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#如何使用-mockserver&quot;&gt;如何使用 Mock Server？&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;這裡會介紹兩種方法：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Postman Mock Service&lt;/li&gt;
&lt;li&gt;Puer Mock Server&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;postman-mockservice&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#postman-mockservice&quot;&gt;Postman Mock Service&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.getpostman.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Postman&lt;/a&gt; 是前後端在開發上很常用到的一款 HTTP Client 應用程式，主要是拿來測試 API，除了有好用的 Collection Test Runner 之外（詳見《&lt;a href=&quot;https://segmentfault.com/a/1190000005055899&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;基於 Postman 的 API 自動化測試&lt;/a&gt;》），其實 Postman 還有提供 Mock Service 的功能，大致流程如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;送出一個 request（R1）&lt;/li&gt;
&lt;li&gt;儲存 R1 至 Collection（C1）&lt;/li&gt;
&lt;li&gt;編輯 R1 的 response，儲存成為一個 example（P1）&lt;/li&gt;
&lt;li&gt;建立一個 C1 的 Mock Server（M1）&lt;/li&gt;
&lt;li&gt;再次向 M1 送出 R1，即會收到格式為 P1 的 response&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;詳細操作方法請見官方教學文章《&lt;a href=&quot;https://www.getpostman.com/docs/postman/mock_servers/mocking_with_examples&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Mocking with examples&lt;/a&gt;》。&lt;/p&gt;
&lt;p&gt;但是使用 Postman Mock Service 會碰到一個問題，雖然 Postman 可以同時 mock 多筆 API，但是一個頁面可能會同時存在「需要 mock 的 API」和「後端已經寫好的 API」。&lt;/p&gt;
&lt;h2 id=&quot;puer-mockserver&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#puer-mockserver&quot;&gt;Puer Mock Server&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;為了解決上面碰到的問題，找到了這個 open-source 的 mock server：&lt;a href=&quot;https://github.com/ufologist/puer-mock&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;puer-mock&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;它可以將沒有 mock 的 API route 經由 proxy 導向後端的 API server，示意圖如下：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;443&quot; height=&quot;233&quot; src=&quot;https://blog.amowu.com/_astro/image4.CcCoibqQ_ZYAakh.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image4.CcCoibqQ_ZYAakh.webp 443w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;安裝＆使用步驟如下：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;npm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--save&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;puer&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;puer-mock&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;新增 &lt;code&gt;mock.json&lt;/code&gt;，加入一筆 mock route（以 &lt;code&gt;GET /api/users&lt;/code&gt; 為例）：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;api&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;&quot;GET /api/users&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;&quot;response&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;                &lt;/span&gt;&lt;span&gt;&quot;foo&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;bar&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;其中 &lt;code&gt;response&lt;/code&gt; 的部分就是你自定義的 mock data。&lt;/p&gt;
&lt;p&gt;新增 &lt;code&gt;server.js&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;module&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;exports&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;puer-mock&apos;&lt;/span&gt;&lt;span&gt;)(__filename, &lt;/span&gt;&lt;span&gt;&apos;./mock.json&apos;&lt;/span&gt;&lt;span&gt;) ;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;修改 &lt;code&gt;package.json&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;scripts&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;&quot;start&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;puer -a server.js&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;如果要 proxy 後端的 API server，可以加上 &lt;code&gt;-t&lt;/code&gt; 或 &lt;code&gt;--target&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;scripts&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;&quot;start&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;puer -a server.js -t http://&amp;lt;YOUR_API_SERVER_HOST&amp;gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;啟動 mock server：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;npm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;start&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;測試 mock API：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;curl&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;http://localhost:8080/api/users&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;foo&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;bar&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;更多的配置可以參考 &lt;a href=&quot;https://github.com/ufologist/puer-mock&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;puer-mock&lt;/a&gt;、&lt;a href=&quot;https://github.com/leeluolee/puer&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;puer&lt;/a&gt; 和 &lt;a href=&quot;https://github.com/nuysoft/Mock&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Mock.js&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id=&quot;什麼是契約測試&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#什麼是契約測試&quot;&gt;什麼是契約測試？&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;契約測試（Integration Contract Test）的流程大致如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;前後端在開發之前先約定好契約，也就是 API 的 spec（常見如 &lt;a href=&quot;https://swagger.io/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Swagger&lt;/a&gt;、&lt;a href=&quot;https://raml.org/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;RAML&lt;/a&gt;、&lt;a href=&quot;https://apiblueprint.org/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;API Blueprint&lt;/a&gt; 或 &lt;a href=&quot;http://apidocjs.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;apiDoc&lt;/a&gt;）&lt;/li&gt;
&lt;li&gt;後端根據這份契約開發 remote server（API server）&lt;/li&gt;
&lt;li&gt;前端根據這份契約建立 mock server&lt;/li&gt;
&lt;li&gt;撰寫契約測試，驗證雙方的 request 和 response 是一致的&lt;/li&gt;
&lt;li&gt;將契約測試上 CI server&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;示意圖如下：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;304&quot; height=&quot;243&quot; src=&quot;https://blog.amowu.com/_astro/image5.DNv7Yg2J_aWii4.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image5.DNv7Yg2J_aWii4.webp 304w&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;為什麼需要契約測試&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#為什麼需要契約測試&quot;&gt;為什麼需要契約測試？&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;防止前後端其中一方更改 API 之後，另一方卻不知情（因為 CI 會失敗）&lt;/li&gt;
&lt;li&gt;契約需要一直保持在最新的狀態，代表 API 文件有人維護&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;結論&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#結論&quot;&gt;結論&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;有一個 Mock Server 對於前後端的並行開發是有幫助的&lt;/li&gt;
&lt;li&gt;加入契約測試可以避免前後端任一方修改 API 之後，導致另一方程式崩潰的狀況&lt;/li&gt;
&lt;li&gt;API 文件有人維護&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;最後，如果再整合進「前端的 E2E 測試」和「後端的 API 測試」，架構的示意圖大概如下：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;604&quot; height=&quot;423&quot; src=&quot;https://blog.amowu.com/_astro/image6.BnT9cB0T_8zpM.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image6.BnT9cB0T_8zpM.webp 604w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;相信這對整個團隊的開發穩定度會有很大的幫助，從此前端與後端過著幸福快樂的日子。&lt;/p&gt;
&lt;h2 id=&quot;參考資料&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#參考資料&quot;&gt;參考資料&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.zhihu.com/question/35436669&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;你是如何構建 Web 前端 Mock Server 的？&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/phodal/fe/blob/master/chapters/chapter-13.md&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;如何處理好前後端分離的 API 問題&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://martinfowler.com/bliki/IntegrationContractTest.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;IntegrationContractTest&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/ufologist/puer-mock/blob/master/why-your-need-a-mock-server.md&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;為什麼你需要一個 mock server&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dev.to/milipski/test-doubles---fakes-mocks-and-stubs&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Test Doubles — Fakes, Mocks and Stubs.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://teddy-chen-tw.blogspot.tw/2012/12/4remote-services.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;對付時好時壞的測試案例(4)：Remote Services&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>Tech</category></item><item><title>如何使用 Docker 切換不同的 MongoDB</title><link>https://blog.amowu.com/posts/2017-07-22-docker-mongodb/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2017-07-22-docker-mongodb/</guid><description>在開發前端的時候，常常會碰到想要回到 migration 之前的 MongoDB 資料結構來除錯，如果只使用本地安裝的 MongoDB，操作上會很麻煩，所以這篇文章會說明如何在本機不安裝 MongoDB 的環境下，使用 Docker 準備多份 MongoDB 資料庫。</description><pubDate>Sat, 22 Jul 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;在開發前端的時候，常常會碰到想要回到 migration 之前的 MongoDB 資料結構來除錯，如果只使用本地安裝的 MongoDB，操作上會很麻煩，所以這篇文章會說明如何在本機不安裝 MongoDB 的環境下，使用 Docker 準備多份 MongoDB 資料庫。&lt;/p&gt;
&lt;p&gt;請確認電腦有安裝 Docker，先準備好要使用的 MongoDB 資料庫備份檔案，大概會是長這樣：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;430&quot; src=&quot;https://blog.amowu.com/_astro/cover.C-l9UPzv_2qQj5E.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/cover.C-l9UPzv_2mHnxE.webp 640w, https://blog.amowu.com/_astro/cover.C-l9UPzv_2qQj5E.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;存放的路徑這裡暫定為 &lt;code&gt;~/Downloads/20170622/foo/...&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;打開 Terminal，下載 MongoDB（這裡以 2.6 版作為示範）的 Docker image：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;docker&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pull&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mongo:2.6&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;然後開啟一個新的 MongoDB Docker container，container 名字可以透過 &lt;code&gt;--name&lt;/code&gt; 自訂：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;docker&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--detach&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--name&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mongo_foo_20170622&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--publish&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;27017:27017&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mongo:2.6&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;使用 &lt;code&gt;docker inspect&lt;/code&gt; 取得 container 的 IP，後面會用到：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;docker&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;inspect&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mongo_foo_20170622&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;grep&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;IPAddress&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;前往剛才存放備份資料庫的位置：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/Downloads&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;開啟並進入一個暫時性質的 Docker container，用途為 restore 資料庫到 &lt;code&gt;mongo_foo_20170622&lt;/code&gt; 的 container：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;docker&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--interactive&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--tty&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--rm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--volume&lt;/span&gt;&lt;span&gt; $PWD&lt;/span&gt;&lt;span&gt;:/tmp&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mongo:2.6&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bash&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;因為 &lt;code&gt;~/Downloads&lt;/code&gt; 被 volume 在 &lt;code&gt;/tmp&lt;/code&gt; 底下，所以可以根據對應的路徑前往該資料庫存放的資料夾位置：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/tmp&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;使用 &lt;code&gt;mongorestore&lt;/code&gt; 恢復備份資料庫到 &lt;code&gt;mongo_foo_20170622&lt;/code&gt;，IP 記得使用上面 &lt;code&gt;docker inspect&lt;/code&gt; 查到的 IP：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mongorestore&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--host&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;172.17.0.2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--db&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;foo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;20170622/foo&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;如果順利 restore 完成之後，就可以離開，它會自動刪除這個一次性的 container：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;exit&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;之後如果還有其它版本的 MongoDB 想要切換著使用的話，可以繼續從第一個步驟建立新的 container。&lt;/p&gt;
&lt;p&gt;順帶一提，Docker for Mac 有內建一個叫 Kitematic 的 Docker GUI，可以使用它來切換不同版本的 MongoDB：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;439&quot; height=&quot;333&quot; src=&quot;https://blog.amowu.com/_astro/image1.B5e4KbXO_Z9xHqa.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image1.B5e4KbXO_Z9xHqa.webp 439w&quot; /&gt;&lt;/p&gt;</content:encoded><category>Tech</category></item><item><title>使用 Google Docs 將圖片轉成文字</title><link>https://blog.amowu.com/posts/2017-06-28-google-docs-ocr/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2017-06-28-google-docs-ocr/</guid><description>像我這種日文不好、需要靠翻譯工具作參考的人，有時候想翻譯雜誌這種長文會很麻煩。</description><pubDate>Wed, 28 Jun 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;像我這種日文不好、需要靠翻譯工具作參考的人，有時候想翻譯雜誌這種長文會很麻煩。&lt;/p&gt;
&lt;p&gt;以前會找一些網路上的 OCR 工具，最近才發現原來 Google Docs 就提供這樣的功能！而且還很強大！ヽ(●´∀`●)ﾉ&lt;/p&gt;
&lt;p&gt;以下圖為例，假如我想要翻譯底下那塊專欄的話：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;950&quot; src=&quot;https://blog.amowu.com/_astro/image1.BIjWnG-1_1wzG7G.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image1.BIjWnG-1_4fQ5b.webp 640w, https://blog.amowu.com/_astro/image1.BIjWnG-1_1wzG7G.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;先將文字區塊裁剪成一張圖檔：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;688&quot; height=&quot;229&quot; src=&quot;https://blog.amowu.com/_astro/image2.CUO9gJsu_1njVHX.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image2.CUO9gJsu_dxDzT.webp 640w, https://blog.amowu.com/_astro/image2.CUO9gJsu_1njVHX.webp 688w&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;上傳圖片到 Google 雲端硬碟&lt;/li&gt;
&lt;li&gt;點選右鍵 &amp;gt; 選擇開啟工具 &amp;gt; Google 文件&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;471&quot; src=&quot;https://blog.amowu.com/_astro/cover.DMcFIX2x_149s3u.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/cover.DMcFIX2x_9A6Cl.webp 640w, https://blog.amowu.com/_astro/cover.DMcFIX2x_149s3u.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;等待它轉換完成之後，就會產生一份帶有文字檔案的 Google 文件：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;664&quot; height=&quot;604&quot; src=&quot;https://blog.amowu.com/_astro/image3.sZCeReRu_12yJlF.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image3.sZCeReRu_ZMyGy7.webp 640w, https://blog.amowu.com/_astro/image3.sZCeReRu_12yJlF.webp 664w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;完成！Google Docs 提供的圖片轉文字功能有以下特色：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;語言自動判斷，日文也沒問題 (&lt;em&gt;´艸`&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;橫書＆直書自動判斷，這個有點厲害 🌚&lt;/li&gt;
&lt;li&gt;相較於其它的 OCR 服務，Google Docs 在內容上的判斷準確率很高！&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;從此翻譯雜誌的效率大幅提升 ｡:.ﾟヽ(*´∀`)ﾉﾟ.:｡&lt;/p&gt;</content:encoded><category>Tech</category></item><item><title>如何解決 GPG 失效的問題？</title><link>https://blog.amowu.com/posts/2017-06-24-gpg/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2017-06-24-gpg/</guid><description>我是用 cider 在管理自己的 dotfiles，然後前陣子因為 gnupg 的 formula 剛好一起被更新，導致我的 GPG signature verification 無法順利運作。</description><pubDate>Sat, 24 Jun 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;我是用 &lt;a href=&quot;https://github.com/msanders/cider&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;cider&lt;/a&gt; 在管理自己的 dotfiles，然後前陣子因為 &lt;a href=&quot;https://www.gnupg.org/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;gnupg&lt;/a&gt; 的 formula 剛好一起被更新，導致我的 &lt;a href=&quot;https://github.com/blog/2144-gpg-signature-verification&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;GPG signature verification&lt;/a&gt; 無法順利運作。&lt;/p&gt;
&lt;p&gt;解決方式：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;unlink&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;gnupg&lt;/span&gt;&lt;span&gt; &amp;amp;&amp;amp; &lt;/span&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;link&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;gnupg&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;如果有跳出某些 conflicting error 的話，可以照著提示解決，例如：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Linking&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/usr/local/Cellar/gnupg/2.1.21...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Error:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Could&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;not&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;symlink&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bin/gpg-agent&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Target&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/usr/local/bin/gpg-agent&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;is&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;symlink&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;belonging&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;to&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;gpg-agent.&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;You&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;can&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;unlink&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;it:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;unlink&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;gpg-agent&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;To&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;force&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;the&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;link&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;and&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;overwrite&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;all&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;conflicting&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;files:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;link&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--overwrite&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;gnupg&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;To&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;list&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;all&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;files&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;that&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;would&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;be&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;deleted:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;link&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--overwrite&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--dry-run&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;gnupg&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;然後再重新 link gnupg 一次：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;unlink&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;gnupg&lt;/span&gt;&lt;span&gt; &amp;amp;&amp;amp; &lt;/span&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;link&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;gnupg&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;最後檢查 Git 能不能順利 commit 和 push，然後確認 GitHub 的 commits 有出現 verified signature 的話，表示順利修復成功。&lt;/p&gt;</content:encoded><category>Tech</category></item><item><title>如何發送 redux-observable 的 catch error 至 Sentry</title><link>https://blog.amowu.com/posts/2017-06-24-redux-observable-catch-error-sentry-24/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2017-06-24-redux-observable-catch-error-sentry-24/</guid><description>我們團隊目前使用 Sentry 這個服務作 error tracking，JavaScript 或 React 的基本安裝方法在 官方文件 都可以找到，這裡就不贅述。</description><pubDate>Sat, 24 Jun 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;我們團隊目前使用 &lt;a href=&quot;https://sentry.io/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Sentry&lt;/a&gt; 這個服務作 error tracking，JavaScript 或 React 的基本安裝方法在 &lt;a href=&quot;https://docs.sentry.io/clients/javascript/integrations/react/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;官方文件&lt;/a&gt; 都可以找到，這裡就不贅述。&lt;/p&gt;
&lt;p&gt;同時我們也有在使用 &lt;a href=&quot;https://github.com/redux-observable/redux-observable&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;redux-observable&lt;/a&gt; 這個 RxJS middleware 來處理帶有副作用的 Redux action。&lt;/p&gt;
&lt;p&gt;根據 redux-observable 這篇 &lt;a href=&quot;https://redux-observable.js.org/docs/recipes/ErrorHandling.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Error Handling&lt;/a&gt; 文件的介紹，一般處理 async 錯誤的寫法大概會是：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { createAction } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;redux-actions&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { Observable } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;rxjs/Observable&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fetchUserEpic&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;action$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;action$.&lt;/span&gt;&lt;span&gt;ofType&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;FETCH_USER&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;mergeMap&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;action&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Observable.&lt;/span&gt;&lt;span&gt;fromPromise&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;fetch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;`/api/users/${&lt;/span&gt;&lt;span&gt;action&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;payload&lt;/span&gt;&lt;span&gt;}`&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;map&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;response&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;createAction&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;FETCH_USER_FULFILLED&apos;&lt;/span&gt;&lt;span&gt;)(response))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;error&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; Observable.&lt;/span&gt;&lt;span&gt;of&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;createAction&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;FETCH_USER_REJECTED&apos;&lt;/span&gt;&lt;span&gt;)(error.message)))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;但是因為這裡並非正規的錯誤拋出方式，導致 Sentry 無法攔截到。&lt;/p&gt;
&lt;p&gt;所以根據 Sentry 的這篇 &lt;a href=&quot;https://blog.sentry.io/2016/08/24/redux-middleware-error-logging&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Rich Error Reports with Redux Middleware&lt;/a&gt; 文件介紹，我們需要另外為它寫一個 Redux middleware 來處理。&lt;/p&gt;
&lt;p&gt;策略是利用 &lt;a href=&quot;https://github.com/acdlite/redux-actions&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;redux-actions&lt;/a&gt; 的 &lt;a href=&quot;https://github.com/acdlite/flux-standard-action&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Flux Standard Action&lt;/a&gt; 特性，將錯誤用 JavaScript 的 Error object 封裝至 action payload：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;error&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; Observable.&lt;/span&gt;&lt;span&gt;of&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;createAction&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;FETCH_USER_REJECTED&apos;&lt;/span&gt;&lt;span&gt;)(&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Error&lt;/span&gt;&lt;span&gt;(error.message))))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;// =&amp;gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;//   type: &apos;FETCH_USER_REJECTED&apos;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;//   payload: new Error(error.message),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;//   error: true,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;// }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;然後寫一個 Redux middleware 去攔截這類 actions：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; Raven &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;raven-js&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Raven.&lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt;(process.env.&lt;/span&gt;&lt;span&gt;REACT_APP_RAVEN_DSN&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;errorReporter&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;store&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;next&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;action&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (action.error) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;error&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; action.payload;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Raven.&lt;/span&gt;&lt;span&gt;captureException&lt;/span&gt;&lt;span&gt;(error, {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;extra: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;action,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;state: store.&lt;/span&gt;&lt;span&gt;getState&lt;/span&gt;&lt;span&gt;(),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;throw&lt;/span&gt;&lt;span&gt; error;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;next&lt;/span&gt;&lt;span&gt;(action);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;errorReporter;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;最後將這個 middleware 載入 Redux store：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame has-title&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;store.js&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { createStore, combineReducers, applyMiddleware } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;redux&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt; reducers &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;./reducers&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; errorReporter &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;./errorReporter&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rootReducer&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;combineReducers&lt;/span&gt;&lt;span&gt;(reducers);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;middleware&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;applyMiddleware&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;errorReporter, &lt;/span&gt;&lt;span&gt;// 注意：errorReporter 要放在其它 middlewares 之前&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;// epicMiddleware, routerMiddleware, etc...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;initialState&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; {}) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;createStore&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;rootReducer&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;initialState&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;middleware&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2 id=&quot;參考資料&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#參考資料&quot;&gt;參考資料&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/ngokevin/redux-raven-middleware&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;redux-raven-middleware&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/captbaritone/raven-for-redux&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;raven-for-redux&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/ezekielchentnik/redux-reporter&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;redux-reporter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/rishantagarwal/react-redux-persist-sentry-middleware&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;react-redux-persist-sentry-middleware&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/redux-observable/redux-observable/issues/94&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;RFC: default error handling of Epics&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>Tech</category></item><item><title>如何自動化 release 的流程？</title><link>https://blog.amowu.com/posts/2017-01-24-how-to-automate-js-release/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2017-01-24-how-to-automate-js-release/</guid><description>這篇文章會介紹如何使用 semantic-release 這個工具，自動化 Node.js (or JavaScript) 專案的版本號，以及 changelog 的 release 流程。</description><pubDate>Tue, 24 Jan 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;這篇文章會介紹如何使用 &lt;a href=&quot;https://github.com/semantic-release/semantic-release&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;semantic-release&lt;/a&gt; 這個工具，自動化 Node.js (or JavaScript) 專案的版本號，以及 changelog 的 release 流程。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;什麼是 semantic-release？&lt;/li&gt;
&lt;li&gt;為什麼要用 semantic-release？&lt;/li&gt;
&lt;li&gt;如何使用 semantic-release？&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;什麼是-semantic-release&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#什麼是-semantic-release&quot;&gt;什麼是 semantic-release？&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/semantic-release/semantic-release&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;semantic-release&lt;/a&gt; 可以自動完成下列這些事：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;當 code 被 push 或 merge PR 回 production branch (ex: master) 的時候&lt;/li&gt;
&lt;li&gt;CI build 被觸發，&lt;a href=&quot;https://github.com/semantic-release/semantic-release&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;semantic-release&lt;/a&gt; 會收集此次更新的所有 commit messages（需遵循 &lt;a href=&quot;https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;AngularJS Git Commit Message Conventions&lt;/a&gt; 的格式）&lt;/li&gt;
&lt;li&gt;自動根據 &lt;a href=&quot;http://semver.org/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;semver&lt;/a&gt; 的規則來更新 package.json 的 version，並建立 Git tag&lt;/li&gt;
&lt;li&gt;自動 publish 新版本的 package 到 &lt;a href=&quot;https://www.npmjs.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;npm registry&lt;/a&gt;（非必要）&lt;/li&gt;
&lt;li&gt;自動在 GitHub &lt;a href=&quot;https://help.github.com/articles/about-releases/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;releases&lt;/a&gt; 的頁面上，產生相對應的 changelog&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;所以簡單來講，&lt;a href=&quot;https://github.com/semantic-release/semantic-release&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;semantic-release&lt;/a&gt; 指的就是遵循 &lt;a href=&quot;http://semver.org/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;semver&lt;/a&gt; 的 release 流程。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://semver.org/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;semver&lt;/a&gt; 的介紹和好處可以在網路上找到很多文章，這裡就不贅述了，它的概念主要就是將版本號分成：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Major&lt;/strong&gt;.&lt;strong&gt;Minor&lt;/strong&gt;.&lt;strong&gt;Patch&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;例如 React 的 0.11.2、Vue.js 的 2.0.10 等，這三個數字各自代表：&lt;/p&gt;
&lt;h2 id=&quot;major&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#major&quot;&gt;Major&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;當你的 API 不兼容前一版本時（又稱 Breaking Change），major + 1，例如：1.x.x -&amp;gt; 2.0.0。&lt;/p&gt;
&lt;h2 id=&quot;minor&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#minor&quot;&gt;Minor&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;當你增加新 feature 的時候，並且不影響前一版本的 API，minor + 1，例如：x.6.x -&amp;gt; x.7.0。&lt;/p&gt;
&lt;h2 id=&quot;patch&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#patch&quot;&gt;Patch&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;當你修復 bug 的時候，並且不影響前一版本的 API，patch + 1，例如：x.x.9 -&amp;gt; x.x.10。&lt;/p&gt;
&lt;p&gt;如果專案有在執行 &lt;a href=&quot;http://danielkummer.github.io/git-flow-cheatsheet/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;git-flow&lt;/a&gt; 的話，minor 配合的就是 feature 的 release flow，patch 則是 hotfix 的 release flow。&lt;/p&gt;
&lt;h2 id=&quot;為什麼要用-semantic-release&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#為什麼要用-semantic-release&quot;&gt;為什麼要用 semantic-release？&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;先來看看如果我們想要完成上面列出的目的，傳統的 release 作法會是怎樣？&lt;/p&gt;
&lt;p&gt;首先，在你開發的過程中，無數次傳奇般的 code 被你給 commit 了上去：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$ git commit &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;m &lt;/span&gt;&lt;span&gt;&quot;blablabla…&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;在你準備好 release 這段即將改變世界的更新時，你必須先手動給 package.json 改版號，一般會用到 &lt;code&gt;npm version&lt;/code&gt; 的指令：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$ npm version [&amp;lt;&lt;/span&gt;&lt;span&gt;newversion&lt;/span&gt;&lt;span&gt;&amp;gt; | major | minor | patch | ...]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;再來，你會手動建立 Git tag，並且將這次的更新 push 到 production 分支（或是送 PR 給 reviewer merge）：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$ git push origin master &lt;/span&gt;&lt;span&gt;--tags&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;接下來，如果你還想將新版本發佈到 npm 騙星星的話：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$ npm publish&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;最後，為了更新 GitHub &lt;a href=&quot;https://help.github.com/articles/about-releases/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;releases&lt;/a&gt; 頁面來告知大家你更新了些什麼，你通常必須在 Git logs 的茫茫大海之中找出這次 commits 的內容，然後手動撰寫 changelog⋯⋯：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;583&quot; src=&quot;https://blog.amowu.com/_astro/cover.DwLI84cW_Z2iVsb.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/cover.DwLI84cW_1AhO0q.webp 640w, https://blog.amowu.com/_astro/cover.DwLI84cW_Z2iVsb.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;但是，如果我們用 &lt;a href=&quot;https://github.com/semantic-release/semantic-release&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;semantic-release&lt;/a&gt; 的話，只需要：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$ git commit &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;m &lt;/span&gt;&lt;span&gt;&quot;fix: blablabla...&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$ git push origin master&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2 id=&quot;如何使用-semantic-release&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#如何使用-semantic-release&quot;&gt;如何使用 semantic-release？&lt;/a&gt;&lt;/h2&gt;
&lt;h2 id=&quot;安裝-semantic-release&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#安裝-semantic-release&quot;&gt;安裝 semantic-release&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;安裝 semantic-release-cli，並在你的專案底下執行 &lt;code&gt;semantic-release-cli setup&lt;/code&gt;（注意：必須是 &lt;code&gt;npm init&lt;/code&gt; 和 &lt;code&gt;git init&lt;/code&gt; 過的專案）：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;npm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-g&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;semantic-release-cli&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;YOUR_PROJECT&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;semantic-release-cli&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;setup&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;回答完問題之後，package.json 會加入下面這段程式：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&quot;scripts&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;semantic-release&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;semantic-release pre &amp;amp;&amp;amp; npm publish &amp;amp;&amp;amp; semantic-release post&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;如果你不打算發佈專案到 &lt;a href=&quot;https://www.npmjs.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;npm registry&lt;/a&gt; 的話，可以移除 &lt;code&gt;npm publish&lt;/code&gt; 這段 code。&lt;/p&gt;
&lt;p&gt;另外，&lt;a href=&quot;https://github.com/semantic-release/semantic-release&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;semantic-release&lt;/a&gt; 也會幫你建立一份 .travis.yml 給 Travis CI 使用，如果你是用其它 CI 工具的話，可以參考這份配置來撰寫自己的腳本。&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;after_success:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- npm run semantic-release&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;注意：記得在 CI 服務上新增 &lt;code&gt;NPM_TOKEN&lt;/code&gt; 及 &lt;code&gt;GH_TOKEN&lt;/code&gt; 兩個環境變數給 &lt;a href=&quot;https://github.com/semantic-release/semantic-release&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;semantic-release&lt;/a&gt; 使用。&lt;/p&gt;
&lt;p&gt;到這裡，基本上已經完成前置作業了，更詳細的設定可以參考 &lt;a href=&quot;https://github.com/semantic-release/semantic-release&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;semantic-release&lt;/a&gt; 的 README。&lt;/p&gt;
&lt;h2 id=&quot;使用-semantic-release-的流程&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#使用-semantic-release-的流程&quot;&gt;使用 semantic-release 的流程&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;要讓 semantic-release 可以順利運作，其中最關鍵的是部份是團隊必須遵守 &lt;a href=&quot;https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;AngularJS Commit Message Conventions&lt;/a&gt; 的 commit message 撰寫規範。&lt;/p&gt;
&lt;p&gt;每一個 commit message 是由 header、body 和 footer 組成，其中 header 是由 type、scope 和 subject 組成：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&amp;gt;(&amp;lt;&lt;/span&gt;&lt;span&gt;scope&lt;/span&gt;&lt;span&gt;&amp;gt;): &amp;lt;&lt;/span&gt;&lt;span&gt;subject&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;footer&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;scope、body 和 footer 是可以根據情況省略的，例如只寫：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&amp;gt;: &amp;lt;&lt;/span&gt;&lt;span&gt;subject&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;這些關鍵字各自代表的作用在 &lt;a href=&quot;https://github.com/conventional-changelog/conventional-changelog-angular/blob/master/convention.md&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;conventional-changelog-angular&lt;/a&gt; 有很詳細的介紹，這裡就不一一贅述了，推薦閱讀 &lt;a href=&quot;http://www.ruanyifeng.com/blog/2016/01/commit_message_change_log.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Commit message 和 Change log 编写指南&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;有了 commit message conventions 之後，搭配一開始提到的 &lt;a href=&quot;http://semver.org/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;semver&lt;/a&gt;，我們就可以開始發揮 &lt;a href=&quot;https://github.com/semantic-release/semantic-release&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;semantic-release&lt;/a&gt;&lt;strong&gt;Changelog-driven Development&lt;/strong&gt; 的魔力了！&lt;/p&gt;
&lt;h3 id=&quot;patch-release&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#patch-release&quot;&gt;Patch Release&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$ git commit &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;m &lt;/span&gt;&lt;span&gt;&quot;fix(payment): 修正「大眾銀行」無法付款的問題&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;當上面這段帶有 fix 關鍵字的 commit 被 merge 回 master 之後，&lt;a href=&quot;https://github.com/semantic-release/semantic-release&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;semantic-release&lt;/a&gt; 就會自動更新 patch 版號（例如：x.x.9 -&amp;gt; x.x.10），並且自動出現在 GitHub &lt;a href=&quot;https://help.github.com/articles/about-releases/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Releases&lt;/a&gt; 的 Bug Fixes 之中。&lt;/p&gt;
&lt;h3 id=&quot;minor-or-featurerelease&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#minor-or-featurerelease&quot;&gt;Minor (or Feature) Release&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$ git commit &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;m &lt;/span&gt;&lt;span&gt;&quot;feat(video): 影片播放器新增「字幕位置」的選項&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;當上面這段帶有 feat 關鍵字的 commit 被 merge 回 master 之後，&lt;a href=&quot;https://github.com/semantic-release/semantic-release&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;semantic-release&lt;/a&gt; 就會自動更新 minor 版號（例如：x.6.x -&amp;gt; x.7.0），並且自動出現在 GitHub &lt;a href=&quot;https://help.github.com/articles/about-releases/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Releases&lt;/a&gt; 的 Features 之中&lt;/p&gt;
&lt;h3 id=&quot;major-or-breaking-release&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#major-or-breaking-release&quot;&gt;Major (or Breaking) Release&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;commit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-m&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;pref(...): blablabla...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;BREAKING CHANGE: blablabla...&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;當上面這段帶有 BREAKING CHANGE 關鍵字的 commit 被 merge 回 master 之後，&lt;a href=&quot;https://github.com/semantic-release/semantic-release&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;semantic-release&lt;/a&gt; 就會自動更新 major 版號（例如：1.x.x -&amp;gt; 2.0.0）， 並且加入 GitHub &lt;a href=&quot;https://help.github.com/articles/about-releases/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Releases&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id=&quot;結論&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#結論&quot;&gt;結論&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/semantic-release/semantic-release&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;semantic-release&lt;/a&gt; 很巧妙地結合了 &lt;a href=&quot;http://semver.org/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;semver&lt;/a&gt; 和 &lt;a href=&quot;https://github.com/conventional-changelog/conventional-changelog-angular/blob/master/convention.md&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;commit message conventions&lt;/a&gt; 來完成 Git tag、npm version 和 GitHub releases changelog 的自動化&lt;/li&gt;
&lt;li&gt;學習了 Changelog-driven Development 的思維方式&lt;/li&gt;
&lt;/ol&gt;</content:encoded><category>Tech</category></item><item><title>Amazon S3 正確處理 HTML5 History 路由問題</title><link>https://blog.amowu.com/posts/2016-06-06-s3-static-website-hosting-route-with-html5-history-pushstate/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2016-06-06-s3-static-website-hosting-route-with-html5-history-pushstate/</guid><description>如果你是使用 Angular、React 或是 Vue 來開發 SPA（單頁面應用），並且放在 Amazon S3 Static Website Hosting 上的話，那麼你會碰到 URL routing 的問題。</description><pubDate>Mon, 06 Jun 2016 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;如果你是使用 Angular、React 或是 Vue 來開發 SPA（單頁面應用），並且放在 &lt;a href=&quot;http://docs.aws.amazon.com/AmazonS3/latest/dev/WebsiteHosting.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Amazon S3 Static Website Hosting&lt;/a&gt; 上的話，那麼你會碰到 URL routing 的問題。&lt;/p&gt;
&lt;p&gt;一般 &lt;a href=&quot;https://github.com/reactjs/react-router&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;react-router&lt;/a&gt; 或 &lt;a href=&quot;https://github.com/vuejs/vue-router&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;vue-router&lt;/a&gt; 都預設使用 hash 的方式來處理 SPA 的路由：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;http://domain.com/#!/paths&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;如果你不喜歡 &lt;code&gt;#!/&lt;/code&gt; 的顯示方式，可以使用 HTML5 的 &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/History&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;History API&lt;/a&gt;，這樣就能像一般網站那樣顯示 URL：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;http://domain.com/paths&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;但是使用 HTML5 History API 時，通常必須搭配 server 端正確的 &lt;a href=&quot;http://readystate4.com/2012/05/17/nginx-and-apache-rewrite-to-support-html5-pushstate/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;路由配置&lt;/a&gt; 才能防止出現 404 Not Found 的情形。&lt;/p&gt;
&lt;p&gt;遺憾的是，在 Amazon S3 Static Website Hosting 上，你無法更動 Apache 或 Nginx 的配置，所以需要靠其它方式來解決問題。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用 S3 的 Redirection Rules&lt;/li&gt;
&lt;li&gt;使用 CloudFront 的 Custom Error Response&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;使用-s3-的-redirection-rules&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#使用-s3-的-redirection-rules&quot;&gt;使用 S3 的 Redirection Rules&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Amazon S3 Static Website Hosting 提供了 &lt;strong&gt;Edit Redirection Rules&lt;/strong&gt; 的選項，我們可以輕鬆使用這段程式碼將所有 &lt;code&gt;domain.com/#!/paths&lt;/code&gt; 所產生的 404，全部重新導向至根路徑：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;RoutingRules&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;RoutingRule&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Condition&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;HttpErrorCodeReturnedEquals&lt;/span&gt;&lt;span&gt;&amp;gt;404&amp;lt;/&lt;/span&gt;&lt;span&gt;HttpErrorCodeReturnedEquals&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;Condition&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Redirect&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;HostName&lt;/span&gt;&lt;span&gt;&amp;gt;你的網域（例：domain.com）&amp;lt;/&lt;/span&gt;&lt;span&gt;HostName&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;ReplaceKeyPrefixWith&lt;/span&gt;&lt;span&gt;&amp;gt;#!/&amp;lt;/&lt;/span&gt;&lt;span&gt;ReplaceKeyPrefixWith&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;Redirect&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;RoutingRule&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;RoutingRules&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;630&quot; height=&quot;375&quot; src=&quot;https://blog.amowu.com/_astro/cover.FQwRja9z_Z2gDSfm.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/cover.FQwRja9z_Z2gDSfm.webp 630w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;然後在你的 app 開始處，加入這段 JavaScript 來將 &lt;code&gt;/#!/paths&lt;/code&gt; rewrite 成 &lt;code&gt;/paths&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;!-- index.html --&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;history.pushState({}, &quot;index.html&quot;, location.hash.substring(2));&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;History API 在各個瀏覽器上的使用方式都不盡相同，這裡我們就省略了判斷的程式碼。&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;使用-cloudfront-的-custom-errorresponse&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#使用-cloudfront-的-custom-errorresponse&quot;&gt;使用 CloudFront 的 Custom Error Response&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;如果你有使用 Amazon CloudFront 來幫 &lt;a href=&quot;http://docs.aws.amazon.com/gettingstarted/latest/swh/getting-started-create-cfdist.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;S3 Static Website Hosting 加入 CDN&lt;/a&gt; 的話，那解決方式就更簡單了：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;前往你的 CloudFront Distribution&lt;/li&gt;
&lt;li&gt;選擇 &lt;strong&gt;Error Pages&lt;/strong&gt; 標籤，點選 &lt;strong&gt;Create Custom Error Response&lt;/strong&gt; 建立自訂的錯誤回應&lt;/li&gt;
&lt;li&gt;HTTP Error Code 選擇 &lt;strong&gt;404: Not Found&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Error Caching Minimum TTL 輸入 &lt;strong&gt;0&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Customize Error Response 選擇 &lt;strong&gt;Yes&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Response Page Path 輸入 &lt;strong&gt;/index.html&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;HTTP Response Code 選擇 &lt;strong&gt;200: OK&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;點選 Create 完成&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;530&quot; height=&quot;290&quot; src=&quot;https://blog.amowu.com/_astro/image1.DLjwuS5-_ZPKoz4.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image1.DLjwuS5-_ZPKoz4.webp 530w&quot; /&gt;&lt;/p&gt;</content:encoded><category>Tech</category></item><item><title>如何正確處理 AWS API Gateway 的 Lambda Error Status</title><link>https://blog.amowu.com/posts/2016-03-09-error-handling-in-aws-api-gateway-with/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2016-03-09-error-handling-in-aws-api-gateway-with/</guid><description>翻譯自原文 Error handling in AWS API Gateway with Lambda</description><pubDate>Wed, 09 Mar 2016 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;翻譯自原文 &lt;a href=&quot;https://medium.com/@pahud/error-handling-in-aws-api-gateway-with-lambda-28fb86b3ea1e&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Error handling in AWS API Gateway with Lambda&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;這篇文章會介紹如何設置 AWS API Gateway 正確處理 Lambda 返回的 HTTP 錯誤狀態碼。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;更新：2016.03.12
&lt;a href=&quot;https://github.com/claudiajs/claudia&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;claudia&lt;/a&gt; 可以自動化解決這個問題，詳細可以參考 &lt;a href=&quot;http://www.infoq.com/cn/news/2016/03/microservices-lambda-claudiajs&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;這篇&lt;/a&gt; 文章。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;本文假設讀者已經知道如何利用 AWS API Gateway 和 Lambda 建立 REST API，詳細可參考 &lt;a href=&quot;http://docs.aws.amazon.com/apigateway/latest/developerguide/getting-started.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Create API Gateway API for Lambda Functions&lt;/a&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;假設你的 Lambda function 錯誤處理如下：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;I am a AWS Lambda function&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;exports.handler = function(event, context) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 一般使用 context.fail 來返回 Lambda function 錯誤&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;context.fail(JSON.stringify({status:&apos;fail&apos;, reason:&apos;some reason&apos;, foo:&apos;bar&apos;}));&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;但是 API Gateway 返回的結果會是 &lt;strong&gt;HTTP 200&lt;/strong&gt;：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;HTTP/1.1 200 OK&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;errorMessage&quot;: &quot;{\&quot;status\&quot;:\&quot;fail\&quot;,\&quot;reason\&quot;:\&quot;some reason\&quot;,\&quot;foo\&quot;:\&quot;bar\&quot;}&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;我們希望的結果是：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;HTTP Status &lt;strong&gt;400&lt;/strong&gt; Bad request&lt;/li&gt;
&lt;li&gt;只顯示 errorMessage 的 JSON 值&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;1-新增-http-status-400-methodresponse&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#1-新增-http-status-400-methodresponse&quot;&gt;1. 新增 HTTP Status 400 Method Response&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;前往 API Gateway Console&lt;/li&gt;
&lt;li&gt;進入 Method Execution&lt;/li&gt;
&lt;li&gt;進入 Method Response&lt;/li&gt;
&lt;li&gt;點選 Add Response&lt;/li&gt;
&lt;li&gt;輸入 HTTP Status &lt;strong&gt;400&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;點選 Add Response Model&lt;/li&gt;
&lt;li&gt;輸入 Content type &lt;strong&gt;application/json&lt;/strong&gt;、Models &lt;strong&gt;Error&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;454&quot; src=&quot;https://blog.amowu.com/_astro/cover.SMrTGqVy_ZDAVQj.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/cover.SMrTGqVy_Z28bMRz.webp 640w, https://blog.amowu.com/_astro/cover.SMrTGqVy_ZDAVQj.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;2-新增-lambda-error-regex-integration-response&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#2-新增-lambda-error-regex-integration-response&quot;&gt;2. 新增 Lambda Error Regex Integration Response&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;進入 Integration Response&lt;/li&gt;
&lt;li&gt;點選 Add integration response&lt;/li&gt;
&lt;li&gt;輸入 Lambda Error Regex &lt;code&gt;.*status.*fail.*&lt;/code&gt;、Method response status &lt;strong&gt;400&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;526&quot; src=&quot;https://blog.amowu.com/_astro/image1.D_gpz2JN_Z1NOIJW.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image1.D_gpz2JN_Z1RYqft.webp 640w, https://blog.amowu.com/_astro/image1.D_gpz2JN_Z1NOIJW.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;3-設置-mapping-templates&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#3-設置-mapping-templates&quot;&gt;3. 設置 Mapping Templates&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;展開 Mapping Templates&lt;/li&gt;
&lt;li&gt;點選 Add mapping template&lt;/li&gt;
&lt;li&gt;輸入 Content-Type &lt;strong&gt;application/json&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;點選 Output passthrough 並改成 Mapping Templates&lt;/li&gt;
&lt;li&gt;輸入以下程式碼：&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;421&quot; src=&quot;https://blog.amowu.com/_astro/image2.Dc83zXmq_Z2nD5hM.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image2.Dc83zXmq_4GUdz.webp 640w, https://blog.amowu.com/_astro/image2.Dc83zXmq_Z2nD5hM.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;4-部署api&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#4-部署api&quot;&gt;4. 部署 API&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;記得點選 &lt;strong&gt;Deploy API&lt;/strong&gt; 完成 API 的更新，然後測試返回結果是否為 &lt;strong&gt;HTTP 400&lt;/strong&gt;。&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;HTTP/1.1 400 Bad Request&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;foo&quot;: &quot;bar&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;reason&quot;: &quot;some reason&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;status&quot;: &quot;fail&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;</content:encoded><category>Tech</category></item><item><title>解決 Universal 架構的 CSS Modules 問題</title><link>https://blog.amowu.com/posts/2015-12-05-how-to-render-css-modules-in-universal-server/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2015-12-05-how-to-render-css-modules-in-universal-server/</guid><description>在 Isomorphic JavaScript 應用中實作 CSS Modules 時，解決 Node.js 無法 import CSS 以及 Server/Client 渲染結果不一致的問題。</description><pubDate>Sat, 05 Dec 2015 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;最近在使用 &lt;a href=&quot;https://facebook.github.io/react/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;React&lt;/a&gt; 和 &lt;a href=&quot;https://github.com/rackt/redux&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Redux&lt;/a&gt; 建構一個 &lt;a href=&quot;http://isomorphic.net/javascript&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Isomorphic JavaScript&lt;/a&gt; （Universal）應用。&lt;/p&gt;
&lt;p&gt;但是在實作 &lt;a href=&quot;https://github.com/css-modules/css-modules&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;CSS Modules&lt;/a&gt; 的時候，會碰上兩個問題：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Server-side 的 Node.js 無法 import &lt;code&gt;*.css&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;如果改成使用 &lt;code&gt;process.env.IS_BROWSER&lt;/code&gt; 來判斷只在 Client-side import，那又會碰到 Server-side 與 Client-side render 出來的 DOM 結果不一致的問題。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;解決方式：&lt;/p&gt;
&lt;p&gt;安裝 &lt;a href=&quot;https://github.com/css-modules/css-modules-require-hook&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;css-modules-require-hook&lt;/a&gt; 這個套件：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;npm install css-modules-require-hook --save-dev&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;修改 webpack config 的 css-loader 設定：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame has-title&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;webpack.config.js&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;// ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;module&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;loaders&lt;/span&gt;&lt;span&gt;: [{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;test:&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt;\.&lt;/span&gt;&lt;span&gt;css&lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;loader: &lt;/span&gt;&lt;span&gt;&apos;style-loader!css-loader?modules&amp;amp;localIdentName=[name]__[local]___[hash:base64:5]!!postcss-loader&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;// ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;在 Node.js 的 main file 加入 css-modules-require-hook：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame has-title&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;server.js&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;css-modules-require-hook&apos;&lt;/span&gt;&lt;span&gt;)({&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;generateScopedName: &lt;/span&gt;&lt;span&gt;&apos;[name]__[local]___[hash:base64:5]&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;// ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;注意 &lt;code&gt;generateScopedName&lt;/code&gt; 的命名格式，必須與 css-loader 的 &lt;code&gt;localIdentName&lt;/code&gt; 保持一致。&lt;/p&gt;</content:encoded><category>Tech</category></item><item><title>Serverless！使用 AWS 開發 Slack Slash Commands</title><link>https://blog.amowu.com/posts/2015-12-05-serverless-slack-slash-commands/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2015-12-05-serverless-slack-slash-commands/</guid><description>本文介紹如何使用 AWS Lambda &amp; AWS API Gateway 搭建一個不需要伺服器的環境，提供 Slack Slash Commands 查詢豆瓣電影。</description><pubDate>Sat, 05 Dec 2015 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;本文介紹如何使用 &lt;a href=&quot;https://aws.amazon.com/lambda/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;AWS Lambda&lt;/a&gt; &amp;amp; &lt;a href=&quot;https://aws.amazon.com/api-gateway/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;AWS API Gateway&lt;/a&gt; 搭建一個不需要伺服器的環境，提供 &lt;a href=&quot;https://api.slack.com/slash-commands&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Slack Slash Commands&lt;/a&gt; 查詢豆瓣電影。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;640&quot; height=&quot;396&quot; src=&quot;https://blog.amowu.com/_astro/image1.CgjIQhLm_Z2rIS6F.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image1.CgjIQhLm_Z2rIS6F.webp 640w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;這篇文章使用到的技術：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://api.slack.com/slash-commands&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Slack Slash Commands&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://aws.amazon.com/lambda/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;AWS Lambda&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://aws.amazon.com/api-gateway/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;AWS API Gateway&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developers.douban.com/wiki/?title=movie_v2&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;豆瓣電影 API&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;閱讀這篇文章需要具備什麼能力：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Node.js 的基本能力&lt;/li&gt;
&lt;li&gt;Amazon Web Services 的基本操作&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;接下來我會逐步講解這些東西：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Slack Slash Commands 的運作機制&lt;/li&gt;
&lt;li&gt;建立一個簡單的 AWS Lambda function&lt;/li&gt;
&lt;li&gt;建立一個簡單的 AWS API Gateway 執行 Lambda function&lt;/li&gt;
&lt;li&gt;使用 Lambda 呼叫豆瓣電影 API&lt;/li&gt;
&lt;li&gt;測試 AWS API Gateway&lt;/li&gt;
&lt;li&gt;將 API Gateway endpoint 加入至 Slack Slash Command&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;slack-slash-commands的運作機制&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#slack-slash-commands的運作機制&quot;&gt;Slack Slash Commands 的運作機制&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;640&quot; height=&quot;178&quot; src=&quot;https://blog.amowu.com/_astro/image2.C7AyjyrZ_Z1kXrAO.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image2.C7AyjyrZ_Z1kXrAO.webp 640w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;當你在 Slack channel 輸入 &lt;code&gt;/movie 權力的遊戲&lt;/code&gt;，Slack 會發出一個 &lt;code&gt;content-type&lt;/code&gt; Header 設為 &lt;code&gt;application/x-www-form-urlencoded&lt;/code&gt; 的 HTTP POST 請求，格式如下：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;token&lt;/span&gt;&lt;span&gt;=YOUR_SLASH_COMMAND_TOKEN&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;team_id&lt;/span&gt;&lt;span&gt;=YOUR_TEAM_ID&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;team_domain&lt;/span&gt;&lt;span&gt;=YOUR_TEAM_DOMAIN&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;channel_id&lt;/span&gt;&lt;span&gt;=YOUR_CHANNEL_ID&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;channel_name&lt;/span&gt;&lt;span&gt;=YOUR_CHANNEL_NAME&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;user_id&lt;/span&gt;&lt;span&gt;=YOUR_USER_ID&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;user_name&lt;/span&gt;&lt;span&gt;=YOUR_USER_NAME&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;command&lt;/span&gt;&lt;span&gt;=/movie&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;text&lt;/span&gt;&lt;span&gt;=權力的遊戲&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;response_url&lt;/span&gt;&lt;span&gt;=YOUR_HOOK_URL&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;然後 Slack 需要收到的 JSON 回應格式如下（詳見 Attachments）：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;response_type&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;in_channel&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;attachments&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;&quot;title&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;權力的遊戲 第五季&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;&quot;title_link&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;http://movie.douban.com/subject/25826612/&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;&quot;text&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Game of Thrones&lt;/span&gt;&lt;span&gt;\n&lt;/span&gt;&lt;span&gt;2015年&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;&quot;fields&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;          &lt;/span&gt;&lt;span&gt;&quot;title&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;導演&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;          &lt;/span&gt;&lt;span&gt;&quot;value&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Michael Slovis&lt;/span&gt;&lt;span&gt;\n&lt;/span&gt;&lt;span&gt;...&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;          &lt;/span&gt;&lt;span&gt;&quot;short&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}, &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;&quot;thumb_url&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;https://img1.doubanio.com/view/movie_poster_cover/ipst/public/p2230256732.jpg&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;responsetype: inchannel&lt;/code&gt; 表示 channel 內的使用者都可以看見，若只想給使用命令的本人看的話可以改成 &lt;code&gt;ephemeral&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Slack Slash Commands 有 3000 ms 的 response timeout 限制&lt;/p&gt;
&lt;h2 id=&quot;建立一個簡單的-aws-lambdafunction&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#建立一個簡單的-aws-lambdafunction&quot;&gt;建立一個簡單的 AWS Lambda function&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;640&quot; height=&quot;178&quot; src=&quot;https://blog.amowu.com/_astro/image3.uhyIN12s_Z2c7M9C.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image3.uhyIN12s_Z2c7M9C.webp 640w&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;前往 AWS Lambda&lt;/li&gt;
&lt;li&gt;點選 Create a Lambda function&lt;/li&gt;
&lt;li&gt;填寫 Configure function，例：slackFunction&lt;/li&gt;
&lt;li&gt;Runtime 選擇 Node.js&lt;/li&gt;
&lt;li&gt;Code entry type 選擇 Edit code inline，輸入簡單的測試程式碼：&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;exports&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;handler&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;event&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;context&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;context.&lt;/span&gt;&lt;span&gt;succeed&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;你好世界!&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;Role 使用 lambda&lt;em&gt;basic&lt;/em&gt;execution&lt;/li&gt;
&lt;li&gt;點選 Next 完成建立&lt;/li&gt;
&lt;li&gt;點選 Test 測試結果：&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;你好世界&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2 id=&quot;建立一個簡單的-aws-api-gateway-執行-lambdafunction&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#建立一個簡單的-aws-api-gateway-執行-lambdafunction&quot;&gt;建立一個簡單的 AWS API Gateway 執行 Lambda function&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;640&quot; height=&quot;178&quot; src=&quot;https://blog.amowu.com/_astro/image4.Chzmb8QU_Z1bgQzb.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image4.Chzmb8QU_Z1bgQzb.webp 640w&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;前往 AWS API Gateway&lt;/li&gt;
&lt;li&gt;點選 New API&lt;/li&gt;
&lt;li&gt;填寫 API name，例：Slack API&lt;/li&gt;
&lt;li&gt;點選 Create API 完成新增&lt;/li&gt;
&lt;li&gt;點選 Create Resource&lt;/li&gt;
&lt;li&gt;填寫 Resource Name &amp;amp; Resource Path，例：movie&lt;/li&gt;
&lt;li&gt;點選 /movie&lt;/li&gt;
&lt;li&gt;點選 Create Method&lt;/li&gt;
&lt;li&gt;選擇 POST&lt;/li&gt;
&lt;li&gt;Integration type 選擇 Lambda Function&lt;/li&gt;
&lt;li&gt;選擇 Lambda Region&lt;/li&gt;
&lt;li&gt;填寫 Lambda Function，例：slackFunction&lt;/li&gt;
&lt;li&gt;點選 Save 完成建立&lt;/li&gt;
&lt;li&gt;點選 Test 測試結果：&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;你好世界&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2 id=&quot;使用-lambda-呼叫豆瓣電影api&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#使用-lambda-呼叫豆瓣電影api&quot;&gt;使用 Lambda 呼叫豆瓣電影 API&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;640&quot; height=&quot;178&quot; src=&quot;https://blog.amowu.com/_astro/image5.Cc-rtCrk_Z1Tas2c.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image5.Cc-rtCrk_Z1Tas2c.webp 640w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;豆瓣電影搜索 API 的格式如下：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;GET&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;https://api.douban.com/v2/movie/search?q={text}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;例：&lt;code&gt;https://api.douban.com/v2/movie/search?q=權力的遊戲&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;count&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;20&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;start&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;total&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;130&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;subjects&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;&quot;rating&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;&quot;genres&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;&quot;collect_count&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;47770&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;&quot;casts&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;}, &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;&quot;title&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;权力的游戏 第五季&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;&quot;original_title&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Game of Thrones&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;&quot;subtype&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;tv&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;&quot;directors&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;}, &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;&quot;year&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;2015&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;&quot;images&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;&quot;alt&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;http://movie.douban.com/subject/25826612/&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;&quot;id&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;25826612&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}, &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;title&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;搜索 &lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;權力的遊戲&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt; 的结果&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;取代 Edit code inline，在本地建立一個 Node.js Lambda project：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$ npm init&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;加入一個支持 promise 的 XMLHttpRequest 庫：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$ npm install request&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;promise &lt;/span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt;save&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;新增 index.js：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; rp &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;request-promise&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;exports&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;handler&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;event&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;context&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;// 從傳進來的參數之中提取要搜尋的字串&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; text &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; event.text &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; event.text.&lt;/span&gt;&lt;span&gt;trim&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 向豆瓣 API 發出 HTTP GET 請求&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;rp(&apos;https://api.douban.com/v2/movie/search?q=&apos; + text)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.then(function(data) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 回傳成功的結果&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;context.succeed(data);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}).catch(function(error) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 回傳失敗的結果&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;context.fail(error);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;將檔案壓縮成 lambda.zip：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;./index.js&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;./node_module/request-promise&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;回到 AWS Lambda&lt;/li&gt;
&lt;li&gt;點選之前建立的 function，例：slackFunction&lt;/li&gt;
&lt;li&gt;將 Code entry type 從 Edit code inline 改為 Upload a .ZIP file&lt;/li&gt;
&lt;li&gt;上傳 lambda.zip&lt;/li&gt;
&lt;li&gt;點選 Actions &amp;gt; Configure test event，加入測試用的請求&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;text&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;權力的遊戲&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;點選 Test 測試豆瓣回應的結果：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;// ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;subjects&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;// ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;&quot;title&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;权力的游戏 第五季&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;// ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}, &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;title&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;搜索 &lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;權力的遊戲&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt; 的结果&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2 id=&quot;測試-aws-apigateway&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#測試-aws-apigateway&quot;&gt;測試 AWS API Gateway&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;640&quot; height=&quot;178&quot; src=&quot;https://blog.amowu.com/_astro/image4.Chzmb8QU_Z1bgQzb.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image4.Chzmb8QU_Z1bgQzb.webp 640w&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;回到 AWS API Gateway&lt;/li&gt;
&lt;li&gt;點選 /movie 的 POST method&lt;/li&gt;
&lt;li&gt;點選 Test，並在 Request Body 加入測試用的請求：&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;text&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;權力的遊戲&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;點選 Test 測試 Lambda 回應的豆瓣結果：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;// ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;subjects&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;// ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;&quot;title&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;权力的游戏 第五季&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;// ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}, &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;title&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;搜索 &lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;權力的遊戲&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt; 的结果&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2 id=&quot;將-api-gateway-endpoint-加入至-slack-slashcommand&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#將-api-gateway-endpoint-加入至-slack-slashcommand&quot;&gt;將 API Gateway endpoint 加入至 Slack Slash Command&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;640&quot; height=&quot;178&quot; src=&quot;https://blog.amowu.com/_astro/image7.BTWldXks_Z58nNz.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image7.BTWldXks_Z58nNz.webp 640w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;因為 Slack 發送的請求 Header 格式是 &lt;code&gt;application/www-form-urlencoded&lt;/code&gt;，所以需要在 AWS API Gateway 之中將它轉換成為 &lt;code&gt;application/json&lt;/code&gt; 格式：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;點選 /movie 的 POST method&lt;/li&gt;
&lt;li&gt;點選 Method Execution&lt;/li&gt;
&lt;li&gt;點選 Integration Request&lt;/li&gt;
&lt;li&gt;點選 Mapping Templates&lt;/li&gt;
&lt;li&gt;點選 Add mapping template&lt;/li&gt;
&lt;li&gt;Content-Type 填寫 application/www-form-urlencoded&lt;/li&gt;
&lt;li&gt;將 Input passthrough 改成 Mapping template&lt;/li&gt;
&lt;li&gt;貼上 Ryan Ray 提供的 template &lt;a href=&quot;https://gist.github.com/ryanray/668022ad2432e38493df&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;gist&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Save&lt;/li&gt;
&lt;li&gt;最後，一定要記得把 API Gateway 部署給外部使用：&lt;/li&gt;
&lt;li&gt;點選 Deploy API&lt;/li&gt;
&lt;li&gt;Deployment stage 選擇 New Stage&lt;/li&gt;
&lt;li&gt;填寫 Stage name，例：development&lt;/li&gt;
&lt;li&gt;點選 Deploy 完成發佈&lt;/li&gt;
&lt;li&gt;然後會得到一個 Invoke URL 格式如下：&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;https://&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;hash&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;.execute-api.&lt;/span&gt;&lt;span&gt;{region}&lt;/span&gt;&lt;span&gt;.amazonaws.com/&lt;/span&gt;&lt;span&gt;{stage}&lt;/span&gt;&lt;span&gt;/movie&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;接下來的步驟，把 AWS API Gateway endpoint 整合進 Slack：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;前往 &lt;a href=&quot;https://YOURTEAMDOMAIN.slack.com/apps/manage&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://YOUR&lt;em&gt;TEAM&lt;/em&gt;DOMAIN.slack.com/apps/manage&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;在 Search app directory 搜尋框輸入 Slash Commands 並進入&lt;/li&gt;
&lt;li&gt;點選 Add Configuration&lt;/li&gt;
&lt;li&gt;填寫 Choose a Command，例：/movie&lt;/li&gt;
&lt;li&gt;點選 Add Slash Command Integration&lt;/li&gt;
&lt;li&gt;填寫 URL，貼上 AWS API Gateway Invoke URL&lt;/li&gt;
&lt;li&gt;Method 選擇 POST&lt;/li&gt;
&lt;li&gt;點選 Save Integration 完成新增&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;640&quot; height=&quot;178&quot; src=&quot;https://blog.amowu.com/_astro/image8.BYQcWiEV_1EeNzN.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image8.BYQcWiEV_1EeNzN.webp 640w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;最後一個步驟，更新 Lambda function，讓它可以處理 Slack 的請求與回應：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rp&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;request-promise&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;exports.handler &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt;(event, context) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/**&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* event 會收到來自 AWS API Gateway 轉換過的 Slack POST JOSN&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*   token=YOUR_SLASH_COMMAND_TOKEN&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*   team_id=YOUR_TEAM_ID&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*   team_domain=YOUR_TEAM_DOMAIN&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*   channel_id=YOUR_CHANNEL_ID&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*   channel_name=YOUR_CHANNEL_NAME&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*   user_id=YOUR_USER_ID&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*   user_name=YOUR_USER_NAME&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*   command=/movie&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*   text=權力的遊戲&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*   response_url=YOUR_HOOK_URL&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;(event.token &lt;/span&gt;&lt;span&gt;!==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;YOUR_SLASH_COMMAND_TOKEN&apos;&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return context.&lt;/span&gt;&lt;span&gt;fail&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;未經授權的請求&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;var text &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; event.text &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; event.text.&lt;/span&gt;&lt;span&gt;trim&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 向豆瓣 API 發出 HTTP GET 請求&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;rp(&apos;https://api.douban.com/v2/movie/search?q=&apos; + text)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.then(function(data) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 提取第一筆電影的結果&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;var subject = data.subjects[0];&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 將豆瓣 API 返回的結果包裝成 Slack 支持的格式&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;var result = {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;response_type&quot;: &quot;in_channel&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;attachments&quot;: [{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;title&quot;: subject.title,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;title_link&quot;: subject.alt,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;text&quot;: subject.original_title+&quot;\n&quot;+subject.year+&quot;年&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;fields&quot;: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;title&quot;: &quot;導演&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;value&quot;: subject.directors[0].name,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;short&quot;: true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;thumb_url&quot;: subject.images.small&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 回傳結果&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;context.succeed(result);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}).catch(function(error) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 回傳失敗的結果&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;context.fail(error);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;重新壓縮 lambda.zip 然後上傳&lt;/li&gt;
&lt;li&gt;在 Slack channel 輸入 /movie 測試結果&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;總結&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#總結&quot;&gt;總結&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;熟悉之後，稍微修改一下，把豆瓣 API 換成其它 API，又可以誕生出更多有趣的 Slash Commands！&lt;/li&gt;
&lt;li&gt;2015/12/30: 更新範例 &lt;a href=&quot;https://github.com/amowu/slack-lambda-google&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;GitHub repo Google 大神&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;因為 Slack 有 3 秒的回應限制，所以 API 不穩的話，常常容易發生 timeout，解決方法可以參考 Slack 提供的方式&lt;a href=&quot;https://api.slack.com/slash-commands&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Delayed responses and multiple responses&lt;/a&gt;，或是提高 Lambda 的 Memory（建議 512 以上），也可使用 AWS SNS 之類的服務處理非同步的 Lambda invoke。&lt;/li&gt;
&lt;li&gt;特別感謝 Ryan Ray 寫的文章 &lt;a href=&quot;http://www.ryanray.me/serverless-slack-integrations&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Serverless Slack Integrations with node.js, AWS Lambda, and AWS API Gateway&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>Tech</category></item><item><title>如何在發布自己的 NPM package 之前打包檔案</title><link>https://blog.amowu.com/posts/2015-12-02-prepublish-npm-package/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2015-12-02-prepublish-npm-package/</guid><description>最近 Bower 宣布停止維護，所以許多前端的 packages 都移到了 NPM 上，甚至連 Grunt 和 Gulp 這類 build tools 都有要被 NPM scripts 取代的趨勢。</description><pubDate>Wed, 02 Dec 2015 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;最近 &lt;a href=&quot;http://bower.io/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Bower&lt;/a&gt; 宣布停止維護，所以許多前端的 packages 都移到了 &lt;a href=&quot;https://www.npmjs.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;NPM&lt;/a&gt; 上，甚至連 &lt;a href=&quot;http://gruntjs.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Grunt&lt;/a&gt; 和 &lt;a href=&quot;http://gulpjs.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Gulp&lt;/a&gt; 這類 build tools 都有要被 NPM scripts 取代的趨勢。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;320&quot; height=&quot;174&quot; src=&quot;https://blog.amowu.com/_astro/image1.gIHF32Yc_1QDU7D.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image1.gIHF32Yc_1QDU7D.webp 320w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;這篇文章主要是紀錄怎麼在發布自己的 package 之前，打包好需要的檔案。&lt;/p&gt;
&lt;p&gt;假設你會使用 &lt;a href=&quot;http://gruntjs.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Grunt&lt;/a&gt; 或 &lt;a href=&quot;http://gulpjs.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Gulp&lt;/a&gt; 將所有原始碼打包存成一支檔案，例如 &lt;code&gt;dist/build.js&lt;/code&gt;，但是因為通常我們不會把 &lt;code&gt;dist&lt;/code&gt; 這類輸出文件放進版本控制，所以要在每次發布新版本的時候動態打包。&lt;/p&gt;
&lt;p&gt;方法是在 &lt;code&gt;package.json&lt;/code&gt; 的 &lt;code&gt;script&lt;/code&gt; 底下新增一個 &lt;code&gt;prepublish&lt;/code&gt; 的腳本，然後把你的打包命令寫在這裡：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame has-title&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;package.json&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;script&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;prepublish&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;...&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;// grunt or gulp&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;接下來是透過 &lt;code&gt;main&lt;/code&gt; 指定程式的進入位置，讓其他人在使用的時候可以正常的 &lt;code&gt;require&lt;/code&gt; 或 &lt;code&gt;import&lt;/code&gt; 你的 package。&lt;/p&gt;
&lt;p&gt;預設的檔案通常是 &lt;code&gt;index.js&lt;/code&gt;，這裡我們把它改成剛剛打包完成的 &lt;code&gt;dist/buid.js&lt;/code&gt; ：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame has-title&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;package.json&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;main&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;dist/build.js&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;最後也是最重要的部分，記得在專案根目錄新增一份 &lt;code&gt;.npmignore&lt;/code&gt; 的空檔案，因為如果沒有它，NPM 在發布的時候會參考 &lt;code&gt;.gitignore&lt;/code&gt; 的清單來決定哪些檔案不能被發布，而通常 &lt;code&gt;dist&lt;/code&gt; 就會在這裡面，所以要將它從 &lt;code&gt;.npmignore&lt;/code&gt; 裡面移除。&lt;/p&gt;
&lt;p&gt;都準備完成之後就可以發布了，如果你還沒有 NPM 的帳號，可以註冊一個：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;npm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;adduser&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;然後發布新版本：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;npm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;publish&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;記得更新 &lt;code&gt;package.json&lt;/code&gt; 的 &lt;code&gt;version&lt;/code&gt; 才能發布。&lt;/p&gt;
&lt;p&gt;發布成功之後前往 &lt;a href=&quot;http://npmjs.com/package/name&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;strong&gt;http://npmjs.com/package/name&lt;/strong&gt;&lt;/a&gt; 看結果：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;700&quot; src=&quot;https://blog.amowu.com/_astro/cover.Bjy1N4qO_ZOoQ1b.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/cover.Bjy1N4qO_ODNbJ.webp 640w, https://blog.amowu.com/_astro/cover.Bjy1N4qO_ZOoQ1b.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;完整專案可以參考我的 &lt;a href=&quot;https://github.com/amowu/phaser-shim&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;GitHub&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;參考文章&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#參考文章&quot;&gt;參考文章&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.npmjs.com/getting-started/publishing-npm-packages&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Publishing npm packages&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>Tech</category></item><item><title>Code Coverage with CircleCI + Codecov</title><link>https://blog.amowu.com/posts/2015-11-02-code-coverage-with-circleci-codecov/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2015-11-02-code-coverage-with-circleci-codecov/</guid><description>最近的案子用到 Redux + React，因為它 Pure Function 的特性，所以 單元測試 很容易寫，順便也趁機會玩一下 程式碼覆蓋率 （Code Coverage）。</description><pubDate>Mon, 02 Nov 2015 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;最近的案子用到 &lt;a href=&quot;https://github.com/rackt/redux&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Redux&lt;/a&gt; + &lt;a href=&quot;https://facebook.github.io/react/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;React&lt;/a&gt;，因為它 &lt;a href=&quot;https://en.wikipedia.org/wiki/Pure_function&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Pure Function&lt;/a&gt; 的特性，所以 &lt;a href=&quot;https://en.wikipedia.org/wiki/Unit_testing&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;單元測試&lt;/a&gt; 很容易寫，順便也趁機會玩一下 &lt;a href=&quot;https://www.blogger.com/%28https://en.wikipedia.org/wiki/Code_coverage%29&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;程式碼覆蓋率&lt;/a&gt; （Code Coverage）。&lt;/p&gt;
&lt;h2 id=&quot;單元測試-unittest&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#單元測試-unittest&quot;&gt;單元測試 Unit Test&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;這裡就不講單元測試怎麼寫了，網路上有很多大神的好文可以爬，我是用 &lt;a href=&quot;https://mochajs.org/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Mocha&lt;/a&gt; + &lt;a href=&quot;http://chaijs.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Chai&lt;/a&gt; ：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;mocha 是 test framework，提供 &lt;code&gt;describe&lt;/code&gt;、&lt;code&gt;it&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;chai 是 assertion library，提供 &lt;code&gt;assert&lt;/code&gt;、&lt;code&gt;should&lt;/code&gt;、&lt;code&gt;expect&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;安裝 &lt;a href=&quot;http://gulpjs.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Gulp&lt;/a&gt; ：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;npm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--save-dev&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;gulp&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;gulp-mocha&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;安裝 &lt;a href=&quot;https://babeljs.io/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Babel&lt;/a&gt; 讓程式支援 &lt;a href=&quot;https://en.wikipedia.org/wiki/ECMAScript&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;ES6&lt;/a&gt; 語法：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;npm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--save-dev&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;babel&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;加入 mocha 到 &lt;code&gt;gulpfile.babel.js&lt;/code&gt; ：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame has-title&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;gulpfile.babel.js&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; gulp &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;gulp&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; mocha &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;gulp-mocha&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;gulp.&lt;/span&gt;&lt;span&gt;task&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;mocha&apos;&lt;/span&gt;&lt;span&gt;, () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; gulp.&lt;/span&gt;&lt;span&gt;src&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;test/**/*.js&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;pipe&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;mocha&lt;/span&gt;&lt;span&gt;());&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;使用 &lt;code&gt;npm test&lt;/code&gt; 取代 &lt;code&gt;gulp mocha&lt;/code&gt; ：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame has-title&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;package.json&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;scripts&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;test&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;gulp mocha&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;這樣做的好處：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;gulp 可不用 &lt;code&gt;-g&lt;/code&gt; 全域安裝&lt;/li&gt;
&lt;li&gt;CI 會自動執行測試&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;程式碼覆蓋率-codecoverage&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#程式碼覆蓋率-codecoverage&quot;&gt;程式碼覆蓋率 Code Coverage&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;有許多提供 code coverage review 的服務，例如： &lt;a href=&quot;https://codeclimate.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Code Climate&lt;/a&gt;、 &lt;a href=&quot;https://codecov.io/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Codecov&lt;/a&gt;、 &lt;a href=&quot;https://coveralls.io/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Coveralls&lt;/a&gt;。這裡選擇 &lt;a href=&quot;https://codecov.io/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Codecov&lt;/a&gt; ，因為它的 GitHub public repo 方案是免費的。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;268&quot; height=&quot;68&quot; src=&quot;https://blog.amowu.com/_astro/image1.xGRsSMdc_ZHaUrq.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image1.xGRsSMdc_ZHaUrq.webp 268w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;安裝 &lt;a href=&quot;https://github.com/gotwarlost/istanbul&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;istanbul&lt;/a&gt; 產生 coverage report：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;npm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--save-dev&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;gulp-istanbul&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;isparta&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/douglasduteil/isparta&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;isparta&lt;/a&gt; 讓 istanbul 支援 ES6&lt;/p&gt;
&lt;p&gt;加入 coverage 到 &lt;code&gt;gulpfile.babel.js&lt;/code&gt; ：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame has-title&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;gulpfile.babel.js&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; istanbul &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;gulp-istanbul&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { Instrumenter } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;isparta&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;gulp.&lt;/span&gt;&lt;span&gt;task&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;coverage:instrument&apos;&lt;/span&gt;&lt;span&gt;, () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; gulp.&lt;/span&gt;&lt;span&gt;src&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;src/**/*.js&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;pipe&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;istanbul&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;instrumenter: Instrumenter,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;includeUntested: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;pipe&lt;/span&gt;&lt;span&gt;(istanbul.&lt;/span&gt;&lt;span&gt;hookRequire&lt;/span&gt;&lt;span&gt;())&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;gulp.&lt;/span&gt;&lt;span&gt;task&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;coverage:report&apos;&lt;/span&gt;&lt;span&gt;, () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; gulp.&lt;/span&gt;&lt;span&gt;src&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;src/**/*.js&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;pipe&lt;/span&gt;&lt;span&gt;(istanbul.&lt;/span&gt;&lt;span&gt;writeReports&lt;/span&gt;&lt;span&gt;())&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;gulp.&lt;/span&gt;&lt;span&gt;task&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;coverage&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;done&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;runSequence&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;coverage:instrument&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;mocha&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;coverage:report&apos;&lt;/span&gt;&lt;span&gt;, done)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;gulp coverage&lt;/code&gt; 會依順序執行：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;gulp coverage:instrument&lt;/code&gt; 配置單元測試的原始碼&lt;/li&gt;
&lt;li&gt;&lt;code&gt;gulp mocha&lt;/code&gt; 執行單元測試&lt;/li&gt;
&lt;li&gt;&lt;code&gt;gulp coverage:report&lt;/code&gt; 產生覆蓋率報告&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;把 &lt;code&gt;npm test&lt;/code&gt; 換成 &lt;code&gt;gulp coverage&lt;/code&gt; ：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame has-title&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;package.json&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;scripts&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;test&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;gulp coverage&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;最後配置 &lt;a href=&quot;https://circleci.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;CircleCI&lt;/a&gt;，當測試成功之後，自動上傳覆蓋率報告至 &lt;a href=&quot;https://codecov.io/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Codecov&lt;/a&gt; ：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;npm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--save-dev&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;codecov.io&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame has-title&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;circle.yml&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;test&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;cat ./coverage/lcov.info | codecov&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;以上，當你 push 程式碼到 GitHub 上、並且通過 CircleCI 的單元測試之後，Codecov 就會產生覆蓋率報告了！&lt;/p&gt;
&lt;p&gt;完整範例請參考我的 &lt;a href=&quot;https://github.com/amowu/amowu.com/commit/9fac520b79d36c36bba7b6d55486e5a631c1c011&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;GitHub&lt;/a&gt;&lt;/p&gt;</content:encoded><category>Tech</category></item><item><title>GTD＆Creative Routines</title><link>https://blog.amowu.com/posts/2015-10-14-gtd-and-creative-routines/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2015-10-14-gtd-and-creative-routines/</guid><description>這是一篇勉勵我自己如何實行 GTD &amp; Creative Routines 的一套方法。</description><pubDate>Wed, 14 Oct 2015 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;這是一篇勉勵我自己如何實行 GTD &amp;amp; Creative Routines 的一套方法。&lt;/p&gt;
&lt;h2 id=&quot;前言&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#前言&quot;&gt;前言&lt;/a&gt;&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;你是否常常在夜晚帶着一絲傷感入睡呢？明明胸懷大志，卻總是因爲忙碌無爲而感到愧疚？時間總是在&lt;/em&gt;&lt;em&gt;&lt;strong&gt;不知道該做什麼&lt;/strong&gt;**之中悄悄流失……&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;節錄自「&lt;a href=&quot;http://www.mifengtd.cn/articles/runningcheese-gtd-system.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;我的时间管理与方法论&lt;/a&gt;」&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;不知道該做什麼&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#不知道該做什麼&quot;&gt;不知道該做什麼&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;為了解決這類焦慮，我們需要有一套 &lt;a href=&quot;https://zh.wikipedia.org/wiki/GTD&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;GTD&lt;/a&gt; 方法，讓自己在&lt;strong&gt;正確的時間&lt;/strong&gt;（Time），有&lt;strong&gt;正確的事&lt;/strong&gt;（Things）可以去&lt;strong&gt;做&lt;/strong&gt;（Done）。&lt;/p&gt;
&lt;h3 id=&quot;什麼是-creative-routines&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#什麼是-creative-routines&quot;&gt;什麼是 Creative Routines？&lt;/a&gt;&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;「世界上最恐怖的事情就是－比你優秀的人，比你更加努力。」&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&quot;http://thepolysh.com/blog/2014/04/03/creative-routines/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;優秀的人&lt;/a&gt;之所以能夠創造出令人矚目的成就，是因爲他們在日常作息、工作和學習之中，養成了&lt;strong&gt;規律良好的習慣&lt;/strong&gt;。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;「優秀是一種習慣」 — 亞里士多德&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;幸福並非取決於天性，我們的一言一行都是日積月累下，養成的習慣。如果說懶惰是一種習慣，那麼優秀也是一種習慣。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;180&quot; src=&quot;https://blog.amowu.com/_astro/image1.BdygWJ2w_2gQDH5.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image1.BdygWJ2w_Z17hBfk.webp 640w, https://blog.amowu.com/_astro/image1.BdygWJ2w_2gQDH5.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;h3 id=&quot;人生終極目標&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#人生終極目標&quot;&gt;人生終極目標&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;我們每天生活所需要維繫的目標，不外乎五大主題：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;健康&lt;/li&gt;
&lt;li&gt;事業&lt;/li&gt;
&lt;li&gt;社會責任&lt;/li&gt;
&lt;li&gt;情感&lt;/li&gt;
&lt;li&gt;愛好&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;若想長時間、規律地維持著良好的習慣，必須得有一個&lt;strong&gt;基準&lt;/strong&gt;，用來判斷上述生活中會碰到的這些事情中，哪些對你而言才是最重要的、哪些才是你想做的、哪些是你該做的，而這個基準就是 GTD 的中心思想，也就是&lt;strong&gt;人生終極目標&lt;/strong&gt;。比如：我的人生目標是&lt;strong&gt;對社會做出貢獻&lt;/strong&gt;。那麼每當你完成一個經過篩選的目標時，也同時在實現人生的終極目標。&lt;/p&gt;
&lt;h3 id=&quot;gtd&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#gtd&quot;&gt;GTD&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;有了人生終極目標之後，接下來我們就可以開始探討如何實現 GTD（Getting Things Done）方法了。
GTD 的好處？&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;把複雜的事情簡單化&lt;/li&gt;
&lt;li&gt;把簡單的事情可操作化&lt;/li&gt;
&lt;li&gt;把可操作的事情度量化、數字化&lt;/li&gt;
&lt;li&gt;把可度量、可數字化的事情可考評化&lt;/li&gt;
&lt;li&gt;把可考評的事情流程化&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;節錄自「什麼是科學管理」 — 《命好不如習慣好》&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;GTD 的壞處？&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;會有人來留言對你說「你活得不累嗎？」&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;GTD 的核心流程，五大步驟：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;收集（Collect）&lt;/li&gt;
&lt;li&gt;處理（Process）&lt;/li&gt;
&lt;li&gt;整理（Organize）&lt;/li&gt;
&lt;li&gt;執行（Do）&lt;/li&gt;
&lt;li&gt;回顧（Plan）&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;收集collect&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#收集collect&quot;&gt;收集（Collect）&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;170&quot; height=&quot;168&quot; src=&quot;https://blog.amowu.com/_astro/image2.C9kyviHu_L0jtz.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image2.C9kyviHu_L0jtz.webp 170w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;不要相信你的記憶能力&lt;/strong&gt;，身為一個專業的程序猿，我們有義務清空自己的記憶體，讓它隨時保持在足夠應付那些真正必要的思考工作上。&lt;/p&gt;
&lt;p&gt;當你的腦中有任何雜音（Stuff）出現，不管大小，先將它往你的水桶（Inbox）裡收集，越簡單越快速的工具越好。（比如：便利貼、筆記本、待辦清單）&lt;/p&gt;
&lt;p&gt;哪些東西讓它進你的水桶？&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;突然閃過的想法&lt;/li&gt;
&lt;li&gt;想要達成的目標&lt;/li&gt;
&lt;li&gt;需要你去完成的待辦事項&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;哪些東西&lt;strong&gt;不&lt;/strong&gt;適合收集？&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;會議、約會：直接使用行事曆（Calendar）會更好&lt;/li&gt;
&lt;li&gt;稍後閱讀：&lt;a href=&quot;https://getpocket.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Pocket&lt;/a&gt;、&lt;a href=&quot;https://www.instapaper.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Instapaper&lt;/a&gt; 這類工具會是你的好朋友&lt;/li&gt;
&lt;li&gt;想收藏的媒體（網站、文章、圖片）：使用相對應的服務（&lt;a href=&quot;https://delicious.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Delicious&lt;/a&gt;、&lt;a href=&quot;https://evernote.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Evernote&lt;/a&gt;、&lt;a href=&quot;https://www.pinterest.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Pinterest&lt;/a&gt;）&lt;/li&gt;
&lt;li&gt;想養成的習慣：使用 &lt;a href=&quot;http://wiki.mbalib.com/zh-tw/21%E5%A4%A9%E6%95%88%E5%BA%94&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;21天效應&lt;/a&gt; 搭配習慣清單（&lt;a href=&quot;http://www.appving.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;微习惯&lt;/a&gt;、&lt;a href=&quot;http://idothing.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;种子习惯&lt;/a&gt;）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;盡量保持單一職責原則（SRP），若有更方便的工具可以處理這些事，那就交給專業的去完成吧，這樣除了能夠專注在該完成的任務上，也能夠隨時知道自己想找的資料存放在何處。&lt;/p&gt;
&lt;h2 id=&quot;處理process&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#處理process&quot;&gt;處理（Process）&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;200&quot; height=&quot;171&quot; src=&quot;https://blog.amowu.com/_astro/image3.BKL6vjDo_2vCEDF.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image3.BKL6vjDo_2vCEDF.webp 200w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;遵循先進先出的順序，檢查那些積在水桶裡的待辦事項，使用下列方法，將它們轉化為可執行的任務（Task）：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;二分鐘法則&lt;/li&gt;
&lt;li&gt;四象限法則&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;二分鐘法則&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#二分鐘法則&quot;&gt;二分鐘法則&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;首先判斷這件事你是否可以在二分鐘左右之內完成？如果可以的話，就趕緊先將它解決了吧！&lt;/p&gt;
&lt;h3 id=&quot;四象限法則&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#四象限法則&quot;&gt;四象限法則&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;如果是無法馬上完成、需要經過整理的任務，就先使用四象限法則來做歸類，依照&lt;strong&gt;重要度&lt;/strong&gt;與&lt;strong&gt;緊急度&lt;/strong&gt;來劃分出任務的&lt;strong&gt;優先度&lt;/strong&gt;（Priority）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;P1：&lt;strong&gt;重要&lt;/strong&gt;＆&lt;strong&gt;緊急&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;P2：&lt;strong&gt;重要&lt;/strong&gt;＆不緊急&lt;/li&gt;
&lt;li&gt;P3：不重要＆&lt;strong&gt;緊急&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;P4：不重要＆不緊急&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;哪些屬於 P1？&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;即將到達期限的工作或學業上的任務&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;會出現 P1 通常是因為：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;外界因素：突然被安排&lt;/li&gt;
&lt;li&gt;個人因素：P2 沒完成&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;因為 P1 會讓人產生壓力感，所以盡量越少越好。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;哪些屬於 P2？&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;趁著在下雨之前，就先修補自己的家。 — 未雨綢繆《詩經》&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;工作或學業上的任務&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;個人想達成的目標&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;會對個人的發展、以及周圍環境有重大影響的事項&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;根據 &lt;a href=&quot;http://wiki.mbalib.com/zh-tw/80/20%E6%B3%95%E5%88%99&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;80/20法則&lt;/a&gt; ，我們應該盡量投資時間在 P2 的任務身上。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;哪些屬於 P3？&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;感到忙碌且盲目的事&lt;/li&gt;
&lt;li&gt;附和別人期望的事&lt;/li&gt;
&lt;li&gt;臨時被安排的事&lt;/li&gt;
&lt;li&gt;不必要的社交回覆（電話、LINE、Facebook）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;P3 盡是些不重要的事，但往往因為它們緊急，所以是最容易佔據人們寶貴時間的主兇，應該盡量避免。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;哪些屬於 P4？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;P4 屬於可偶爾放鬆一下，但不可沈溺於此的事項。&lt;/p&gt;
&lt;p&gt;如同這篇「 &lt;a href=&quot;http://blog.jobbole.com/84577/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;绝地武士&lt;/a&gt; 」之中提到，我們常常把時間浪費在那些不重要的雜魚身上，而忽略了自己真正想要的目標，拯救公主。&lt;/p&gt;
&lt;p&gt;結論：走出 P3，專注 P2。&lt;/p&gt;
&lt;h2 id=&quot;整理organize&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#整理organize&quot;&gt;整理（Organize）&lt;/a&gt;&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;大事化小，小事化無。&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;320&quot; height=&quot;204&quot; src=&quot;https://blog.amowu.com/_astro/image4.BRq0fE6L_2ctp5t.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image4.BRq0fE6L_2ctp5t.webp 320w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;經過前面的檢查之後，我們已經安排了優先度給任務，接下來就是根據目標大小拆分成專案（Project），將大事化小，小事化無。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;目標金字塔&lt;/li&gt;
&lt;li&gt;三隻青蛙理論&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;目標金字塔&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#目標金字塔&quot;&gt;目標金字塔&lt;/a&gt;&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;每次馬拉松比賽之前，我都要乘車把比賽的線路仔細地看一遍，並把沿途比較醒目的標誌畫下來，比如：第一個標誌是銀行；第二個標誌是一棵大樹；第三個標誌是一棟紅房子，就這樣一直畫到賽程的終點。比賽開始後，我就以百米的速度奮力地向第一個目標衝去，等到達第一個目標之後，我又以同樣的速度向第二個目標衝去。四十多公里的賽程，就被我這麼分解成幾個小目標輕鬆地跑完了。 
— 山田本一&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我們可以推測任務可能所需的時間，將之歸類為長期目標、中期目標、短期目標、小目標：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;長期目標：年度目標&lt;/li&gt;
&lt;li&gt;中期目標：季目標、月目標&lt;/li&gt;
&lt;li&gt;短期目標：周目標&lt;/li&gt;
&lt;li&gt;小目標：每日目標&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;三隻青蛙理論&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#三隻青蛙理論&quot;&gt;三隻青蛙理論&lt;/a&gt;&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;如果你必須吃掉一隻青蛙，不要長時間盯着它看。如果你必須吃掉三隻青蛙，記得要先吃掉最大、最醜的那隻。 — 博恩崔西《吃掉那隻青蛙》&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;為自己的每一天、甚至每周、每個月、每一年，準備&lt;strong&gt;最重要的三件事&lt;/strong&gt;。&lt;/p&gt;
&lt;h2 id=&quot;執行do&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#執行do&quot;&gt;執行（Do）&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;199&quot; height=&quot;200&quot; src=&quot;https://blog.amowu.com/_astro/image5.D0NIGD-D_Z22o4Pj.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image5.D0NIGD-D_Z22o4Pj.webp 199w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;經過前面的整理之後，我們已經有了最重要的三隻青蛙，再來就是 GTD 最關鍵的一步，Do It！不把青蛙消化掉的話，前面講的都會是空談，讓我們把它完成吧！&lt;/p&gt;
&lt;h3 id=&quot;番茄工作法&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#番茄工作法&quot;&gt;番茄工作法&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;http://wiki.mbalib.com/zh-tw/%E7%95%AA%E8%8C%84%E5%B7%A5%E4%BD%9C%E6%B3%95&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;番茄工作法&lt;/a&gt; 的基本原則：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;一次只做一件事情&lt;/li&gt;
&lt;li&gt;一次番茄時鐘二十五分鐘&lt;/li&gt;
&lt;li&gt;中途不可中斷，一旦中斷，必須重新計算該次番茄時鐘&lt;/li&gt;
&lt;li&gt;每完成一次番茄時鐘，休息五分鐘&lt;/li&gt;
&lt;li&gt;每完成四次番茄時鐘，休息二十五分鐘&lt;/li&gt;
&lt;li&gt;花費超過三個小時的任務，表示還切得不夠細&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;番茄工作法的目的：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;提高集中力，專注在黃金工作時間&lt;/li&gt;
&lt;li&gt;量化被打斷的次數、原因，做出相對應的調整&lt;/li&gt;
&lt;li&gt;量化該任務所花費的番茄時鐘，完善預估流程&lt;/li&gt;
&lt;li&gt;增進成就感，激勵持久動機&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;回顧plan&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#回顧plan&quot;&gt;回顧（Plan）&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;200&quot; height=&quot;200&quot; src=&quot;https://blog.amowu.com/_astro/image6.avbuX0Gi_1hBUoP.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image6.avbuX0Gi_1hBUoP.webp 200w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;到這裡我們已經將那些雜亂的待辦事項，整理成為確切可執行的目標，解決了最初的問題：&lt;strong&gt;不知道該做什麼&lt;/strong&gt;！&lt;/p&gt;
&lt;h3 id=&quot;pdca-循環&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#pdca-循環&quot;&gt;PDCA 循環&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;儘管如此，我們規劃的目標並不會一開始就如此順遂，GTD 最困難的部份就是拆分任務，這件事就跟程序猿預估時程一樣困難，需要長時間的經驗累積，不斷地調整、修正、優化、迭代，最後才能讓 GTD 融入於自己的心流（Flow）。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://wiki.mbalib.com/zh-tw/%E6%88%B4%E6%98%8E%E5%BE%AA%E7%8E%AF&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;PDCA 循環&lt;/a&gt; 的概念，讓專案保持在執行 → 回顧 → 處理 → 整理 → 執行的迭代循環之中。&lt;/p&gt;
&lt;p&gt;回顧的場景（Context）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;每日目標：每天睡前準備好明天的三隻青蛙，讓自己隔天起床就知道今天該完成什麼&lt;/li&gt;
&lt;li&gt;周目標：利用周一上班的收假症候群緩衝時間，回顧自己上周與本周的任務狀況&lt;/li&gt;
&lt;li&gt;月目標：月底或月初，回顧任務並進行適當的調整&lt;/li&gt;
&lt;li&gt;年目標：新年期間，回顧過去一年的成果，重新訂下新年目標&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;願望清單&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#願望清單&quot;&gt;願望清單&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;準備一份願望清單（Wish List），並依照自己可以負擔的值段，將願望分配成年獎勵、月獎勵、周獎勵、日獎勵。比如：今天完成這項任務的話，晚上就去吃好料的。完成這項困難的月目標的話，就買支新的 iPhone 犒賞自己。今年的目標達到的話，明年就給自己一趟出國旅行作為獎勵！&lt;/p&gt;
&lt;h2 id=&quot;結語&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#結語&quot;&gt;結語&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;其實我覺得需要 To-Do List 處理的事項會越來越減少，因為各式各樣手機 App 搭配通知系統會更高效，然後隨著人工智慧的發展，電子秘書也會越來越聰明，加上物聯網和大數據也成熟之後，人類真的只需要躺著等事情主動來讓你做就好了。&lt;/p&gt;
&lt;h2 id=&quot;參考文章&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#參考文章&quot;&gt;參考文章&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.evernote.com/zhtw/2015/02/27/tapping-daily-ritual-for-great-creative-minds/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;時間總是不夠用？偉大創作者的日常作息一探&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://blog.jobbole.com/84577/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;时间管理很简单，看看绝地武士的招数&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.zhihu.com/question/26701102/answer/56136441&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;做一点事情就想放松，然后就开始拖延，怎么克服？&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.mifengtd.cn/articles/runningcheese-gtd-system.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;我的时间管理与方法论&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.infoq.com/cn/news/2015/10/pixalut-staffan&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;《番茄工作法图解》作者亲身讲解：这些最佳实践可以帮你筛选出那个最重要的任务&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>Tech</category></item><item><title>OpenCC 簡繁中文轉換 Alfred 2 workflow v1.0.0 released!</title><link>https://blog.amowu.com/posts/2015-08-05-opencc-converter-alfred-2-workflow/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2015-08-05-opencc-converter-alfred-2-workflow/</guid><description>從以前就在找一個可以簡繁互換的 Alfred workflow，可惜一直沒找到滿意的，所以一直以來都是用 Yahoo 的輸入法來切換模式。結果前幾天突然看到 OpenCC 這個開源項目，而且還有 Python 的庫可以用！二話不說就自己來寫一個了，以下是節自我的 GitHub…</description><pubDate>Wed, 05 Aug 2015 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;從以前就在找一個可以簡繁互換的 Alfred workflow，可惜一直沒找到滿意的，所以一直以來都是用 Yahoo 的輸入法來切換模式。結果前幾天突然看到 &lt;a href=&quot;https://github.com/BYVoid/OpenCC&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;OpenCC&lt;/a&gt; 這個開源項目，而且還有 Python 的庫可以用！二話不說就自己來寫一個了，以下是節自我的 GitHub &lt;a href=&quot;https://github.com/amowu/alfred-chinese-converter&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;alfred-chinese-converter&lt;/a&gt; v1.0.0 的 README。&lt;/p&gt;
&lt;h2 id=&quot;introduction-介紹&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#introduction-介紹&quot;&gt;Introduction 介紹&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;使用開源項目 &lt;a href=&quot;https://github.com/BYVoid/OpenCC&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;OpenCC&lt;/a&gt;（開放中文轉換）開發的 &lt;a href=&quot;http://www.alfredapp.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Alfred 2&lt;/a&gt; workflow，支持簡繁體中文的&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E7%B9%81%E7%B0%A1%E8%BD%89%E6%8F%9B&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;詞彙級別轉換&lt;/a&gt;、&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E7%B9%81%E7%B0%A1%E8%BD%89%E6%8F%9B&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;異體字轉換&lt;/a&gt;以及&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E7%B9%81%E7%B0%A1%E8%BD%89%E6%8F%9B&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;地區習慣用詞轉換&lt;/a&gt;（中國大陸、臺灣、香港）。&lt;/p&gt;
&lt;h2 id=&quot;features-特點&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#features-特點&quot;&gt;Features 特點&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;節選自 &lt;a href=&quot;https://github.com/BYVoid/OpenCC&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;OpenCC&lt;/a&gt; 的部份特點：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;嚴格區分「一簡對多繁」和「一簡對多異」。&lt;/li&gt;
&lt;li&gt;完全兼容異體字，可以實現動態替換。&lt;/li&gt;
&lt;li&gt;嚴格審校一簡對多繁詞條，原則爲「能分則不合」。&lt;/li&gt;
&lt;li&gt;支持中國大陸、臺灣、香港異體字和地區習慣用詞轉換，如「裏」「裡」、「鼠標」「滑鼠」。&lt;/li&gt;
&lt;li&gt;詞庫和函數庫完全分離，可以自由修改、導入、擴展。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;installation-安裝&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#installation-安裝&quot;&gt;Installation 安裝&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Mac OS X 環境底下，使用 &lt;a href=&quot;http://brew.sh/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Homebrew&lt;/a&gt; 安裝 &lt;a href=&quot;https://github.com/BYVoid/OpenCC&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;OpenCC&lt;/a&gt; 這套開放中文轉換庫：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;opencc&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2 id=&quot;download-下載&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#download-下載&quot;&gt;Download 下載&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/amowu/alfred-chinese-converter/releases/download/1.0.0/alfred-chinese-converter.alfredworkflow&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;alfred-chinese-converter.alfredworkflow&lt;/a&gt; v1.0.0&lt;/p&gt;
&lt;h2 id=&quot;usage-用法&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#usage-用法&quot;&gt;Usage 用法&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;使用關鍵字 &lt;code&gt;cc&lt;/code&gt; 輸入需要作簡繁轉換的詞句：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;680&quot; height=&quot;163&quot; src=&quot;https://blog.amowu.com/_astro/image1.BBUxvU39_Z28ewHM.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image1.BBUxvU39_iDh4c.webp 640w, https://blog.amowu.com/_astro/image1.BBUxvU39_Z28ewHM.webp 680w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Alfred 會列出 7 項轉換結果：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;簡體到繁體&lt;/li&gt;
&lt;li&gt;繁體到簡體&lt;/li&gt;
&lt;li&gt;簡體到臺灣正體&lt;/li&gt;
&lt;li&gt;臺灣正體到簡體&lt;/li&gt;
&lt;li&gt;簡體到香港繁體（香港小學學習字詞表標準）&lt;/li&gt;
&lt;li&gt;香港繁體（香港小學學習字詞表標準）到簡體&lt;/li&gt;
&lt;li&gt;簡體到繁體（臺灣正體標準）並轉換爲臺灣常用詞彙&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;680&quot; height=&quot;475&quot; src=&quot;https://blog.amowu.com/_astro/image2.DqYhKIlg_2iDdh.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image2.DqYhKIlg_Z2x2evs.webp 640w, https://blog.amowu.com/_astro/image2.DqYhKIlg_2iDdh.webp 680w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;選擇其中一筆結果，自動複製至剪貼簿：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;356&quot; height=&quot;125&quot; src=&quot;https://blog.amowu.com/_astro/image3.ClJVbeau_ZkVL4R.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image3.ClJVbeau_ZkVL4R.webp 356w&quot; /&gt;&lt;/p&gt;</content:encoded><category>Tech</category></item><item><title>DevOps：持續整合＆持續交付（Docker、CircleCI、AWS）</title><link>https://blog.amowu.com/posts/2015-04-25-devops-continuous-integration-delivery-docker-circleci-aws-beanstalk/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2015-04-25-devops-continuous-integration-delivery-docker-circleci-aws-beanstalk/</guid><description>這篇文章將一步一步介紹如何使用 Docker、GitHub Flow、CircleCI、AWS Elastic Beanstalk 與 Slack 來完成持續整合與持續交付的開發流程。</description><pubDate>Sat, 25 Apr 2015 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;這篇文章將一步一步介紹如何使用 Docker、GitHub Flow、CircleCI、AWS Elastic Beanstalk 與 Slack 來完成&lt;strong&gt;持續整合&lt;/strong&gt;與&lt;strong&gt;持續交付&lt;/strong&gt;的開發流程。&lt;/p&gt;
&lt;h2 id=&quot;前言&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#前言&quot;&gt;前言&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;什麼是持續整合持續交付&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#什麼是持續整合持續交付&quot;&gt;什麼是持續整合＆持續交付？&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;持續整合＆持續交付（Continuous Integration &amp;amp; Continous Delivery），簡稱 CI &amp;amp; CD，具體介紹可以參考「 &lt;a href=&quot;https://samkuo.me/post/2013/10/continuous-integration-deployment-delivery/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;山姆鍋對持續整合、持續部署、持續交付的定義&lt;/a&gt; 」這篇文章。&lt;/p&gt;
&lt;p&gt;簡單來說就是盡量減少手動人力，將一些日常工作交給自動化工具。例如：環境建置、單元測試、日誌紀錄、產品部署。&lt;/p&gt;
&lt;h3 id=&quot;我使用了哪些工具&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#我使用了哪些工具&quot;&gt;我使用了哪些工具？&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://git-scm.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Git&lt;/a&gt; — 版本管理&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;GitHub&lt;/a&gt; — 程式碼託管、審查&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://circleci.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;CircleCI&lt;/a&gt; — 自動化建置、測試、部署&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.docker.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Docker&lt;/a&gt; — 可攜式、輕量級的執行環境&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://aws.amazon.com/elasticbeanstalk/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;AWS Elastic Beanstalk&lt;/a&gt; — 雲端平台&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://slack.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Slack&lt;/a&gt; — 團隊溝通、日誌、通知&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;看完這篇你可以學到什麼&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#看完這篇你可以學到什麼&quot;&gt;看完這篇你可以學到什麼？&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;瞭解 GiHub 的工作流程（&lt;a href=&quot;https://guides.github.com/introduction/flow/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;GitHub Flow&lt;/a&gt;），利用 &lt;strong&gt;Pull Request&lt;/strong&gt; 以及&lt;strong&gt;分支&lt;/strong&gt;來完成&lt;strong&gt;代碼審查&lt;/strong&gt;（Code Review）與&lt;strong&gt;環境配置&lt;/strong&gt;，例如：開發版（development）、測試版（testing/QA）、上線產品（staging/production）。&lt;/li&gt;
&lt;li&gt;使用 Docker，統一開發者、測試人員、以及產品的執行環境。&lt;/li&gt;
&lt;li&gt;使用 EB CLI 將應用程式部署到 AWS Elastic Beanstalk 平台上。&lt;/li&gt;
&lt;li&gt;使用 CircleCI 將以上工作全部自動化。偵測 GitHub 分支上的程式碼，若有更新則觸發：建置 Docker 環境、單元測試、然後自動部署新版本到 AWS EB。&lt;/li&gt;
&lt;li&gt;使用 Slack，讓團隊成員能夠即時接收 GitHub 與 CircleCI 每一項動作的通知。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;577&quot; height=&quot;500&quot; src=&quot;https://blog.amowu.com/_astro/image1.BUIdMP9M_JeLiQ.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image1.BUIdMP9M_JeLiQ.webp 577w&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;內容大綱&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#內容大綱&quot;&gt;內容大綱&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Node.js
&lt;ul&gt;
&lt;li&gt;在本地端執行 Node.js&lt;/li&gt;
&lt;li&gt;在本地端測試 Node.js&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;GitHub&lt;/li&gt;
&lt;li&gt;CircleCI
&lt;ul&gt;
&lt;li&gt;在 CircleCI 測試 Node.js&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Code Review with GitHub Flow&lt;/li&gt;
&lt;li&gt;Docker
&lt;ul&gt;
&lt;li&gt;在 Docker 執行 Node.js&lt;/li&gt;
&lt;li&gt;在 CircleCI 測試 Docker&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;AWS Elastic Beanstalk
&lt;ul&gt;
&lt;li&gt;在本地端部署 AWS&lt;/li&gt;
&lt;li&gt;在 CircleCI 部署 AWS&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Slack&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;nodejs&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#nodejs&quot;&gt;Node.js&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;296&quot; height=&quot;80&quot; src=&quot;https://blog.amowu.com/_astro/image2.C8pcjDeh_1rLebJ.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image2.C8pcjDeh_1rLebJ.webp 296w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;安裝：&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://nodejs.org/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;node&lt;/a&gt;: 0.10&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;這篇文章以 Node.js 的應用程式作為範例，其他語言（Ruby on Rails、Python、PHP）也同樣適用此工作流程。&lt;/p&gt;
&lt;h3 id=&quot;建立新專案&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#建立新專案&quot;&gt;建立新專案&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mkdir&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;hello-ci-workflow&lt;/span&gt;&lt;span&gt; $ &lt;/span&gt;&lt;span&gt;cd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;hello-ci-workflow&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3 id=&quot;在本地端執行-nodejs&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#在本地端執行-nodejs&quot;&gt;在本地端執行 Node.js&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;初始化 Node.js 的環境，填寫一些資料之後會在目錄下產生一個 &lt;code&gt;package.json&lt;/code&gt; 的檔案：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;npm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;init&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;安裝 Node.js 的 web framework，以 &lt;a href=&quot;http://expressjs.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Express&lt;/a&gt; 為例：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;npm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;express&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--save&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;*--save*&lt;/code&gt;&lt;em&gt;: 寫入 &lt;em&gt;&lt;code&gt;*package.json*&lt;/code&gt;&lt;/em&gt; 的 dependencies。&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;完成之後， &lt;code&gt;package.json&lt;/code&gt; 大概會長這個樣子：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame has-title&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;package.json&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;name&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;hello-ci-workflow&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;main&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;index.js&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;dependencies&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;express&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;^4.12.3&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;scripts&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;start&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;node index.js&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;在 &lt;code&gt;index.js&lt;/code&gt; 裡寫一段簡單的 Hello World! 的程式：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame has-title&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;index.js&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; express &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;express&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; app &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;express&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;app.&lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;/&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;req&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;res&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;res.&lt;/span&gt;&lt;span&gt;send&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;Hello World!&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; server &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; app.&lt;/span&gt;&lt;span&gt;listen&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;3000&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; () {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; host &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; server.&lt;/span&gt;&lt;span&gt;address&lt;/span&gt;&lt;span&gt;().address;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; port &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; server.&lt;/span&gt;&lt;span&gt;address&lt;/span&gt;&lt;span&gt;().port;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;Example app listening at http://%s:%s&apos;&lt;/span&gt;&lt;span&gt;, host, port);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;執行 &lt;code&gt;npm start&lt;/code&gt; 或 &lt;code&gt;node index.js&lt;/code&gt; ：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;npm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;start&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;打開瀏覽器 &lt;code&gt;http://localhost:3000&lt;/code&gt; 看結果：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;250&quot; height=&quot;70&quot; src=&quot;https://blog.amowu.com/_astro/image3.D1dxrGZ4_Z7lzxF.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image3.D1dxrGZ4_Z7lzxF.webp 250w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;*--save-dev*&lt;/code&gt;&lt;em&gt;: 寫入 &lt;em&gt;&lt;code&gt;*package.json*&lt;/code&gt;&lt;/em&gt; 的 devDependencies，正式上線環境不會被安裝。&lt;/em&gt;&lt;/p&gt;
&lt;h3 id=&quot;在本地端測試-nodejs&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#在本地端測試-nodejs&quot;&gt;在本地端測試 Node.js&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;安裝 Node.js 的單元測試，以 &lt;a href=&quot;http://mochajs.org/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Mocha&lt;/a&gt; 為例：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$ npm install mocha &lt;/span&gt;&lt;span&gt;--save-dev&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;*--save-dev*&lt;/code&gt;&lt;em&gt;: 寫入 &lt;em&gt;&lt;code&gt;*package.json*&lt;/code&gt;&lt;/em&gt; 的 devDependencies，正式上線環境不會被安裝。&lt;/em&gt;&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame has-title&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;package.json&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;name&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;hello-ci-workflow&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;main&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;index.js&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;dependencies&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;express&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;^4.12.3&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;devDependencies&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;mocha&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;^2.2.4&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;scripts&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;start&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;node index.js&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;根目錄 &lt;code&gt;test&lt;/code&gt; 資料夾，並新增一個測試腳本 &lt;code&gt;test.js&lt;/code&gt; ：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mkdir&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;test&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;test&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;touch&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;test.js&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;加入一筆錯誤的測試 &lt;code&gt;assert.equal(1, [1,2,3].indexOf(0))&lt;/code&gt; ：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame has-title&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;test/test.js&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; assert &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;assert&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;describe&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;Array&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt;(){&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;describe&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;#indexOf()&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt;(){&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;it&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;should return -1 when the value is not present&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt;(){&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;assert.&lt;/span&gt;&lt;span&gt;equal&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;, [&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;].&lt;/span&gt;&lt;span&gt;indexOf&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;執行 mocha 測試：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$ .&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;node_modules&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;.bin&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;mocha&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Array&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;#indexOf()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;) should &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; when the value &lt;/span&gt;&lt;span&gt;is&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;not&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;present&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;0 &lt;/span&gt;&lt;span&gt;passing&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;9ms&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;1 &lt;/span&gt;&lt;span&gt;failing&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;結果顯示 &lt;code&gt;1 failing&lt;/code&gt;，測試沒通過，因為 &lt;code&gt;[1,2,3].indexOf(0)&lt;/code&gt; 回傳的值不等於 &lt;code&gt;-1&lt;/code&gt; 。&lt;/p&gt;
&lt;p&gt;將 &lt;code&gt;test.js&lt;/code&gt; 的測試修正：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame has-title&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;test/test.js&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;assert.equal(-1,&lt;/span&gt;&lt;span&gt; [1,2,3].indexOf(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;再次執行 mocha 測試：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;./node_modules/.bin/mocha&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;Array&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;#indexOf()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;✓&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;should&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;when&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;the&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;is&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;not&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;present&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;passing&lt;/span&gt;&lt;span&gt; (6ms)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;結果顯示 &lt;code&gt;1 passing&lt;/code&gt; ，通過測試。&lt;/p&gt;
&lt;h2 id=&quot;github&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#github&quot;&gt;GitHub&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;374&quot; height=&quot;80&quot; src=&quot;https://blog.amowu.com/_astro/image4.kD-i4BjP_ZImJWi.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image4.kD-i4BjP_ZImJWi.webp 374w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;安裝：&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://git-scm.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;git&lt;/a&gt;: 2.3&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;帳號：&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;初始化 git 環境：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$ git init .&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;輸入 &lt;code&gt;git status&lt;/code&gt; 會顯示目前哪些檔案有過更動：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;status&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;On&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;branch&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;master&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Initial&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;commit&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Untracked&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;files:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;use&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;git add &amp;lt;file&amp;gt;...&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;to&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;include&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;what&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;will&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;be&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;committed&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;index.js&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;node_modules/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;package.json&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;test/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;將 &lt;code&gt;node_modules&lt;/code&gt; 加到 &lt;code&gt;.gitignore&lt;/code&gt; 黑名單，因為這個資料夾是由 &lt;code&gt;npm install&lt;/code&gt; 自動產生的，不需要放到 GitHub 上：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame has-title&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;.gitignore&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# Dependency directory&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;node_modules&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;將更動 commit：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt; $ &lt;/span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;commit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-m&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;first commit&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;打開 GitHub，新增一個 repository：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;325&quot; height=&quot;47&quot; src=&quot;https://blog.amowu.com/_astro/image5.BFSKwyg2_lyzEE.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image5.BFSKwyg2_lyzEE.webp 325w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;輸入 repository 的名稱，以 &lt;code&gt;hello-ci-workflow&lt;/code&gt; 為例：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;430&quot; height=&quot;44&quot; src=&quot;https://blog.amowu.com/_astro/image6.DpdIUQrT_2one.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image6.DpdIUQrT_2one.webp 430w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;使用 &lt;code&gt;git remote add&lt;/code&gt; 將新創建的 GitHub repository 加入到 remote：&lt;/p&gt;
&lt;p&gt;&lt;em&gt;帳號：&lt;/em&gt;&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;remote&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;origin&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;https://github.com/&amp;lt;USER_NAME&amp;gt;/hello-ci-workflow.git&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;*&amp;lt;USER_NAME&amp;gt;*&lt;/code&gt;* 改成自己的帳號。*&lt;/p&gt;
&lt;p&gt;使用 &lt;code&gt;git push&lt;/code&gt; 將程式碼傳到 GitHub：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;push&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-u&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;origin&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;master&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;成功之後前往 &lt;code&gt;https://github.com/&amp;lt;USER_NAME&amp;gt;/hello-ci-workflow&lt;/code&gt; 就可以看到剛才上傳的檔案：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;211&quot; height=&quot;205&quot; src=&quot;https://blog.amowu.com/_astro/image7.DkV_l1so_ZOsSGW.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image7.DkV_l1so_ZOsSGW.webp 211w&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;circleci&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#circleci&quot;&gt;CircleCI&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;378&quot; height=&quot;80&quot; src=&quot;https://blog.amowu.com/_astro/image8.PfRS1b0__ZAdUf5.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image8.PfRS1b0__ZAdUf5.webp 378w&quot; /&gt;&lt;/p&gt;
&lt;h3 id=&quot;加入-github-repository&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#加入-github-repository&quot;&gt;加入 GitHub repository&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;點選左邊欄的 &lt;code&gt;Add Projects&lt;/code&gt; 按鈕：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;195&quot; height=&quot;75&quot; src=&quot;https://blog.amowu.com/_astro/image9.Cy15K0v3_Z4Ux6Q.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image9.Cy15K0v3_Z4Ux6Q.webp 195w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;選擇自己的 GitHub 帳號：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;250&quot; height=&quot;68&quot; src=&quot;https://blog.amowu.com/_astro/image10.096gJ53a_17KaGE.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image10.096gJ53a_17KaGE.webp 250w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;搜尋要加入的 GitHub repository，然後點選 &lt;code&gt;Build project&lt;/code&gt; 按鈕，以 &lt;code&gt;hello-ci-workflow&lt;/code&gt; 為例：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;350&quot; height=&quot;94&quot; src=&quot;https://blog.amowu.com/_astro/image11.Cujqo5Qq_Zc8v4Y.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image11.Cujqo5Qq_Zc8v4Y.webp 350w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;完成之後 CircleCI 就會自動執行第一次的建構，不過因為還沒加入測試腳本，所以建構結果會顯示 no test：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;269&quot; height=&quot;78&quot; src=&quot;https://blog.amowu.com/_astro/image12.8zXabepk_Z1p1mB4.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image12.8zXabepk_Z1p1mB4.webp 269w&quot; /&gt;&lt;/p&gt;
&lt;h3 id=&quot;在-circleci-測試nodejs&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#在-circleci-測試nodejs&quot;&gt;在 CircleCI 測試 Node.js&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;在專案根目錄底下建立一個 &lt;code&gt;circle.yml&lt;/code&gt; ，並加入 mocha test：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame has-title&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;circle.yml&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;machine&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;node&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;version&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;0.10&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;test&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;override&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;./node_modules/.bin/mocha&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;完成之後將檔案 push 上 GitHub：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;circle.yml&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;commit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-m&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;add circle.yml&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;push&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Push 成功之後，CircleCI 會自動觸發建構和測試：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;500&quot; height=&quot;300&quot; src=&quot;https://blog.amowu.com/_astro/image13.BwsYwGn7_ZOsaOa.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image13.BwsYwGn7_ZOsaOa.webp 500w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;測試通過，建置成功：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;210&quot; height=&quot;55&quot; src=&quot;https://blog.amowu.com/_astro/image14.rWCNbKgR_2bH92O.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image14.rWCNbKgR_2bH92O.webp 210w&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;代碼審查code-reviewwith-githubflow&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#代碼審查code-reviewwith-githubflow&quot;&gt;代碼審查（Code Review）with GitHub Flow&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;目前開發中比較常用的 workflow 有 &lt;a href=&quot;http://nvie.com/posts/a-successful-git-branching-model/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Git flow&lt;/a&gt; 和 &lt;a href=&quot;https://guides.github.com/introduction/flow/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;GitHub flow&lt;/a&gt; 兩種，可以參考以下幾篇文章。&lt;/p&gt;
&lt;p&gt;Git flow：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://ihower.tw/blog/archives/5140&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Git flow 開發流程&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://danielkummer.github.io/git-flow-cheatsheet/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;git-flow cheatsheet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://nvie.com/posts/a-successful-git-branching-model/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;A successful Git branching model&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;GitHub flow：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://guides.github.com/introduction/flow/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Understanding the GitHub Flow&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.arthurtoday.com/2015/02/git-flow-vs-github-flow.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Git Flow 和 Github Flow 的不同&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://blog.krdai.info/post/17485259496/github-flow&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;在 GitHub 當中使用的 work flow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;這裡我們使用 GitHub flow，它的核心精神是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;所有在 master 分支上的程式都一定要是通過測試，可以部署的產品版本。&lt;/li&gt;
&lt;li&gt;要開發功能、修復 Bug、做任何事情，都要從 master 開一條新的分支。&lt;/li&gt;
&lt;li&gt;隨時 commit 和 push 你的程式碼到 GitHub 上，與大家討論。&lt;/li&gt;
&lt;li&gt;功能完成時，使用 pull request 讓大家作 code review。&lt;/li&gt;
&lt;li&gt;確認沒問題之後才可以 merge 回 master，並且部屬新版本到線上。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;建立一條分支&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#建立一條分支&quot;&gt;建立一條分支&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;為了確保 master 這條主線上的程式碼都是穩定的，所以建議開發者依照不同的功能、建立不同的分支，這裡以 &lt;code&gt;test-github-flow&lt;/code&gt; 為例，使用 &lt;code&gt;git branch&lt;/code&gt; 新增分支、然後 &lt;code&gt;git checkout&lt;/code&gt; 切換分支：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;branch&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;test-github-flow&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;checkout&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;test-github-flow&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3 id=&quot;加入-commits&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#加入-commits&quot;&gt;加入 commits&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;在 &lt;code&gt;test.js&lt;/code&gt; 裡加入一行錯誤的測試 &lt;code&gt;assert.equal(3, [1,2,3].indexOf(5))&lt;/code&gt; ：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame has-title&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;test/test.js&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;// ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;assert.&lt;/span&gt;&lt;span&gt;equal&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;, [&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;].&lt;/span&gt;&lt;span&gt;indexOf&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;test/test.js&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;commit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-m&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;add a error test case&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3 id=&quot;新增一個-pullrequest&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#新增一個-pullrequest&quot;&gt;新增一個 Pull Request&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Push 到 GitHub 的 test-github-flow 分支：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;push&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-u&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;origin&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;test-github-flow&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;打開 GitHub 之後，會出現 &lt;code&gt;test-github-flow&lt;/code&gt; 分支的 push commits，點選旁邊的 &lt;code&gt;Compare &amp;amp; pull request&lt;/code&gt; 按鈕：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;105&quot; src=&quot;https://blog.amowu.com/_astro/image15.CPHXdD9C_ZdiPT2.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image15.CPHXdD9C_Z19lWff.webp 640w, https://blog.amowu.com/_astro/image15.CPHXdD9C_ZdiPT2.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;點選之後會進入 Open a pull request 的填寫頁面，選擇想要 merge 的分支、輸入描述之後，點選 &lt;code&gt;Create pull request&lt;/code&gt; 按鈕：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;488&quot; src=&quot;https://blog.amowu.com/_astro/image16.Bj_DnTI6_Z1CFQ5e.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image16.Bj_DnTI6_1GdWR3.webp 640w, https://blog.amowu.com/_astro/image16.Bj_DnTI6_Z1CFQ5e.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;h3 id=&quot;檢視討論你的程式碼&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#檢視討論你的程式碼&quot;&gt;檢視＆討論你的程式碼&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;新增一個 pull request 之後，其他人就會在 GitHub 上出現通知：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;170&quot; height=&quot;35&quot; src=&quot;https://blog.amowu.com/_astro/image17.B9YDOVYM_ZP1oUz.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image17.B9YDOVYM_ZP1oUz.webp 170w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;點進去之後可以看見相關的 commits 與留言，但是下面有一個紅紅大大的叉叉；因為每次 GitHub 只要有新的 push，就會觸發 CircleCI 的自動建置和測試，並且顯示結果在 GitHub 上：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;427&quot; src=&quot;https://blog.amowu.com/_astro/image18.Bokf5Sru_1qh6bb.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image18.Bokf5Sru_Z2arwmh.webp 640w, https://blog.amowu.com/_astro/image18.Bokf5Sru_1qh6bb.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;點選叉叉，前往 CircleCI 查看錯誤原因：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;35&quot; src=&quot;https://blog.amowu.com/_astro/image19.Dn8RBYz8_vAsNV.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image19.Dn8RBYz8_2949iX.webp 640w, https://blog.amowu.com/_astro/image19.Dn8RBYz8_vAsNV.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;就會發現剛剛 push 到 test-github-flow 的測試沒通過：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;210&quot; height=&quot;75&quot; src=&quot;https://blog.amowu.com/_astro/image20.CxTZpuqj_Z1HSgkD.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image20.CxTZpuqj_Z1HSgkD.webp 210w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;580&quot; height=&quot;535&quot; src=&quot;https://blog.amowu.com/_astro/image21.Cgr5xMtC_1pv4Vo.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image21.Cgr5xMtC_1pv4Vo.webp 580w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;回到 GitHub，因為測試沒通過，所以審查者不能讓這筆 pull request 被 merge 回 master。&lt;/p&gt;
&lt;p&gt;找到剛剛 commit 的那段程式碼，留言告知請開發者修正錯誤之後，再重新 commit push 上來：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;620&quot; height=&quot;200&quot; src=&quot;https://blog.amowu.com/_astro/image22.B6ZNi4Vi_FSgEx.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image22.B6ZNi4Vi_FSgEx.webp 620w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;251&quot; src=&quot;https://blog.amowu.com/_astro/image23.BEphJYAh_1zTmXs.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image23.BEphJYAh_YwgRD.webp 640w, https://blog.amowu.com/_astro/image23.BEphJYAh_1zTmXs.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;232&quot; src=&quot;https://blog.amowu.com/_astro/image24.YOiCyBR4_Z2ouEaV.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image24.YOiCyBR4_Z1e0L1P.webp 640w, https://blog.amowu.com/_astro/image24.YOiCyBR4_Z2ouEaV.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;修正 &lt;code&gt;test.js&lt;/code&gt; 的測試腳本：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame has-title&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;test/test.js&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;// ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;assert.&lt;/span&gt;&lt;span&gt;equal&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;, [&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;].&lt;/span&gt;&lt;span&gt;indexOf&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;再次 commit &amp;amp; push：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;test/test.js&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;commit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-m&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;fix error test case&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;push&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;回到 GitHub 的 pull request 頁面，可以看到最新一筆的 commit 成功通過 CircleCI 的測試了：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;432&quot; src=&quot;https://blog.amowu.com/_astro/image25.ZXh816Pg_Zot0Y8.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image25.ZXh816Pg_ZJY1em.webp 640w, https://blog.amowu.com/_astro/image25.ZXh816Pg_Zot0Y8.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;460&quot; height=&quot;295&quot; src=&quot;https://blog.amowu.com/_astro/image26.BVYsA88M_Zm3j7p.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image26.BVYsA88M_Zm3j7p.webp 460w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;210&quot; height=&quot;75&quot; src=&quot;https://blog.amowu.com/_astro/image27.EHCqv6VU_220dbL.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image27.EHCqv6VU_220dbL.webp 210w&quot; /&gt;&lt;/p&gt;
&lt;h3 id=&quot;merge部署&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#merge部署&quot;&gt;Merge＆部署&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;審查之後，確定沒有問題，就可以點選 &lt;code&gt;Merge pull request&lt;/code&gt; 的按鈕，將 &lt;code&gt;test-github-flow&lt;/code&gt; 的程式碼 merge 回主線 &lt;code&gt;master&lt;/code&gt; ：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;155&quot; src=&quot;https://blog.amowu.com/_astro/image28.CfND4L7k_yjTgL.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image28.CfND4L7k_MjnyQ.webp 640w, https://blog.amowu.com/_astro/image28.CfND4L7k_yjTgL.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;182&quot; src=&quot;https://blog.amowu.com/_astro/image29.YQT19mkm_Z1Wnqm.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image29.YQT19mkm_10jraa.webp 640w, https://blog.amowu.com/_astro/image29.YQT19mkm_Z1Wnqm.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;175&quot; height=&quot;70&quot; src=&quot;https://blog.amowu.com/_astro/image30.CNNSbZlT_1tdunL.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image30.CNNSbZlT_1tdunL.webp 175w&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;docker&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#docker&quot;&gt;Docker&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;335&quot; height=&quot;80&quot; src=&quot;https://blog.amowu.com/_astro/image31.hAqQMx9Q_Z2jDITB.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image31.hAqQMx9Q_Z2jDITB.webp 335w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;安裝：&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/boot2docker/boot2docker&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;boot2docker&lt;/a&gt;: 1.5（Mac only）&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.docker.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;docker&lt;/a&gt;: 1.5&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;什麼是 Docker？為什麼要用它？&lt;/p&gt;
&lt;p&gt;因為 Docker 最近很火，所以網路上不缺關於介紹它的文章，原諒我這裡只稍微提一下：&lt;/p&gt;
&lt;p&gt;以往開發人員面對開發環境不同的問題，常常出現「明明在我的電腦上可以跑」的囧境，所以為了解決這類問題，通常會使用虛擬機器（VM）搭配一些工具（ &lt;a href=&quot;https://www.vagrantup.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Vagrant&lt;/a&gt;、 &lt;a href=&quot;https://www.chef.io/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Chef&lt;/a&gt; ）來協助統一開發人員、測試人員、上線產品的執行環境。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;500&quot; height=&quot;257&quot; src=&quot;https://blog.amowu.com/_astro/image32.BRIgKpJS_RYsFR.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image32.BRIgKpJS_RYsFR.webp 500w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Docker 也是類似的解決方案，不同於 VM 的是，Docker 運行起來更輕巧、可攜度更高。配置好一份設定之後，就可以讓大家馬上進入開發狀況，減少不必要的環境問題，提升效率。&lt;/p&gt;
&lt;h2 id=&quot;在-docker-執行nodejs&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#在-docker-執行nodejs&quot;&gt;在 Docker 執行 Node.js&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;在專案根目錄底下建立一個 &lt;code&gt;Dockerfile&lt;/code&gt; ：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# Dockerfile&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# 從 [Docker Hub](https://hub.docker.com/) 安裝 Node.js image。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;node:0.10&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# 設定 container 的預設目錄位置&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;WORKDIR&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/hello-ci-workflow&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# 將專案根目錄的檔案加入至 container&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# 安裝 npm package&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ADD&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/hello-ci-workflow&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;RUN&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;npm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# 開放 container 的 3000 port&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;EXPOSE&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;3000&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;CMD&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;npm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;start&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;使用 &lt;code&gt;docker build&lt;/code&gt; 建構您的 image：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;docker&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;build&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-t&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;hello-ci-workflow&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;*-t hello-ci-workflow*&lt;/code&gt;* 是 image 名稱。*&lt;/p&gt;
&lt;p&gt;使用 &lt;code&gt;docker run&lt;/code&gt; 執行您的 image：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;docker&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-p&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;3000:3000&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-d&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;hello-ci-workflow&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;*-d*&lt;/code&gt;* 在背景執行 node，可以使用 &lt;em&gt;&lt;code&gt;*docker logs*&lt;/code&gt;&lt;/em&gt; 看執行結果。*&lt;/p&gt;
&lt;p&gt;打開瀏覽器 &lt;code&gt;http://localhost:3000&lt;/code&gt; 看結果：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;250&quot; height=&quot;70&quot; src=&quot;https://blog.amowu.com/_astro/image3.D1dxrGZ4_Z7lzxF.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image3.D1dxrGZ4_Z7lzxF.webp 250w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;其實每一次都要 &lt;code&gt;build&lt;/code&gt; 和 &lt;code&gt;run&lt;/code&gt; 還蠻麻煩的，推薦可以試試 &lt;a href=&quot;https://github.com/docker/compose&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Docker Compose&lt;/a&gt;，用起來有點像 &lt;a href=&quot;https://www.vagrantup.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Vagrant&lt;/a&gt; 。&lt;/p&gt;
&lt;h3 id=&quot;在-circleci-測試docker&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#在-circleci-測試docker&quot;&gt;在 CircleCI 測試 Docker&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;修改 &lt;code&gt;circle.yml&lt;/code&gt; ：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame has-title&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;circle.yml&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;machine&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;# 環境改成 docker&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;services&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;docker&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;dependencies&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;override&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;# 建構方式使用 docker build&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;docker build -t hello-ci-workflow .&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;test&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;override&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;./node_modules/.bin/mocha&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;# 使用 curl 測試 docker 是否有順利執行 node&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;docker run -d -p 3000:3000 hello-ci-workflow; sleep 10&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;curl --retry 10 --retry-delay 5 -v http://localhost:3000&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;*-p*&lt;/code&gt;* 可以指定 EB 的應用平台，例如 php 之類；這裡使用 docker。*&lt;/p&gt;
&lt;p&gt;Push 更新到 GitHub：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Dockerfile&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;circle.yml&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;commit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-m&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;add Docker&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;push&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;查看 CircleCI 建構＆測試結果：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;498&quot; height=&quot;806&quot; src=&quot;https://blog.amowu.com/_astro/image34.D2zOUt0G_Z1qflIx.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image34.D2zOUt0G_Z1qflIx.webp 498w&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;aws-elastic-beanstalk&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#aws-elastic-beanstalk&quot;&gt;AWS Elastic Beanstalk&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;300&quot; height=&quot;80&quot; src=&quot;https://blog.amowu.com/_astro/image35.B3Xhu5wo_ZXbNaI.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image35.B3Xhu5wo_ZXbNaI.webp 300w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;帳號：&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://aws.amazon.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Amazon Web Services&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;安裝：&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://docs.aws.amazon.com/elasticbeanstalk/latest/dg/eb-cli3-getting-set-up.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;AWS EB CLI&lt;/a&gt;: 3.x&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;最後要將程式上線啦！現在 PaaS 雲端平台的選擇非常多（ &lt;a href=&quot;https://www.heroku.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Heroku&lt;/a&gt;、 &lt;a href=&quot;https://cloud.google.com/appengine/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Google App Engine&lt;/a&gt;、 &lt;a href=&quot;http://azure.microsoft.com/z&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Azure&lt;/a&gt;、 &lt;a href=&quot;https://www.openshift.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;OpenShift&lt;/a&gt;、 &lt;a href=&quot;https://www.linode.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Linode&lt;/a&gt; ），這裡我選擇 Amazon 推出的 Elastic Beanstalk 當作範例，以下是它的特色：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;支援的開發環境多（Java、.NET、PHP、Node.js、Python、Ruby、GO），重點是有支援 Docker！&lt;/li&gt;
&lt;li&gt;只需要上傳程式碼，Elastic Beanstalk 即可幫你完成從容量配置、負載均衡（load balancing）、自動擴展（auto scaling）到應用程式的運行狀況監控的部署。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;436&quot; height=&quot;393&quot; src=&quot;https://blog.amowu.com/_astro/image36.CF603OnP_ZwOq37.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image36.CF603OnP_ZwOq37.webp 436w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;初始化 EB 環境：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;eb&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;init&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-p&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;docker&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;該命令將提示您配置各種設置。 按 Enter 鍵接受預設值。&lt;/p&gt;
&lt;p&gt;如果你已經存有一組 AWS EB 權限的憑證，該命令會自動使用它。 否則，它會提示您輸入 &lt;code&gt;Access key ID&lt;/code&gt; 和 &lt;code&gt;Secret access key&lt;/code&gt; ，必須前往 AWS IAM 建立一組。&lt;/p&gt;
&lt;p&gt;初始化成功之後，可以使用 &lt;code&gt;eb create&lt;/code&gt; 快速建立各種不同的環境，例如：development, staging, production。&lt;/p&gt;
&lt;p&gt;這裡我們以 &lt;code&gt;env-development&lt;/code&gt; 為例：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;eb&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;env-development&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;等待 Elastic Beanstalk 完成環境的建立。 當它完成之後，您的應用已經備有負載均衡（load-balancing）與自動擴展（autoscaling）的功能了。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;485&quot; height=&quot;562&quot; src=&quot;https://blog.amowu.com/_astro/image37.DfJ3yd_R_ARJLE.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image37.DfJ3yd_R_ARJLE.webp 485w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;使用 &lt;code&gt;eb open&lt;/code&gt; 前往目前版本的執行結果：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;eb&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;open&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;env-development&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;475&quot; height=&quot;70&quot; src=&quot;https://blog.amowu.com/_astro/image38.B3cYhD1b_Zuq93X.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image38.B3cYhD1b_Zuq93X.webp 475w&quot; /&gt;&lt;/p&gt;
&lt;h3 id=&quot;在本地端部署-aws&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#在本地端部署-aws&quot;&gt;在本地端部署 AWS&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;稍微修改 &lt;code&gt;index.js&lt;/code&gt; ：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame has-title&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;index.js&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;// ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;app.&lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;/&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;req&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;res&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;res.&lt;/span&gt;&lt;span&gt;send&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;Hello env-development!&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;// ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;執行 &lt;code&gt;eb deploy&lt;/code&gt; 部署新版本到 AWS Elastic Beanstalk：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;eb&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;deploy&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;env-development&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;341&quot; height=&quot;190&quot; src=&quot;https://blog.amowu.com/_astro/image39.CcncF-3r_Z1uDXXP.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image39.CcncF-3r_Z1uDXXP.webp 341w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;部署完成之後，執行 &lt;code&gt;eb open&lt;/code&gt; 打開網頁：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;eb&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;open&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;env-development&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;471&quot; height=&quot;70&quot; src=&quot;https://blog.amowu.com/_astro/image40.D_J2eWsO_18gQGk.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image40.D_J2eWsO_18gQGk.webp 471w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;env-development&lt;/code&gt; 上的應用程式更新完成。&lt;/p&gt;
&lt;h3 id=&quot;在-circleci-部署aws&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#在-circleci-部署aws&quot;&gt;在 CircleCI 部署 AWS&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;git checkout&lt;/code&gt; 將分支切換回主線 master：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;checkout&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;master&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;eb create&lt;/code&gt; 新增一組新的環境，作為產品上線用，命名為 &lt;code&gt;env-production&lt;/code&gt; ：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;eb&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;env-production&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;eb&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;open&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;env-production&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;471&quot; height=&quot;70&quot; src=&quot;https://blog.amowu.com/_astro/image41.-L4H08BJ_2ewEQa.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image41.-L4H08BJ_2ewEQa.webp 471w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;這樣就成功啟動第二組機器了，目前我們有 &lt;code&gt;env-development&lt;/code&gt; 和 &lt;code&gt;env-production&lt;/code&gt; 兩組環境。&lt;/p&gt;
&lt;h3 id=&quot;前往-aws-iam-新增一組帳號給-circleci使用&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#前往-aws-iam-新增一組帳號給-circleci使用&quot;&gt;前往 &lt;/a&gt;&lt;a href=&quot;https://console.aws.amazon.com/iam/home&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;AWS IAM&lt;/a&gt; 新增一組帳號給 CircleCI 使用：&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;Dashboard&lt;/code&gt; &amp;gt; &lt;code&gt;Users&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Create New Users&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Enter User Names: &lt;strong&gt;CircleCI&lt;/strong&gt; &amp;gt; &lt;code&gt;Create&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Download Credentials&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Dashboard&lt;/code&gt; &amp;gt; &lt;code&gt;Users&lt;/code&gt; &amp;gt; &lt;code&gt;CircleCI&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Attach Pollcy&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AWSElasticBeanstalkFullAccess&lt;/code&gt; &amp;gt; &lt;code&gt;Attach Pollcy&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;前往 CircleCI，設定您的 AWS 權限：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;Project Settings&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Permissions&lt;/code&gt; &amp;gt; &lt;code&gt;AWS Permissions&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;打開剛才下載的 &lt;code&gt;credentials.csv&lt;/code&gt;，輸入 &lt;code&gt;Access Key ID&lt;/code&gt; &amp;amp; &lt;code&gt;Secret Access Key&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Save AWS keys&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;在 &lt;code&gt;.elasticbeanstalk&lt;/code&gt; 目錄底下，建立 &lt;code&gt;config.global.yml&lt;/code&gt;：&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame has-title&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;.elasticbeanstalk/config.global.yml&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;global&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;application_name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;hello-ci-workflow&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;default_region&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;us-west-2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;# EB 所在的 region，預設是 us-west-2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;修改 &lt;code&gt;circle.yml&lt;/code&gt; ：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame has-title&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;circle.yml&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;machine&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;# 安裝 eb 需要 python&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;python&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;version&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;2.7&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;services&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;docker&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;dependencies&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;pre&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;# 安裝 eb&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;sudo pip install awsebcli&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;override&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;docker build -t hello-ci-workflow .&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;test&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;override&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;npm test&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;docker run -d -p 3000:3000 hello-ci-workflow; sleep 10&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;curl --retry 10 --retry-delay 5 -v http://localhost:3000&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# 新增一筆部署腳本&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;deployment&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;production&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;branch&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;master&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;commands&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;eb deploy env-production&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;這樣就能在 GitHub 的 master 支線有更新時，觸發 CircleCI 的自動建置、測試、然後部署。&lt;/p&gt;
&lt;p&gt;接下來馬上來試試看流程，修改 &lt;code&gt;index.js&lt;/code&gt; ：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame has-title&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;index.js&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;// ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;app.&lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;/&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;req&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;res&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;res.&lt;/span&gt;&lt;span&gt;send&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;Hello env-production!&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;// ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Commit &amp;amp; Push：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;commit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-m&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;test deploy production&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;push&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;前往 CircleCI 看結果：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;615&quot; height=&quot;390&quot; src=&quot;https://blog.amowu.com/_astro/image42.Dg8Ag9wZ_Z1TgU3e.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image42.Dg8Ag9wZ_Z1TgU3e.webp 615w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;部署成功， &lt;code&gt;eb open&lt;/code&gt; 打開瀏覽器來看看結果：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;eb&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;open&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;env-production&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;471&quot; height=&quot;70&quot; src=&quot;https://blog.amowu.com/_astro/image43.5GyN-7CE_40jwx.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image43.5GyN-7CE_40jwx.webp 471w&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;slack&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#slack&quot;&gt;Slack&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;200&quot; height=&quot;80&quot; src=&quot;https://blog.amowu.com/_astro/image44.RxsI3_8i_11TdR.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image44.RxsI3_8i_11TdR.webp 200w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;帳號：&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://slack.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Slack&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;到這邊其實已經差不多結束了，最後來講講 Slack 吧。&lt;/p&gt;
&lt;p&gt;Slack 是一款給團隊使用的即時溝通工具，類似的產品還有 &lt;a href=&quot;https://gitter.im/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Gitter&lt;/a&gt; 與 &lt;a href=&quot;https://www.hipchat.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;HipChat&lt;/a&gt; 。&lt;/p&gt;
&lt;p&gt;至於跟 Skype、Lync 這些軟體有什麼不一樣的地方呢？&lt;/p&gt;
&lt;p&gt;它們整合了許多開發工具（GitHub、CircleCI）的服務，例如 GitHub 有新的 push、pull request、issue；CircleCI 的單元測試沒有通過之類的通知，會即時出現在你的團隊的 Slack 上面，既然我們已經將大部分的工作自動化，勢必需要讓相關人員知道這些工具發生了哪些事情，所以使用 Slack 是必要的。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;登入 Slack 頁面&lt;/li&gt;
&lt;li&gt;點選 &lt;code&gt;Configure Integrations&lt;/code&gt; &amp;gt; &lt;code&gt;CircleCI&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;300&quot; height=&quot;365&quot; src=&quot;https://blog.amowu.com/_astro/image45.BLKKuDDV_Z269foq.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image45.BLKKuDDV_Z269foq.webp 300w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;550&quot; height=&quot;75&quot; src=&quot;https://blog.amowu.com/_astro/image46.CXgx99kh_Z1S8pqX.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image46.CXgx99kh_Z1S8pqX.webp 550w&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;選擇要接收 CircleCI 通知的 channel&lt;/li&gt;
&lt;li&gt;點選 &lt;code&gt;Add CircleCI Integration&lt;/code&gt; 按鈕&lt;/li&gt;
&lt;li&gt;複製畫面上的 &lt;code&gt;webhook URL&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;602&quot; height=&quot;196&quot; src=&quot;https://blog.amowu.com/_astro/image47.jh8E3xgh_ZWci39.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image47.jh8E3xgh_ZWci39.webp 602w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;654&quot; height=&quot;78&quot; src=&quot;https://blog.amowu.com/_astro/image48.BSiJUqXX_ZJBR4h.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image48.BSiJUqXX_ZLObMN.webp 640w, https://blog.amowu.com/_astro/image48.BSiJUqXX_ZJBR4h.webp 654w&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;返回 CircleCI&lt;/li&gt;
&lt;li&gt;點選 &lt;code&gt;Project settings&lt;/code&gt; &amp;gt; &lt;code&gt;Chat Notifications&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;貼上將複製的 &lt;code&gt;Webhook URL&lt;/code&gt; &amp;gt; &lt;code&gt;Save&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;207&quot; height=&quot;49&quot; src=&quot;https://blog.amowu.com/_astro/image49.BeF6jdww_1PFBKO.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image49.BeF6jdww_1PFBKO.webp 207w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;220&quot; height=&quot;65&quot; src=&quot;https://blog.amowu.com/_astro/image50.DwlelJf2_ZSPv5S.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image50.DwlelJf2_ZSPv5S.webp 220w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;300&quot; height=&quot;445&quot; src=&quot;https://blog.amowu.com/_astro/image51.CDCDXNy8_2vn7Ou.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image51.CDCDXNy8_2vn7Ou.webp 300w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;類似的步驟，將 GitHub 的通知加入 Slack：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;551&quot; height=&quot;71&quot; src=&quot;https://blog.amowu.com/_astro/image52.BBv1Ux9g_Z2c1sDP.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image52.BBv1Ux9g_Z2c1sDP.webp 551w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;290&quot; height=&quot;160&quot; src=&quot;https://blog.amowu.com/_astro/image53.WUrxIi0w_1Hjuqa.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image53.WUrxIi0w_1Hjuqa.webp 290w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;230&quot; height=&quot;155&quot; src=&quot;https://blog.amowu.com/_astro/image54.BCm0b1bC_1Qz3zD.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image54.BCm0b1bC_1Qz3zD.webp 230w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;測試 Slack 通知，是否能夠順利運作，新增一條 &lt;code&gt;test-slack&lt;/code&gt; 分支：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;branch&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;test-slack&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;checkout&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;test-slack&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;修改 &lt;code&gt;index.js&lt;/code&gt; ：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame has-title&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;index.js&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;// ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;app.&lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;/&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;req&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;res&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;res.&lt;/span&gt;&lt;span&gt;send&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;Hello Slack!&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;// ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Commit &amp;amp; Push：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;index.js&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;commit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-m&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;index.js: update to test slack&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;push&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-u&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;origin&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;test-slack&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;CircleCI 通過測試，開啟一個 Pull Request&lt;/li&gt;
&lt;li&gt;將 &lt;code&gt;test-slack&lt;/code&gt; merge 回 &lt;code&gt;master&lt;/code&gt;，觸發 CircleCI 自動部署&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;515&quot; height=&quot;475&quot; src=&quot;https://blog.amowu.com/_astro/image55.DGEikU3B_GLd2c.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image55.DGEikU3B_GLd2c.webp 515w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;That’s it! On your next build, you’ll start seeing CircleCI build notifications in your Slack chatroom.&lt;/p&gt;
&lt;p&gt;結束！可以看見 Slack channel 會顯示每一個步驟的通知過程：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;450&quot; height=&quot;580&quot; src=&quot;https://blog.amowu.com/_astro/image56.B3lfLekT_1yNEOA.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image56.B3lfLekT_1yNEOA.webp 450w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;eb open&lt;/code&gt; 打開瀏覽器查看結果，成功自動部署新版本：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;eb&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;open&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;env-production&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;471&quot; height=&quot;65&quot; src=&quot;https://blog.amowu.com/_astro/image57.Lapu-7wn_1f2qWp.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image57.Lapu-7wn_1f2qWp.webp 471w&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;結語&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#結語&quot;&gt;結語&lt;/a&gt;&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;「書山有路勤為徑，學海無涯苦作舟。」 — 韓愈&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;DevOps 的開發流程與工具每天都在不斷推陳出新，請站在巨人的肩膀上、保持一顆「活到老、學到老」的心。&lt;/p&gt;
&lt;p&gt;我將這個範例的程式碼放在 &lt;a href=&quot;https://github.com/amowu/hello-ci-workflow&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;GitHub&lt;/a&gt; 上，有興趣的人可以參考看看。&lt;/p&gt;
&lt;p&gt;文章若有需要改進的地方，還請不吝指教，感激不盡。&lt;/p&gt;
&lt;h2 id=&quot;參考&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#參考&quot;&gt;參考&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/DevOps&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;DevOps — Wikipedia&lt;/a&gt; [&lt;a href=&quot;https://zh.wikipedia.org/zh/DevOps&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;中文&lt;/a&gt;]&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Continuous_integration&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Continuous integration（持續整合）- Wikipedia&lt;/a&gt; [&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E6%8C%81%E7%BA%8C%E6%95%B4%E5%90%88&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;中文&lt;/a&gt;]&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Continuous_delivery&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Continuous delivery（持續交付）- Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://blog.eavatar.com/post/2013/10/continuous-integration-deployment-delivery/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;山姆鍋對持續整合、持續部署、持續交付的定義&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://rettamkrad.blogspot.tw/2014/11/integrate-circleci-with-github.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Integrate CircleCI with GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://guides.github.com/introduction/flow/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Understanding the GitHub Flow · GitHub Guides&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.docker.com/examples/nodejs_web_app&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Dockerizing a Node.js Web App&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://circleci.com/integrations/docker&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Integration with Docker Containers — CircleCI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://circleci.com/docs/docker&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Continuous Integration and Delivery with Docker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://docs.aws.amazon.com/elasticbeanstalk/latest/dg/eb-cli3-getting-started.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Getting Started with EB CLI 3.x&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://blog.circleci.com/slack-integration/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Slack Integration | The Circle Blog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://realpython.com/blog/python/docker-in-action-fitter-happier-more-productive/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Docker in Action — Fitter, Happier, More Productive&lt;/a&gt; [&lt;a href=&quot;http://segmentfault.com/a/1190000002598713&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;中文&lt;/a&gt;]&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://qiita.com/sawanoboly/items/28e98827bc044abdc32f&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;CircleCIからAWS Elastic Beanstalkにpush&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://waytothepiratecove.blogspot.tw/2015/03/delivery-pipeline-and-zero-downtime.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Delivery pipeline and zero downtime release&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://sauceio.com/index.php/2014/12/ci-cd-with-docker-beanstalk-circleci-slack-gantree/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Re-Blog: CI &amp;amp; CD With Docker, Beanstalk, CircleCI, Slack, &amp;amp; Gantree&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://mherman.org/blog/2015/03/06/node-with-docker-continuous-integration-and-delivery&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Node With Docker — Continuous Integration and Delivery&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.infoq.com/cn/articles/docker-integrated-test-and-deployment&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;深入浅出Docker（四）：Docker的集成测试部署之道&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>Tech</category></item><item><title>如何優雅地在 Mac 上使用 dotfiles？</title><link>https://blog.amowu.com/posts/2015-01-29-hacker-guide-to-setting-up-your-mac/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2015-01-29-hacker-guide-to-setting-up-your-mac/</guid><description>註：這篇文章的原始碼放在 GitHub ，會以上面的更新為主。</description><pubDate>Thu, 29 Jan 2015 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;註：這篇文章的原始碼放在 &lt;a href=&quot;https://github.com/amowu/dotfiles&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;GitHub&lt;/a&gt; ，會以上面的更新為主。&lt;/p&gt;
&lt;p&gt;如果不清楚什麼是 dotfiles 的話，可以參閱 &lt;a href=&quot;http://cloudchen.logdown.com/posts/49264746647/dotfiles&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;善用 dotfiles 個人化自己的工作環境&lt;/a&gt; ：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;*dotfiles，顧名思義，就是檔案名稱以 . (dot) 為 prefix 的檔案通稱，若是您的作業系統是 Mac OS X 或是 Linux 這類 &lt;em&gt;nix-based 的作業系統，一般來說在視窗環境中是看不到這些檔案的，因為對系統來說，他們是所謂的隱藏檔，這些檔案有一些共通點，那就是他們通常用來儲存一些個人化的設定或是自定的拓展功能，以符合使用者本身的使用需求與習慣，有了這些設定好的檔案之後，使用者可以讓整個系統用起來更為順手，並且大幅提昇他們自身的工作效率！因此對某些使用者來說，這些 dotfiles 設定檔對他們來說，反而可能是他們機器上最重要的檔案呢！&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;這份 dotfiles 是 fork 自 &lt;a href=&quot;https://github.com/holman/dotfiles&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Holman’s dotfiles&lt;/a&gt; ，並根據個人的需求修改過，如果有興趣，閱讀完這份文件之後，歡迎 fork 一份回去配置成適合自己的 dotfiles。&lt;/p&gt;
&lt;p&gt;更多的 dotfiles 請參考 &lt;a href=&quot;https://dotfiles.github.io/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;GitHub does dotfiles&lt;/a&gt; 。&lt;/p&gt;
&lt;h2 id=&quot;erase-and-reinstall-osx&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#erase-and-reinstall-osx&quot;&gt;Erase and reinstall OS X&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;如果你打算從乾淨的 Mac 環境開始，請參閱 &lt;a href=&quot;http://support.apple.com/zh-tw/HT5943&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;OS X：如何清除並安裝&lt;/a&gt; 。&lt;/p&gt;
&lt;h2 id=&quot;install-xcode&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#install-xcode&quot;&gt;Install Xcode&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;更新 App Store。&lt;/li&gt;
&lt;li&gt;安裝 &lt;a href=&quot;https://itunes.apple.com/us/app/xcode/id497799835?mt=12&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Xcode&lt;/a&gt;。&lt;/li&gt;
&lt;li&gt;開啟 Terminal，安裝 Xcode Command Line Tools：&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;xcode-select&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--install&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2 id=&quot;install-dotfiles&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#install-dotfiles&quot;&gt;Install dotfiles&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/amowu/dotfiles/archive/master.zip&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;下載&lt;/a&gt;或使用 git clone 一份到 &lt;code&gt;$HOME&lt;/code&gt; 目錄底下的 &lt;code&gt;.dotfiles&lt;/code&gt; 資料夾裡面：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;clone&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;https://github.com/amowu/dotfiles.git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/.dotfiles&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;進入 &lt;code&gt;.dotfiles&lt;/code&gt; 資料夾：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;cd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/.dotfiles&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;安裝 dotfiles：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;script/bootstrap&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;bootstrap.sh&lt;/code&gt; 這個程式會自動完成以下工作：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;檢查並安裝 &lt;a href=&quot;http://brew.sh/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Homebrew&lt;/a&gt;。&lt;/li&gt;
&lt;li&gt;檢查並安裝 &lt;a href=&quot;http://ohmyz.sh/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Oh My Zsh&lt;/a&gt;。&lt;/li&gt;
&lt;li&gt;檢查並連結 dotfiles(&lt;code&gt;.zshrc&lt;/code&gt;, &lt;code&gt;.vimrc&lt;/code&gt;, &lt;code&gt;.gitconfig&lt;/code&gt;,&lt;code&gt;.gitignore&lt;/code&gt;, …)。&lt;/li&gt;
&lt;li&gt;更新並安裝 brew packages(binaries, fonts, apps)。&lt;/li&gt;
&lt;li&gt;設置 Mac OS X 的 defaults settings。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;完成之後，手動安裝一些 App Store 上才有的軟體(Dash, Moom, …)。&lt;/p&gt;
&lt;h2 id=&quot;restore-backup&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#restore-backup&quot;&gt;Restore backup&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;使用 &lt;a href=&quot;https://github.com/lra/mackup&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Mackup&lt;/a&gt; 進行備份回復：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;mackup&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;restore&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;什麼是 Mackup？底下會介紹。&lt;/p&gt;
&lt;h2 id=&quot;dotfiles&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#dotfiles&quot;&gt;dotfiles&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;執行 &lt;code&gt;~/.dotfiles/script/bootstrap&lt;/code&gt; 的時候，腳本會將目錄底下所有的 &lt;code&gt;*.symlink&lt;/code&gt; 檔案透過 &lt;code&gt;ln&lt;/code&gt; 命令建立連結至&lt;code&gt;$HOME&lt;/code&gt; 目錄底下：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;390&quot; height=&quot;236&quot; src=&quot;https://blog.amowu.com/_astro/image1.D68-poQk_Z1Dbrd2.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image1.D68-poQk_Z1Dbrd2.webp 390w&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;topical&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#topical&quot;&gt;Topical&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;每一個環境的配置是以資料夾的形式被獨立區分。例如，如果想要新增 “Java” 的配置到 dotfiles，你可以簡單的新增一個命名為 &lt;code&gt;java&lt;/code&gt; 的資料夾，然後將檔案建至目錄底下。 任何副檔名是 &lt;code&gt;.zsh&lt;/code&gt; 的檔案將在 shell 執行時被自動載入至環境中。 任何副檔名是 &lt;code&gt;.symlink&lt;/code&gt;的檔案將在你執行 &lt;code&gt;script/bootstrap&lt;/code&gt; 安裝時被連結至 &lt;code&gt;$HOME&lt;/code&gt; 目錄底下。&lt;/p&gt;
&lt;h2 id=&quot;components&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#components&quot;&gt;Components&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;一些目錄中比較特別的檔案：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;bin/&lt;/strong&gt;: 任何在 &lt;code&gt;bin/&lt;/code&gt; 目錄底下的檔案可以在 shell 執行時直接使用。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;topic/*.zsh&lt;/strong&gt;: 任何 &lt;code&gt;.zsh&lt;/code&gt; 結尾的檔案都會在 shell 執行時被載入至環境。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;topic/path.zsh&lt;/strong&gt;: 任何命名為 &lt;code&gt;path.zsh&lt;/code&gt; 的檔案會在 shell 執行時優先被載入至 &lt;code&gt;$PATH&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;topic/*.symlink&lt;/strong&gt;: 任何 &lt;code&gt;*.symlink&lt;/code&gt; 結尾的檔案都會在 &lt;code&gt;$HOME&lt;/code&gt; 目錄底下建立連結。這可以讓你在配置環境的時候也保持版本控制的優點。新增 symlink 的時候需要執行 &lt;code&gt;script/bootstrap&lt;/code&gt; 安裝。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;不同於 &lt;a href=&quot;https://github.com/holman/dotfiles&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Holman’s dotfiles&lt;/a&gt; ，我修改了一些部分：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Shell 的部分改用 &lt;a href=&quot;http://ohmyz.sh/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Oh My Zsh&lt;/a&gt;取代原作者自己配置的 zsh。&lt;/li&gt;
&lt;li&gt;移除 &lt;strong&gt;topic/aliases.zsh&lt;/strong&gt;、&lt;strong&gt;topic/completion.zsh&lt;/strong&gt; 等檔案，改用 Oh My Zsh 的 [plugins]。(&lt;a href=&quot;https://github.com/robbyrussell/oh-my-zsh/wiki/Plugins&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://github.com/robbyrussell/oh-my-zsh/wiki/Plugins&lt;/a&gt;) 代替。&lt;/li&gt;
&lt;li&gt;移除 &lt;strong&gt;zsh/prompt.zsh&lt;/strong&gt;、&lt;strong&gt;zsh/window.zsh.zsh&lt;/strong&gt; 等檔案，改用 Oh My Zsh 的 [themes]。(&lt;a href=&quot;https://github.com/robbyrussell/oh-my-zsh/wiki/Themes&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://github.com/robbyrussell/oh-my-zsh/wiki/Themes&lt;/a&gt;) 代替。&lt;/li&gt;
&lt;li&gt;dotfiles 只專注在 &lt;strong&gt;topic/*.symlink&lt;/strong&gt;、&lt;strong&gt;topic/path.zsh&lt;/strong&gt; 的配置。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;os-x&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#os-x&quot;&gt;OS X&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;bin/dot&lt;/code&gt; 是一支簡單的腳本，會在 &lt;code&gt;script/bootstrap&lt;/code&gt; 配置完 dotfiles 之後執行，安裝自定的 OS X 程式並設定系統參數配置。執行 &lt;code&gt;dot&lt;/code&gt; 之後，它會跑以下兩支腳本檔：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;$HOME/.dotfiles/homebrew/install.sh&lt;/code&gt; - Homebrew packages&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$HOME/.dotfiles/osx/set-defaults.sh&lt;/code&gt; - OS X defaults setting&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;homebrew-packages&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#homebrew-packages&quot;&gt;Homebrew packages&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;執行 &lt;code&gt;homebrew/install.sh&lt;/code&gt; 的時候，腳本會使用 &lt;a href=&quot;http://brew.sh/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Homebrew&lt;/a&gt; 和 &lt;a href=&quot;http://caskroom.io/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Homebrew Cask&lt;/a&gt; 來安裝 &lt;strong&gt;binary&lt;/strong&gt;、&lt;strong&gt;font&lt;/strong&gt; 還有 &lt;strong&gt;app&lt;/strong&gt;，可以根據個人需求修改這個檔案，增加或減少自己需要的 packages：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;binaries&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;git&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;tree&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;字型都是以 &lt;strong&gt;font-XXX&lt;/strong&gt; 的形式命名，可以用 &lt;code&gt;brew cask search /font-XXX/&lt;/code&gt; 搜尋是否存在。&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;fonts&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;font-roboto&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;應用程式可以用 &lt;code&gt;brew cask search XXX&lt;/code&gt; 或是 &lt;a href=&quot;http://caskroom.io/search&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Cask Search&lt;/a&gt; 網站搜尋是否存在。&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;apps&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;dropbox&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;google-chrome&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;以下是目前安裝的 packages：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;450&quot; height=&quot;696&quot; src=&quot;https://blog.amowu.com/_astro/image2.BNL96wOq_FJnz2.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image2.BNL96wOq_FJnz2.webp 450w&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;os-x-defaultssetting&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#os-x-defaultssetting&quot;&gt;OS X defaults setting&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;執行 &lt;code&gt;osx/set-defaults.sh&lt;/code&gt; 之後，程式會將 Mac OS X 的一些系統設置改變，可以根據個人需求修改這個檔案，或是參考 &lt;a href=&quot;https://github.com/mathiasbynens/dotfiles/blob/master/.osx&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Mathias’s dotfiles&lt;/a&gt; 整理好的配置。以下是目前設定的配置：&lt;/p&gt;
&lt;p&gt;加快視窗 resize 的速度(Cocoa applications)&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;defaults&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;NSGlobalDomain&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;NSWindowResizeTime&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-float&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0.001&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;預設展開儲存視窗&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;defaults&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;NSGlobalDomain&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;NSNavPanelExpandedStateForSaveMode&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-bool&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;defaults&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;NSGlobalDomain&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;NSNavPanelExpandedStateForSaveMode2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-bool&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;關閉”你確定要開啟這個應用程式?”詢問視窗&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;defaults&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.apple.LaunchServices&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;LSQuarantine&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-bool&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;開啟觸控板輕觸點擊功能&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;defaults&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.apple.driver.AppleBluetoothMultitouch.trackpad&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Clicking&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-bool&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;defaults&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-currentHost&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;NSGlobalDomain&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.apple.mouse.tapBehavior&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-int&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;defaults&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;NSGlobalDomain&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.apple.mouse.tapBehavior&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-int&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;開啟觸控板兩指輕觸彈出右鍵選單功能&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;defaults&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.apple.driver.AppleBluetoothMultitouch.trackpad&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TrackpadRightClick&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-bool&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;開啟觸控板三指拖曳功能&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;defaults&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-currentHost&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;NSGlobalDomain&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.apple.trackpad.threeFingerDragGesture&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-bool&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;defaults&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.apple.driver.AppleBluetoothMultitouch.trackpad&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TrackpadThreeFingerDrag&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-bool&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;開啟觸控板四指向下滑出現 app expose 功能&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;defaults&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.apple.AppleMultitouchTrackpad&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TrackpadThreeFingerVertSwipeGesture&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-int&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;defaults&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.apple.driver.AppleBluetoothMultitouch.trackpad&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TrackpadThreeFingerVertSwipeGesture&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-int&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;defaults&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.apple.dock&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;showAppExposeGestureEnabled&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-int&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;加快觸控板/滑鼠的速度&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;defaults&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;NSGlobalDomain&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.apple.trackpad.scaling&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-int&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;defaults&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;NSGlobalDomain&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.apple.mouse.scaling&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-int&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;開啟所有視窗組件支援鍵盤控制&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;defaults&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;NSGlobalDomain&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AppleKeyboardUIMode&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-int&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;關閉鍵盤按住的輸入限制&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;defaults&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;NSGlobalDomain&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ApplePressAndHoldEnabled&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-bool&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;加快鍵盤輸入&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;defaults&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;NSGlobalDomain&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;KeyRepeat&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-int&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;預設 Finder 起始位置為桌面&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;defaults&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.apple.finder&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;NewWindowTarget&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-string&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;“PfDe”&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;defaults&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.apple.finder&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;NewWindowTargetPath&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-string&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;“file://&lt;/span&gt;&lt;span&gt;${HOME}&lt;/span&gt;&lt;span&gt;/Desktop/”&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;顯示副檔名&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;defaults&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;NSGlobalDomain&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AppleShowAllExtensions&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-bool&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;顯示 Finder 狀態列&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;defaults&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.apple.finder&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ShowStatusBar&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-bool&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;顯示 Finder 路徑列&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;defaults&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.apple.finder&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ShowPathbar&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-bool&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;允許框選 Finde Quick Look 的文字&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;defaults&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.apple.finder&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;QLEnableTextSelection&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-bool&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Finder 標題列顯示完整路徑&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;defaults&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.apple.finder&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;_FXShowPosixPathInTitle&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-bool&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;預設搜尋列的結果為當前目錄下&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;defaults&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.apple.finder&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;FXDefaultSearchScope&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-string&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;“SCcf”&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;關閉改變副檔名的警告提示&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;defaults&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.apple.finder&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;FXEnableExtensionChangeWarning&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-bool&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;開啟資料夾的 spring loading 功能&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;defaults&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;NSGlobalDomain&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.apple.springing.enabled&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-bool&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;開啟 Dock 的 spring loading 功能&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;defaults&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.apple.dock&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;enable-spring-load-actions-on-all-items&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-bool&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;移除 spring loading 的延遲&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;defaults&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;NSGlobalDomain&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.apple.springing.delay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-float&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;避免在 network volumes 底下建立 .DS_Store 檔案&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;defaults&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.apple.desktopservices&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;DSDontWriteNetworkStores&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-bool&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;使用 column view 作為 Finder 預設顯示選項&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;defaults&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.apple.finder&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;FXPreferredViewStyle&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-string&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;“clmv”&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;關閉清空垃圾桶的警告提示&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;defaults&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.apple.finder&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;WarnOnEmptyTrash&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-bool&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;使用黑色的選單列和 Dock&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;defaults&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;NSGlobalDomain&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AppleInterfaceStyle&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Dark&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;使用縮放的效果作為視窗放大縮小效果&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;defaults&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.apple.dock&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mineffect&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-string&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;“scale”&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;應用程式縮小至自己的圖示&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;defaults&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.apple.dock&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;minimize-to-application&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-bool&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;顯示 Dock 應用程式開啟中的小亮燈提示&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;defaults&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.apple.dock&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;show-process-indicators&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-bool&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;關閉 Dock 開啟應用程式的彈跳動畫&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;defaults&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.apple.dock&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;launchanim&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-bool&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;加快 Mission Control 的動畫速度&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;defaults&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.apple.dock&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;expose-animation-duration&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-float&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0.1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;關閉 Mission Control 的應用程式群組化顯示&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;defaults&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.apple.dock&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;expose-group-by-app&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-bool&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;關閉 Dashboard&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;defaults&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.apple.dashboard&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mcx-disabled&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-bool&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;將 Dashboard 從多重桌面之中移除&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;defaults&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.apple.dock&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;dashboard-in-overlay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-bool&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;自動隱藏 Dock&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;defaults&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.apple.dock&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;autohide&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-bool&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;移除隱藏 Dock 的延遲&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;defaults&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.apple.dock&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;autohide-delay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-float&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;移除 Dock 的顯示/隱藏動畫&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;defaults&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.apple.dock&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;autohide-time-modifier&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-float&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;將隱藏的應用程式 Dock 圖示半透明顯示&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;defaults&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.apple.dock&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;showhidden&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-bool&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;以上，若修改過 &lt;code&gt;homebrew/install.sh&lt;/code&gt; 或 &lt;code&gt;osx/set-defaults.sh&lt;/code&gt; 之後，直接執行指令：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;dot&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;就會再次更新 packages 還有 defaults setting。&lt;/p&gt;
&lt;h2 id=&quot;mackup&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#mackup&quot;&gt;Mackup&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;當初始環境都安裝好之後，剩下的就是恢復備份。除了 &lt;code&gt;.zsrc&lt;/code&gt;、&lt;code&gt;.vimrc&lt;/code&gt; 這類 dotfile 比較適合放在版本控制之外，其他像是 Sublime 的 plugin、iTerm2 的 setting、Oh My Zsh 的 plugin、等等很多還有一般應用程式的配置檔需要備份，甚至是 SSH 的 key，這些我認為都不適合丟進 dotfiles 放上 GitHub。所以這裡介紹 &lt;a href=&quot;https://github.com/lra/mackup&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Mackup&lt;/a&gt; 這個簡單的工具作為解決方案，使用方式很簡單， &lt;code&gt;brew install mackup&lt;/code&gt; 安裝完之後只要執行：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;mackup&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;backup&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;就可以將檔案備份到 Dropbox 或 Google Drive。當需要恢復的時候則是執行：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;mackup&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;restore&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;就會將雲端硬碟上的備份以 &lt;code&gt;ln&lt;/code&gt; 連結的方式在新電腦上同步。&lt;/p&gt;
&lt;p&gt;配置方式也很容易，建立一份 &lt;code&gt;~/.mackup.cfg&lt;/code&gt;，或是直接使用 &lt;code&gt;.dofiles/mackup/mackup.cfg.symlink&lt;/code&gt; 來修改：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;[storage]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;engine&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;dropbox&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;# 同步的雲端硬碟，有 dropbox 與 google_drive 可以選擇&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;directory&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Mackup&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;# 同步的資料夾，這裡會將所有備份同步至 ~/Dropbox/Mackup 底下&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# 指定要同步的應用程式&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;[applications_to_sync]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;iterm2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;oh-my-zsh&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;sublime-text-3&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ssh&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;[applications_to_ignore]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# 指定不想同步的應用程式&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;以下是目前備份的應用程式：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;260&quot; height=&quot;274&quot; src=&quot;https://blog.amowu.com/_astro/image3.DvO_5-Gj_2lCYHs.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image3.DvO_5-Gj_2lCYHs.webp 260w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;更多詳細的配置與支援的軟體請參閱 &lt;a href=&quot;https://github.com/lra/mackup/tree/master/doc&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;mackup 的文件&lt;/a&gt; 。&lt;/p&gt;
&lt;h2 id=&quot;參考文章&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#參考文章&quot;&gt;參考文章&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://lapwinglabs.com/blog/hacker-guide-to-setting-up-your-mac&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Hacker’s Guide to Setting up Your Mac&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://carlosbecker.com/posts/first-steps-with-mac-os-x-as-a-developer/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;First steps with Mac OS X as a Developer&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>Tech</category></item><item><title>AngularJS + Google Spreadsheet</title><link>https://blog.amowu.com/posts/2014-03-10-angularjs-google-spreadsheet/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2014-03-10-angularjs-google-spreadsheet/</guid><description>概要</description><pubDate>Mon, 10 Mar 2014 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;概要&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#概要&quot;&gt;概要&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;這篇文章主要是說明如何寫 AngularJS 的 service，以類似 &lt;code&gt;[$resource](http://code.angularjs.org/1.2.6/docs/api/ngResource.%24resource)&lt;/code&gt; REST 的方式來取得 Google 試算表的資料。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;本文假設讀者已讀過 &lt;a href=&quot;http://docs.angularjs.org/tutorial&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;AngularJS Tutorial&lt;/a&gt;，了解 AngularJS 基本概念。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;目錄&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#目錄&quot;&gt;目錄&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;建立試算表&lt;/li&gt;
&lt;li&gt;Template&lt;/li&gt;
&lt;li&gt;App Module&lt;/li&gt;
&lt;li&gt;Service&lt;/li&gt;
&lt;li&gt;Controller&lt;/li&gt;
&lt;li&gt;注意事項&lt;/li&gt;
&lt;li&gt;參考&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;建立試算表&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#建立試算表&quot;&gt;建立試算表&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;前往 &lt;a href=&quot;https://drive.google.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://drive.google.com&lt;/a&gt; 或是參考已經建立好的 &lt;a href=&quot;https://docs.google.com/spreadsheet/ccc?key=0AhPhtlCrkuIFdEQ0TzNsSUl0QmFMdmU3QUcxRlhJV1E&amp;amp;usp=drive_web&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;試算表&lt;/a&gt; 。&lt;/p&gt;
&lt;p&gt;先建立一份新的試算表： &lt;code&gt;建立&lt;/code&gt; &amp;gt; &lt;code&gt;試算表&lt;/code&gt; 。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;233&quot; height=&quot;395&quot; src=&quot;https://blog.amowu.com/_astro/image1.wBdiLRyA_db230.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image1.wBdiLRyA_db230.webp 233w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;填寫資料表，以 &lt;a href=&quot;http://docs.angularjs.org/tutorial&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;AngularJS Tutorial&lt;/a&gt; 的 &lt;a href=&quot;https://github.com/angular/angular-phonecat/blob/master/app/phones/phones.json&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;phones.json&lt;/a&gt; 為例。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;626&quot; src=&quot;https://blog.amowu.com/_astro/image2.BoBoVM2l_1xlArn.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image2.BoBoVM2l_1vb7As.webp 640w, https://blog.amowu.com/_astro/image2.BoBoVM2l_1xlArn.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;將試算表權限公開： &lt;code&gt;檔案&lt;/code&gt; &amp;gt; &lt;code&gt;共用&lt;/code&gt; &amp;gt; &lt;code&gt;擁有存取權的使用者&lt;/code&gt; &amp;gt; &lt;code&gt;變更&lt;/code&gt; 。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;518&quot; src=&quot;https://blog.amowu.com/_astro/image3.D1NcFCdH_XPaPP.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image3.D1NcFCdH_Z1CtCxr.webp 640w, https://blog.amowu.com/_astro/image3.D1NcFCdH_XPaPP.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;共用設定&lt;/code&gt; &amp;gt; &lt;code&gt;公開在網路上&lt;/code&gt; &amp;gt; &lt;code&gt;存取權&lt;/code&gt; &amp;gt; &lt;code&gt;可檢視&lt;/code&gt; &amp;gt; &lt;code&gt;儲存&lt;/code&gt; 。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;568&quot; height=&quot;414&quot; src=&quot;https://blog.amowu.com/_astro/image4.CTcL5hKk_Z29jbYk.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image4.CTcL5hKk_Z29jbYk.webp 568w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;權限公開後，發佈到網路： &lt;code&gt;檔案&lt;/code&gt; &amp;gt; &lt;code&gt;發佈到網路&lt;/code&gt; &amp;gt; &lt;code&gt;內容有所變更時自動更新發佈&lt;/code&gt; &amp;gt; &lt;code&gt;開始發佈&lt;/code&gt; 。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;521&quot; src=&quot;https://blog.amowu.com/_astro/image5.BGuKE9ra_pegOc.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image5.BGuKE9ra_Z2aCCOL.webp 640w, https://blog.amowu.com/_astro/image5.BGuKE9ra_pegOc.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;發佈完成後，公開連結中會提供一組 key，可以前往 &lt;a href=&quot;https://developers.google.com/gdata/samples/spreadsheet_sample&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Google Data APIs&lt;/a&gt; 測試看看資料是否回傳正確。&lt;/p&gt;
&lt;p&gt;接下來進入正式寫程式的階段，這裡我們會使用到 &lt;a href=&quot;https://github.com/jsoma/tabletop&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Tabletop.js&lt;/a&gt; 這個第三方的 library，提供 javascript 快速取得 json 格式的試算表資料。&lt;/p&gt;
&lt;p&gt;這裡會以 &lt;a href=&quot;http://docs.angularjs.org/tutorial&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;AngularJS Tutorial&lt;/a&gt; 的 &lt;a href=&quot;https://github.com/angular/angular-phonecat&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;phonecatApp&lt;/a&gt; 為例子，說明如何實作 Tabletop.js 的 service。&lt;/p&gt;
&lt;h2 id=&quot;template&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#template&quot;&gt;Template&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;建立 &lt;code&gt;index.html&lt;/code&gt;，並加入 &lt;a href=&quot;https://github.com/angular/angular.js/tree/v1.2.6&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;angular.js&lt;/a&gt;， &lt;a href=&quot;https://github.com/angular/bower-angular-route/tree/v1.2.6&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;angular-route.js&lt;/a&gt; 和 &lt;a href=&quot;https://github.com/jsoma/tabletop/tree/v1.3.3&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;tabletop.js&lt;/a&gt;。
&lt;code&gt;index.html&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;!doctype html&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ng-app&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;phonecatApp&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; ng-view&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;src&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;angular.js&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;src&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;angular-route.js&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;src&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;tabletop.js&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;src&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;js/app.js&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;src&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;js/controllers.js&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;src&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;js/services.js&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;app.js，controller.js 和 services.js 後面會說明，接下來先加入 phone-list.html。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;partials/phone-list.html:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ng-repeat&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;phone in phones&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;img&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ng-src&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;{{phone.imageurl}}&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;{{phone.name}}&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;{{phone.snippet}}&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;phone-list.html&lt;/code&gt; 綁定了 &lt;code&gt;name&lt;/code&gt;， &lt;code&gt;snippet&lt;/code&gt; 和 &lt;code&gt;imageurl&lt;/code&gt; 三個變數，對應試算表的欄位名稱。&lt;/p&gt;
&lt;h2 id=&quot;app-module&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#app-module&quot;&gt;App Module&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;接著使用 &lt;code&gt;angular-route.js&lt;/code&gt; 提供的 &lt;code&gt;ngRoute&lt;/code&gt; 來幫 app 設定路由，這裡先一律導向 &lt;code&gt;/phones&lt;/code&gt; 。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;js/app.js&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; phonecatApp &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; angular.&lt;/span&gt;&lt;span&gt;module&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;phonecatApp&apos;&lt;/span&gt;&lt;span&gt;, [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&apos;ngRoute&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&apos;phonecatServices&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&apos;phonecatControllers&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;]);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;phonecatApp.&lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt;([&lt;/span&gt;&lt;span&gt;&apos;$routeProvider&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$routeProvider&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;$routeProvider.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;when&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;/phones&apos;&lt;/span&gt;&lt;span&gt;, {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;templateUrl: &lt;/span&gt;&lt;span&gt;&apos;partials/phone-list.html&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;controller: &lt;/span&gt;&lt;span&gt;&apos;PhoneListCtrl&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}).&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;otherwise&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;redirectTo: &lt;/span&gt;&lt;span&gt;&apos;/phones&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}]);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;接下來要開始寫 &lt;code&gt;phonecatServices&lt;/code&gt; 和 &lt;code&gt;phonecatControllers&lt;/code&gt; 。&lt;/p&gt;
&lt;h2 id=&quot;service&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#service&quot;&gt;Service&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;這裡將 tabletop.js 封裝成一個 &lt;code&gt;Phone&lt;/code&gt; 的 &lt;a href=&quot;http://code.angularjs.org/1.2.6/docs/guide/dev_guide.services.creating_services&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;service&lt;/a&gt;，並自訂一個 &lt;code&gt;query&lt;/code&gt; 方法提供試算表查詢使用。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;js/services.js&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; phonecatServices &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; angular.&lt;/span&gt;&lt;span&gt;module&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;phonecatServices&apos;&lt;/span&gt;&lt;span&gt;, []);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;phonecatServices.&lt;/span&gt;&lt;span&gt;factory&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;Phone&apos;&lt;/span&gt;&lt;span&gt;, [&lt;/span&gt;&lt;span&gt;&apos;$rootScope&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$rootScope&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;query&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;callback&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Tabletop.&lt;/span&gt;&lt;span&gt;init&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;key: &lt;/span&gt;&lt;span&gt;&apos;YOUR_KEY&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;simpleSheet: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;parseNumbers: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;          &lt;/span&gt;&lt;span&gt;callback&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;tabletop&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;(callback &lt;/span&gt;&lt;span&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;typeof&lt;/span&gt;&lt;span&gt;(callback) &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;function&quot;&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;$rootScope.&lt;/span&gt;&lt;span&gt;$apply&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;                &lt;/span&gt;&lt;span&gt;callback&lt;/span&gt;&lt;span&gt;(data);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}]);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;key&lt;/code&gt; 的部分記得填寫試算表提供的 key。&lt;/p&gt;
&lt;h2 id=&quot;controller&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#controller&quot;&gt;Controller&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;最後剩下 &lt;code&gt;phonecatControllers&lt;/code&gt;，使用 &lt;code&gt;Phone.query&lt;/code&gt; 取得試算表的 json 格式資料。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;js/controllers.js&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; phonecatControllers &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; angular.&lt;/span&gt;&lt;span&gt;module&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;phonecatControllers&apos;&lt;/span&gt;&lt;span&gt;, []);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;phonecatControllers.&lt;/span&gt;&lt;span&gt;controller&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;PhoneListCtrl&apos;&lt;/span&gt;&lt;span&gt;, [&lt;/span&gt;&lt;span&gt;&apos;$scope&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;Phone&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$scope&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;Phone&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Phone.&lt;/span&gt;&lt;span&gt;query&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;$scope.phones &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; data;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}]);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;完成，完整範例程式碼請參考 GitHub &lt;a href=&quot;https://github.com/amowu/angular-spreadsheet-sample&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;angular-spreadsheet-sample&lt;/a&gt; 。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;442&quot; height=&quot;599&quot; src=&quot;https://blog.amowu.com/_astro/image6.DrzugoAU_2gtFkT.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image6.DrzugoAU_2gtFkT.webp 442w&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;注意事項&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#注意事項&quot;&gt;注意事項&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;新版 Google 試算表不支援。&lt;/li&gt;
&lt;li&gt;試算表欄位名稱只能小寫，例如：&lt;code&gt;imgUrl&lt;/code&gt;，要改成 &lt;code&gt;imgurl&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;參考&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#參考&quot;&gt;參考&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://docs.angularjs.org/tutorial&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;AngularJS Tutorial&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developers.google.com/gdata/samples/spreadsheet_sample&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Spreadsheets Data API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/jsoma/tabletop&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Tabletop.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://coderwall.com/p/duapqq&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Use a Google Spreadsheet as your JSON backend&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>Tech</category></item><item><title>書摘《遊戲化的時代》</title><link>https://blog.amowu.com/posts/2014-02-28-gamification/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2014-02-28-gamification/</guid><description>在 2011 年，Gamification (遊戲化) 繼 Web 2.0，Mashup，Cloud，Freemium 之後，成為網路上科技的熱門關鍵詞。  Gamification 將會是Social media、Big data、Internet of Things…</description><pubDate>Fri, 28 Feb 2014 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;在 2011 年，&lt;/em&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Gamification&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;em&gt;Gamification&lt;/em&gt;&lt;/a&gt;* (遊戲化) 繼 &lt;em&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Web_2.0&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;em&gt;Web 2.0&lt;/em&gt;&lt;/a&gt;&lt;/em&gt;，&lt;em&gt;&lt;a href=&quot;https://goo.gl/3mWtjK&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;em&gt;Mashup&lt;/em&gt;&lt;/a&gt;&lt;/em&gt;，&lt;em&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Cloud_computing&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;em&gt;Cloud&lt;/em&gt;&lt;/a&gt;&lt;/em&gt;，&lt;em&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Freemium&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;em&gt;Freemium&lt;/em&gt;&lt;/a&gt;&lt;/em&gt; 之後，成為網路上科技的熱門關鍵詞。
 Gamification 將會是*&lt;a href=&quot;https://en.wikipedia.org/wiki/Social_media&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;em&gt;Social media&lt;/em&gt;&lt;/a&gt;&lt;em&gt;、&lt;/em&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Big_data&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;em&gt;Big data&lt;/em&gt;&lt;/a&gt;&lt;em&gt;、&lt;/em&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Internet_of_things&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;em&gt;Internet of Things&lt;/em&gt;&lt;/a&gt;* 的下一波網路趨勢。*&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;持續步行的工具就是遊戲化&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#持續步行的工具就是遊戲化&quot;&gt;持續「步行」的工具就是遊戲化&lt;/a&gt;&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;知道步行對健康有益，但要付諸行動真的很難。… 遊戲化漸漸地扮演起重要的角色，為了持續每天的健康步行運動，只要帶著遊戲化工具，每天無聊的步行就會得到回饋。&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;142&quot; height=&quot;179&quot; src=&quot;https://blog.amowu.com/_astro/image1.BB9tOpQS_26tE7r.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image1.BB9tOpQS_26tE7r.webp 142w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;1998 年發售的「口袋皮卡丘」結合計步器和電子寵物，是成功的遊戲化案例。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;480&quot; height=&quot;281&quot; src=&quot;https://blog.amowu.com/_astro/image2.Bc41x1J5_ZhToKo.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image2.Bc41x1J5_ZhToKo.webp 480w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;近幾年熱門的健康管理 App 之一， &lt;a href=&quot;https://secure-nikeplus.nike.com/plus/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Nike+&lt;/a&gt; 運用智慧型手機的 GPS 和感應器來達成有趣的慢跑應用。&lt;/p&gt;
&lt;h2 id=&quot;serious-game與遊戲化的不同&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#serious-game與遊戲化的不同&quot;&gt;Serious Game 與遊戲化的不同&lt;/a&gt;&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;「&lt;/em&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Serious_game&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;em&gt;Serious Game&lt;/em&gt;&lt;/a&gt;* 是將社會上的各種問題帶到遊戲裏，而遊戲化是將遊戲帶到社會的各個角落。」*&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;480&quot; height=&quot;640&quot; src=&quot;https://blog.amowu.com/_astro/image3.C4-Oa9et_ZJbeQ7.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image3.C4-Oa9et_ZJbeQ7.webp 480w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;小便斗加上一個簡單的靶心之後，就能增添許多樂趣。&lt;/p&gt;
&lt;h2 id=&quot;會員制與遊戲化哪裏不同&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#會員制與遊戲化哪裏不同&quot;&gt;會員制與遊戲化哪裏不同？&lt;/a&gt;&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;思考使用者的「動機」，動機的概念可分為外在動機與內在動機。外在動機指的是因報酬，賞罰等理由所被激發的動機，內在動機則相反，並非因為外來的報酬或賞罰，而是被活動內容所激發的內在動機。&lt;strong&gt;&lt;strong&gt;所謂的遊戲化，既是想要謀得報酬的外在動機，同時也是能驅動內在動機的結構&lt;/strong&gt;&lt;/strong&gt;。&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;400&quot; height=&quot;309&quot; src=&quot;https://blog.amowu.com/_astro/image4.0KlqSFej_1EgB7d.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image4.0KlqSFej_1EgB7d.webp 400w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Starbucks 利用簡單的集點遊戲規則讓消費者參與解決環保問題。詳見 &lt;a href=&quot;http://www.bnext.com.tw/article/view/id/15191&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Karma Cup&lt;/a&gt;。
 註： &lt;a href=&quot;http://www.onearth.org/article/meet-the-change-makers-starbucks-quest-for-a-better-cup&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;圖片來源 OnEarth&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;受到量化的我們&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#受到量化的我們&quot;&gt;受到量化的我們&lt;/a&gt;&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;在印刷技術上，「油墨」扮演重要的角色。而在遊戲化，同樣也需要各種「測量的技術」。&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;480&quot; height=&quot;310&quot; src=&quot;https://blog.amowu.com/_astro/image5.B0OpEhtL_Zc29jt.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image5.B0OpEhtL_Zc29jt.webp 480w&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;如果沒有碼表記錄這些時間，世界選手大會或奧運的競技，根本無法成立。&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;註： &lt;a href=&quot;http://www.washingtonpost.com/blogs/london-2012-olympics/?attachment_id=8917&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;圖片來源 Washington Post&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;480&quot; height=&quot;262&quot; src=&quot;https://blog.amowu.com/_astro/image6.Bu8g98XZ_1nc0xE.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image6.Bu8g98XZ_1nc0xE.webp 480w&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;「不譴責，不罰款，而是獎賞！」
 福斯汽車 2010 年舉辦了一個名為「車速照相樂透的活動」，並不是針對超速車輛的取締罰款，而是表揚遵守車速駕駛的偵測系統，試行期間平均車速下降了 22%。&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;能用數據計算的東西，要變成遊戲比較容易，相反地，判斷標準不一的對象就比較難以變成遊戲，例如評價一幅畫。&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;遊戲變成生活化之後遊戲化就會擴大散播&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#遊戲變成生活化之後遊戲化就會擴大散播&quot;&gt;遊戲變成生活化之後，遊戲化就會擴大散播？&lt;/a&gt;&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;「知之者不如好之者，好之者不如&lt;/em&gt;&lt;em&gt;&lt;strong&gt;樂之者&lt;/strong&gt;**。」
 印刷技術是結合造紙術和冶金術，再由能看懂文字的知識族群拓展。同樣地，遊戲化也是因為有智慧手機，&lt;/em&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Lifelog&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;em&gt;Lifelog&lt;/em&gt;&lt;/a&gt;* 與眾多的玩家才能夠有今日的發展。*&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;遊戲之所以會進展得那麼快原因如下&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#遊戲之所以會進展得那麼快原因如下&quot;&gt;遊戲之所以會進展得那麼快，原因如下：&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;測量技術的進步&lt;/li&gt;
&lt;li&gt;網路的發達讓「時間」有了戲劇性的變化&lt;/li&gt;
&lt;li&gt;遊戲世代的成熟&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;測量技術的進步&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#測量技術的進步&quot;&gt;測量技術的進步&lt;/a&gt;&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;透過許多感應器和 &lt;em&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Lifelog&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;em&gt;Lifelog&lt;/em&gt;&lt;/a&gt;&lt;/em&gt; 的功能，已可以從生活中的各個「空間」中得到要成立遊戲所需的數值，原本壓根無法變成遊戲的各種領域裏，因為有了上述的功能，使得原本不能實現的遊戲變得可能了。&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;網路的發達讓時間有了戲劇性的變化&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#網路的發達讓時間有了戲劇性的變化&quot;&gt;網路的發達讓「時間」有了戲劇性的變化&lt;/a&gt;&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;比「遊戲」更重要的一件事，時間的獲得。
 在活版印刷的時代「紙」所發揮的功能，就相當於現代的智慧型手機所帶來的快速一樣。為什麼「時間」的取得會那麼重要呢？其實應該說「任何時候都能確認遊戲的結果」才是更重要的。
 玩家在遊戲裏做了什麼動作，透過社群網路，馬上就會有人回覆訊息。即使沒有任何人回應，電腦也會成為「對手」，穩定用戶的結構早已完備。&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;遊戲世代的成熟&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#遊戲世代的成熟&quot;&gt;遊戲世代的成熟&lt;/a&gt;&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;以活版印刷時代為例，「必須有一定以上的人識字」才有辦法拓展活版印刷技術。同樣地，要促進遊戲化最後的條件就是「遊戲世代的成熟」。
 玩遊戲的「人」增加了，而那個世代的人在社會的權責地位愈高，追求「遊戲」的機會也就愈高。遊戲的世代隨著時間的經過總算超過半數了。&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;空間時間人&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#空間時間人&quot;&gt;「空間」「時間」「人」&lt;/a&gt;&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;「空間」「時間」「人」，這麼複雜的條件正好在 2010 年代帶來極大的影響力。正因為要擴大遊戲化的條件全都齊全了，遊戲化就要開始展開。&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;遊戲改變商業&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#遊戲改變商業&quot;&gt;遊戲改變商業&lt;/a&gt;&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;迪士尼認為要提升「顧客滿意度」，首先要提升「員工滿意度」。
 「遊戲化並不是讓人不認真，鬧著玩的東西，而是喚起人的本性，讓人認真工作的工具。」&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;思考遊戲化&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#思考遊戲化&quot;&gt;思考遊戲化&lt;/a&gt;&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;「任何人都覺得有趣的遊戲架構並不存在」，我們想的不應該是「加進甚麼結構應該會很好玩吧？」，而是「想讓玩家是甚麼的話，這樣設計應該不錯」。&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;玩家的操作與設計者的期待不一致&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#玩家的操作與設計者的期待不一致&quot;&gt;玩家的操作與設計者的期待不一致&lt;/a&gt;&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;事實上，讓玩家感受勝利的快樂，並不是拓展遊戲化業者的主要目的，他們希望從玩家身上賺取利潤，希望玩家透過遊戲學習某件事，或者是希望玩家對社會有實質的貢獻等。不同於電腦遊戲，遊戲化的主要目的並不是提供玩家娛樂。另一方面，大部分玩家的目的不是從自己口袋掏錢出去，更不是想對社會有什麼幫助，只是想開心地玩個遊戲而已。兩者的動機不一致，在建構遊戲化時，會是很大的問題和阻礙。&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;太慢的遊戲讓人容易產生厭倦&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#太慢的遊戲讓人容易產生厭倦&quot;&gt;太慢的「遊戲」讓人容易產生厭倦&lt;/a&gt;&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;並不是只要注入遊戲元素就能夠增加固定用戶，遊戲還有另外一個現象，那就是「厭倦」。一般常見容易上癮的遊戲設計，基於道德倫理的觀念，也經常受到許多人的否定。因此，&lt;strong&gt;&lt;strong&gt;不需要設計易使人成癮的遊戲&lt;/strong&gt;&lt;/strong&gt;，只需要讓大部分使用者「慢慢地厭倦」就可以。因為這樣的結構設計可以讓用戶反覆利用網路服務。&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;遊戲化只是拉條輔助線而已&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#遊戲化只是拉條輔助線而已&quot;&gt;遊戲化只是拉條輔助線而已&lt;/a&gt;&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;「世界上幾乎所有事，也許都含有遊戲的要素在其中」。並不是任何事只要遊戲化就一定能成功。例如，有人像是在玩遊戲一樣愛上做菜，卻有人不知道如何將做菜當作是遊戲一般享受。&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;結語&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#結語&quot;&gt;結語&lt;/a&gt;&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;遊戲，應該早就存在於我們所能想像的歷史之前，只是遊戲，遊玩的概念經常以配角之姿出現，所以我們才沒有注意到遊戲。
 對社會而言，與其推廣各種多樣的遊戲，我們可以更進一步描繪未來的藍圖，讓人們可以將遊戲當作更幸福的科技使用。那就是「開放設計遊戲的權限」。&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;「過去十年，是社群的十年。未來十年，是遊戲十年。遊戲的力量將會影響每個人的行為，是非常強力且令人興奮的力量。各位！請一起開心的製作遊戲，使用遊戲吧！」&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;</content:encoded><category>Tech</category></item><item><title>重點筆記《Hackers and Painters》</title><link>https://blog.amowu.com/posts/2013-10-01-hackers-and-painters/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2013-10-01-hackers-and-painters/</guid><description>我們的時代是程式設計師主導的時代，而偉大的程式設計師就是 hacker。</description><pubDate>Tue, 01 Oct 2013 00:00:00 GMT</pubDate><content:encoded>&lt;ul&gt;
&lt;li&gt;我們的時代是程式設計師主導的時代，而偉大的程式設計師就是 hacker。&lt;/li&gt;
&lt;li&gt;Hacker 追求的三個特色，好玩，高智商，探索精神，而不是實用性和金錢。&lt;/li&gt;
&lt;li&gt;Hacker 倫理之二，你可以用電腦創造美和藝術，使生活更美好。&lt;/li&gt;
&lt;li&gt;惡意入侵電腦系統的人應該被稱為 cracker，hacker 是指那些最優秀的程式設計師。&lt;/li&gt;
&lt;li&gt;應該把 hacker 和 畫家 當作同一種人看待，他們都是創作者。&lt;/li&gt;
&lt;li&gt;行為怪異和憤世嫉俗的人比普通人更可能成為 hacker。&lt;/li&gt;
&lt;li&gt;Hacker 的最高境界是創造規格。&lt;/li&gt;
&lt;li&gt;賺錢的軟體往往不是好玩的軟體。&lt;/li&gt;
&lt;li&gt;Hacker 如何才能做自己喜歡的事情？答案是像音樂家一樣，找一份養家糊口的“白天工作”。&lt;/li&gt;
&lt;li&gt;面試程式設計師的時候，主要關注的就是業餘時間他們寫了什麼軟體。&lt;/li&gt;
&lt;li&gt;應該定期的從頭開始，而不要長年累月在同一個項目上，試圖把最新想法加進去。&lt;/li&gt;
&lt;li&gt;Hacker 可以通過觀看優秀的程式學習程式設計，不是看它們的執行結果。&lt;/li&gt;
&lt;li&gt;一幅畫是逐步完成的，過早優化是一件危險的事情。&lt;/li&gt;
&lt;li&gt;Hacker 就像畫家，工作起來是有心理周期的。有時候你有了一個令人興奮的新項目，願意為它一天工作16個小時。但過了一陣子又會覺得無聊，對所有事情都提不起興趣。&lt;/li&gt;
&lt;li&gt;正確的合作方法是將項目分割成嚴格定義的模塊，每一個模塊由一個人明確負責。&lt;/li&gt;
&lt;li&gt;從他人的角度思考問題正是成功的奧秘所在。&lt;/li&gt;
&lt;li&gt;判斷一個人是否具備 “換位思考” 的能力有一個好方法，那就是看他怎樣像沒有技術背景的人解釋技術問題。&lt;/li&gt;
&lt;li&gt;在達文西的年代，繪畫並不是一件很酷的事情，達文西用自己的工作推動繪畫成為一種偉大的表達方式。同樣，程式設計到底能夠有多酷，取決於我們能夠用這種新媒介做出怎樣的工作。&lt;/li&gt;
&lt;li&gt;只有深入了解當前的技術，hacker 才能構想下一代技術，知識產權的擁有者也許會說不，我們不需要你的幫助，我們自己就能開發下一代技術。他們錯了，在電腦工業歷史上，新技術往往是由外部人員開發的，所占比例高於內部人員。&lt;/li&gt;
&lt;li&gt;如果我們不可以研究當前的技術，不能思考如何改進它，那麼我們怎樣才能開發出新技術呢？&lt;/li&gt;
&lt;li&gt;如果 hacker 有自己的節日，那就是 4 月 1 日愚人節，你可以放心地作弄其他人。&lt;/li&gt;
&lt;li&gt;專制國家會腐敗，腐敗國家會貧窮，貧窮國家會弱小。&lt;/li&gt;
&lt;li&gt;不要在半夜發佈程式碼，然後回家睡覺。&lt;/li&gt;
&lt;li&gt;管理企業其實很簡單，只要記住兩點：做出用戶喜歡的產品，保證開支小於收入。你就會超過大多數創業公司，隨著事業發展，你自己就能琢磨出其他的訣竅。&lt;/li&gt;
&lt;li&gt;幾個 hacker 搞懂如何租用辦公室，或者如何雇用銷售人員，要比那些公司搞懂如何正確寫出軟體容易得多。&lt;/li&gt;
&lt;li&gt;想致富？最好的辦法就是自己創業，或者加入創業公司。你想安穩的工作四十年，還是全力拼四年？&lt;/li&gt;
&lt;li&gt;隨著工業化時代的來臨，手工藝人越來越少。目前還存在的最大手工藝人群體就是程式設計師。&lt;/li&gt;
&lt;li&gt;如果解決方法是醜陋的，那就肯定還有更好的解決方法，只是還沒有發現而已。&lt;/li&gt;
&lt;li&gt;那些內核最小，最乾淨的程式語言才會存在于進化的主幹上，因為它們的生命力最頑強。&lt;/li&gt;
&lt;li&gt;現在被認為運行速度慢的語言，將來會有更大的發展空間。&lt;/li&gt;
&lt;li&gt;浪費程式設計師的時間而不是浪費機器的時間才是真正沒效率的。隨著電腦速度越來越快，這會變得越來越明顯。&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>Tech</category></item><item><title>Trello workflow</title><link>https://blog.amowu.com/posts/2013-10-01-workflow/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2013-10-01-workflow/</guid><description>團隊開發的網頁遊戲《末日少女》已經上市快兩個月了，成績比當初想像中還好，相較於 Gu Morning，這次《末日少女》的開發學到很多寶貴的經驗，稍微鬆口氣之餘，也有許多需要檢討改善的地方，而《末日少女》Mobile…</description><pubDate>Tue, 01 Oct 2013 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;團隊開發的網頁遊戲&lt;a href=&quot;http://tw.new.beanfun.com/xd/www/index.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;《末日少女》&lt;/a&gt;已經上市快兩個月了，成績比當初想像中還好，相較於 &lt;a href=&quot;http://www.gamania.com/mobilegame/gumorning/ch/index.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Gu Morning&lt;/a&gt;，這次《末日少女》的開發學到很多寶貴的經驗，稍微鬆口氣之餘，也有許多需要檢討改善的地方，而《末日少女》Mobile 版其實也已經開發好一陣子了，最近這個專案只剩下我一個人交到我手上負責，趁這個機會導入一些新的開發方式，也 PO 上來分享心得。&lt;/p&gt;
&lt;h2 id=&quot;這篇文章會講什麼&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#這篇文章會講什麼&quot;&gt;這篇文章會講什麼？&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;HtpChat — 開發團隊的 Log 整合中心。&lt;/li&gt;
&lt;li&gt;Trello — 團隊的專案管理小白板。&lt;/li&gt;
&lt;li&gt;Git flow — 最佳程式開發流程。&lt;/li&gt;
&lt;li&gt;Jenkins — CI(持續整合)建構工具。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;hipchat&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#hipchat&quot;&gt;HipChat&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;300&quot; height=&quot;88&quot; src=&quot;https://blog.amowu.com/_astro/image1.CIT3VJRM_Z1uN3QY.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image1.CIT3VJRM_Z1uN3QY.webp 300w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.hipchat.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;HipChat&lt;/a&gt; 是一款跨平台團隊溝通工具，為了節省開會浪費的時間，通常會使用 &lt;a href=&quot;https://zh.wikipedia.org/wiki/Microsoft_Lync&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Lync&lt;/a&gt; 或 &lt;a href=&quot;https://zh.wikipedia.org/wiki/Skype&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Skype&lt;/a&gt; 這類通訊軟體，而 HipChat 除了該有的聊天功能，歷史查詢，附件上傳，@mentions 通知之外，最棒的是它可以整合許多服務，例如 &lt;a href=&quot;https://github.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;GitHub&lt;/a&gt;，&lt;a href=&quot;https://www.heroku.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Heroku&lt;/a&gt;，&lt;a href=&quot;https://www.atlassian.com/software/jira&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;JIRA&lt;/a&gt; 等，將所有開發上分散的訊息統一管理，方便團隊隨時瞭解狀況，而接下來這篇文章會講到如何整合 &lt;a href=&quot;https://www.blogger.com/blogger.g?blogID=696354394590316778&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Trello&lt;/a&gt;，&lt;a href=&quot;https://www.blogger.com/blogger.g?blogID=696354394590316778&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;GitHub&lt;/a&gt; 和 &lt;a href=&quot;https://www.blogger.com/blogger.g?blogID=696354394590316778&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Jenkins&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;528&quot; height=&quot;385&quot; src=&quot;https://blog.amowu.com/_astro/image2.B00E5rki_1PrEM3.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image2.B00E5rki_1PrEM3.webp 528w&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;trello&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#trello&quot;&gt;Trello&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;300&quot; height=&quot;96&quot; src=&quot;https://blog.amowu.com/_astro/image3.CTPVpvCY_ZjALSs.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image3.CTPVpvCY_ZjALSs.webp 300w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;不同於一般市面上複雜的專案管理軟體，&lt;a href=&quot;https://zh.wikipedia.org/wiki/Scrum&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Trello&lt;/a&gt; 是一個簡單易用的 &lt;a href=&quot;https://zh.wikipedia.org/wiki/Scrum&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Scrum&lt;/a&gt; board，之前 &lt;a href=&quot;https://blog.amowu.com/posts/2012-08-02-development-tools/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Development Tools&lt;/a&gt;這篇文章有稍微介紹，而我目前就是使用它來管理新專案，試用兩個月下來，感覺還不錯。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;356&quot; src=&quot;https://blog.amowu.com/_astro/image4.D3afuQco_r6LR5.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image4.D3afuQco_ZmtSBf.webp 640w, https://blog.amowu.com/_astro/image4.D3afuQco_r6LR5.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Trello 主要由 Board，List 和 Card 組成，如上圖所示，目前我將開發板分成五個 List：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Backlog&lt;/strong&gt; — 所有企劃開出來的功能會集中在這個 Backlog List，並按照預計完成日期排序。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;To Do&lt;/strong&gt; — Backlog 中的功能如果已經準備好企劃文件和美術檔案，那卡片就會移到 To Do 待命。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Doing&lt;/strong&gt; — 正在實作中的功能會從 To Do 移至 Doing，建議每個人只留一張卡片在這。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Done (vX.X.X)&lt;/strong&gt; — 下一版要完成的功能，在 Doing 完成的卡片會移到 Done，待產品發佈後會從 Done 改為 Live。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Live (vX.X.X)&lt;/strong&gt; — 已經上線的功能 List，可以隨時使用封存，讓開發板保持乾淨。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;vX.X.X 為版號。
每張 Card 還可以根據屬性標上自訂 Label，例如 feature，bug 等。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;zapier&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#zapier&quot;&gt;Zapier&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;由於 HipChat 沒有直接整合 Trello，所以需要透過第三方服務來支援，&lt;a href=&quot;https://zapier.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Zapier&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;300&quot; height=&quot;155&quot; src=&quot;https://blog.amowu.com/_astro/image5.Be8U77lb_Z1dneiI.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image5.Be8U77lb_Z1dneiI.webp 300w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Zapier 使用上跟 &lt;a href=&quot;https://ifttt.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;IFTTT&lt;/a&gt; 一樣，當某個服務被觸發時，另一個服務就執行某個動作，而這邊我們要使用的就是 Trello 和 HipChat，當 Trello 的 New Activity 被 trigger 的時候，HipChat 就 Create Message。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;91&quot; src=&quot;https://blog.amowu.com/_astro/image6.ZMEmtP-e_1uOqUg.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image6.ZMEmtP-e_1Y72fu.webp 640w, https://blog.amowu.com/_astro/image6.ZMEmtP-e_1uOqUg.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;連結 Trello 和 HipChat 帳號：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;292&quot; src=&quot;https://blog.amowu.com/_astro/image7.AiJ4emGF_Z23Vksc.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image7.AiJ4emGF_2cfRVG.webp 640w, https://blog.amowu.com/_astro/image7.AiJ4emGF_Z23Vksc.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;選擇 Trello Board：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;499&quot; height=&quot;527&quot; src=&quot;https://blog.amowu.com/_astro/image8.CKxui3ct_2f1Yzb.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image8.CKxui3ct_2f1Yzb.webp 499w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;設定 HipChat 的通知格式：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;494&quot; height=&quot;767&quot; src=&quot;https://blog.amowu.com/_astro/image9.BFekrN-g_8nWkC.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image9.BFekrN-g_8nWkC.webp 494w&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;也可以直接套用其他人分享的&lt;a href=&quot;https://zapier.com/app/explore?services=hipchat%2Ctrello&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;設定&lt;/a&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;git-flow&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#git-flow&quot;&gt;Git flow&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/nvie/gitflow&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Git flow&lt;/a&gt; 是一套 Git 流程管理工具，它會幫你在現有的 Git 開發環境上，建立五條 branch：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;master&lt;/strong&gt; — 最穩定的產品版本。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;develop&lt;/strong&gt; — 正在開發的版本，所有開發人員基本上都會在這條主線上。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;feature&lt;/strong&gt; — 開發新功能都需要使用這條支線，會從 develop 分支出來，完成後自動 merge 回 develop。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;release&lt;/strong&gt; — 準備發佈的新版本，從 develop 分支出來，完成後自動建立 tag 並 merge 回 master 和 develop。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;hotfix&lt;/strong&gt; — release 之後發現有緊急 bug 必須修復時會使用，從 master 分支出來，完成後 merge 回 master 和 develop。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;這篇 &lt;a href=&quot;http://danielkummer.github.io/git-flow-cheatsheet/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;git-flow cheatsheet&lt;/a&gt; 用圖解的方式解釋 git flow，會比較容易懂。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;安裝&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#安裝&quot;&gt;安裝&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;OSX：&lt;code&gt;$ brew install git-flow&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Linux：&lt;code&gt;$ apt-get install git-flow&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;使用&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#使用&quot;&gt;使用&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;初始化：&lt;code&gt;$ git flow init&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;初始過程中會問你一些分支命名，建議直接使用預設。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;new-feature&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#new-feature&quot;&gt;New Feature&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;開始開發新功能：&lt;code&gt;$ git flow feature start &amp;lt;FEATURE_NAME&amp;gt;&lt;/code&gt;
結束新功能開發：&lt;code&gt;$ git flow feature finish &amp;lt;FEATURE_NAME&amp;gt;&lt;/code&gt;
將程式 push 到 remote 端：&lt;code&gt;$ git flow feature publish &amp;lt;FEATURE_NAME&amp;gt;&lt;/code&gt;
Pull remote 端的程式：&lt;code&gt;$ git flow feature pull &amp;lt;FEATURE_NAME&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;h3 id=&quot;new-release&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#new-release&quot;&gt;New Release&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;準備 release：&lt;code&gt;$ git flow release start &amp;lt;VERSION&amp;gt;&lt;/code&gt;
完成 release：&lt;code&gt;$ git flow release finish &amp;lt;VERSION&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;VERSION 為版號。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;new-hotfix&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#new-hotfix&quot;&gt;New Hotfix&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;修復緊急 bug：&lt;code&gt;$ git flow hotfix start &amp;lt;VERSION&amp;gt;&lt;/code&gt;
修復結束：&lt;code&gt;$ git flow hotfix finish &amp;lt;VERSION&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;如果想從 SVN 跳槽到 Git 的話，可以使用這個指令將整個 Repository 搬過來：
&lt;code&gt;$ git svn clone &amp;lt;SVN_REPOSITORY&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;如果不習慣用命令列操作的話，推薦使用 &lt;a href=&quot;http://www.sourcetreeapp.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;SourceTree&lt;/a&gt; 這套軟體，裡面已經直接內建 git flow。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;393&quot; src=&quot;https://blog.amowu.com/_astro/image10.C3TZECTZ_gXsog.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image10.C3TZECTZ_Z1JIXyx.webp 640w, https://blog.amowu.com/_astro/image10.C3TZECTZ_gXsog.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;接著要整合 GitHub 和 HipChat，只需要幾個步驟就可以完成：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;前往 HipChat &amp;gt; Group admin &amp;gt; API &amp;gt; 建立一組新的 token。&lt;/li&gt;
&lt;li&gt;前往 GitHub &amp;gt; 你的 Repository &amp;gt; Settings &amp;gt; Service Hooks &amp;gt; HipChat &amp;gt; 將 auth token 跟 room ID 填上，Active 記得打勾，這樣就完成了，之後 GitHub 有任何動態都會在 HipChat 上通知。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;jenkins&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#jenkins&quot;&gt;Jenkins&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;300&quot; height=&quot;96&quot; src=&quot;https://blog.amowu.com/_astro/image11.BZUdzWYF_ZRuV1a.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image11.BZUdzWYF_ZRuV1a.webp 300w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;最後要介紹的是&lt;a href=&quot;http://en.wikipedia.org/wiki/Continuous_integration&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;持續整合&lt;/a&gt;工具 &lt;a href=&quot;http://jenkins-ci.org/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Jenkins&lt;/a&gt;，為了減少每次發佈版本浪費的時間，Jenkins 可以自動化這些鎖碎的步驟，而我目前專案的每日建置會處理的事項如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;定時輪詢 Git 是否有最新的版本。&lt;/li&gt;
&lt;li&gt;若是有最新版本則拉下來更新。&lt;/li&gt;
&lt;li&gt;編譯程式檢查是否有錯誤。&lt;/li&gt;
&lt;li&gt;封裝檔案 (.ipa &amp;amp; .apk)。&lt;/li&gt;
&lt;li&gt;通知 HipChat。&lt;/li&gt;
&lt;li&gt;將檔案 Email 給指定成員。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;安裝-1&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#安裝-1&quot;&gt;安裝&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;前往&lt;a href=&quot;http://jenkins-ci.org/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;官方網站&lt;/a&gt;下載對應版本。&lt;/p&gt;
&lt;h2 id=&quot;設定&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#設定&quot;&gt;設定&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;安裝完畢之後，預設直接使用瀏覽器開啟 &lt;code&gt;http://localhost:8080&lt;/code&gt;，接下來需要安裝一些 Plugins：
前往 Manage Jenkins &amp;gt; Manage Plugins &amp;gt; Available &amp;gt; 右上角 Filter 搜尋可安裝的外掛。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://wiki.jenkins-ci.org/display/JENKINS/Git+Plugin&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Git Plugin&lt;/a&gt; — 如果你是使用 Git 的話當然就必裝囉。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://wiki.jenkins-ci.org/display/JENKINS/HipChat+Plugin&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;HipChat Plugin&lt;/a&gt; — 整合 HipChat，安裝完之後 &amp;gt; Manage Jenkins &amp;gt; Configure System &amp;gt; Global HipChat Notifier Settings &amp;gt; 新增一組 HipCht API token，然後填上 Room 跟 Jenkins URL 就完成了。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://wiki.jenkins-ci.org/display/JENKINS/Email-ext+plugin&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Email-ext plugin&lt;/a&gt; — 建置成功可以發送自訂格式的 Email。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;安裝完外掛之後接下來點選 New Job 開啟一個新建置：
填寫 Job name，這裡我們選 Free-Style project。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;528&quot; height=&quot;403&quot; src=&quot;https://blog.amowu.com/_astro/image12.C2fOEAqI_4zSdJ.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image12.C2fOEAqI_4zSdJ.webp 528w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;創建好一個 Job 後會進入 Configure 頁面。&lt;/p&gt;
&lt;p&gt;首先先設定 HipChat 的一些參數 (如果有安裝 plugin 的話)。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;313&quot; height=&quot;168&quot; src=&quot;https://blog.amowu.com/_astro/image13.DBnWsr8u_Z1SQSQG.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image13.DBnWsr8u_Z1SQSQG.webp 313w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;設定 Git，填上本機 Repository 位置，選擇 master branch (產品主線)，Remote 端 Repository 如果是用 GitHub 的話選擇 githubweb 然後填上 URL。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;514&quot; height=&quot;481&quot; src=&quot;https://blog.amowu.com/_astro/image14.AOIT4Gad_ZyGBta.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image14.AOIT4Gad_ZyGBta.webp 514w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;接下來設定排程，選擇 Poll SCM (輪詢)，並依照指定的日期時間格式設定，以 &lt;code&gt;H/5 6,7,8,9,20,21,22,23 * * *&lt;/code&gt; 為例，無論何月何日星期幾，每天 6-9 點，20-23點，每隔 5 分鐘輪詢一次。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;377&quot; height=&quot;235&quot; src=&quot;https://blog.amowu.com/_astro/image15.9ANPbIFY_Z1Sim6L.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image15.9ANPbIFY_Z1Sim6L.webp 377w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;然後就是建置的部分，內容會依專案而異，以我的 AIR Mobile 專案為例，使用 Shell 指令，&lt;code&gt;mxmlc&lt;/code&gt; 先編譯除錯程式，然後 &lt;code&gt;adt&lt;/code&gt; 分別產出 .ipa 和 .apk 檔案。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;540&quot; height=&quot;283&quot; src=&quot;https://blog.amowu.com/_astro/image16.C3hGu5CA_28MW3L.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image16.C3hGu5CA_28MW3L.webp 540w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;最後設定建置成功後需要作哪些事：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Archive the artifacts — 封存指定檔案，注意檔案的路徑會是在 &lt;code&gt;$WORKSPACE&lt;/code&gt; 底下。&lt;/li&gt;
&lt;li&gt;HipChat Notification — 發送通知給 HipChat。&lt;/li&gt;
&lt;li&gt;E-mail Notification — 發送信件給指定成員，如果需要自訂格式也可以使用前面介紹的 plugin。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;455&quot; height=&quot;483&quot; src=&quot;https://blog.amowu.com/_astro/image17.KXvDPboX_2gU3sP.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image17.KXvDPboX_2gU3sP.webp 455w&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;結語&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#結語&quot;&gt;結語&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;上面講完了 HipChat，Trello，Git flow 和 Jenkins 四樣看似無關的服務，但結合起來運用會讓專案開發上的效率更佳提升，雖然前期的準備需要花點時間，但對專案是很值得的投資，Try it! :-)&lt;/p&gt;
&lt;h3 id=&quot;參考資料&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#參考資料&quot;&gt;參考資料&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://ihower.tw/blog/archives/5140&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Git flow 開發流程&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://danielkummer.github.io/git-flow-cheatsheet/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;git-flow cheatsheet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://edwardinaction.blogspot.tw/2011/04/git-flow-dev-team.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Git-flow 讓 Dev Team 步上穩健開發之路&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://fann.im/blog/2012/03/12/git-flow-notes/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Git-flow 使用笔记&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://dev.classmethod.jp/tool/jenkins_testflight_hipchat/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;JenkinsでiOSアプリ開発の細々した作業を自動化する&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.riaspace.com/2011/03/packaging-air-application-for-ios-devices-with-adt-command-and-ant-script/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Packaging AIR application for iOS devices with ADT command and ANT script&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://rettamkrad.blogspot.tw/2013/01/NodeAppCI.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;NodeJS App的持續整合（Continuous Integration）伺服器架設&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://jeffreylands.blogspot.tw/2010/03/blog-post.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;每日自動備份, 檔名如何加上日期 ?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://blog.csdn.net/wirelessqa/article/details/8647771&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;【Jenkins常见问题解决】01. Mac上使用Jenkins持续集成报错Can’t connect to window server — not enough permissions.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.cnblogs.com/gao241/archive/2013/03/20/2971416.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Jenkins配置基于角色的项目权限管理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.infoq.com/cn/articles/MSBuild-2&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;用MSBuild和Jenkins搭建持续集成环境（2）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.dotblogs.com.tw/kirkchen/archive/2012/05/14/install_and_setting_jenkins_as_ci_server.aspx&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;使用Jenkins打造Continuous Integration Server (1) — 安裝以及環境設定&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://ithelp.ithome.com.tw/question/10109471&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;CI Server 28 — 發送每日建置結果報表&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.juvenxu.com/2011/05/18/hudson-email-ext/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;使用email-ext替换Jenkins(Hudson)的默认邮件通知&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://help.adobe.com/en_US/flashbuilder/using/WSe4e4b720da9dedb5-6caff02f136a645e895-7ffd.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Packaging and exporting&lt;/a&gt; -&lt;a href=&quot;http://help.adobe.com/zh_TW/air/build/WS901d38e593cd1bac25d3d8c712b2d86751e-8000.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;使用 Flex SDK 建立您的第一個 AIR for Android 應用程式&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://help.adobe.com/zh_TW/air/build/WS901d38e593cd1bac1e63e3d128cdca935b-8000.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;ADT package 命令&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://help.adobe.com/en_US/AIR/1.5/devappshtml/WS5b3ccc516d4fbf351e63e3d118666ade46-7f74.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Creating a self-signed certificate with ADT&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://stackoverflow.com/questions/6844650/why-is-flexmojos-unable-to-resolve-assets-png-for-transcoding&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Why is FlexMojos “unable to resolve ‘assets/**/*.png’ for transcoding”?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://stackoverflow.com/questions/5919138/an-error-occurred-because-there-is-no-graphics-environment-available&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;An error occurred because there is no graphics environment available&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>Tech</category></item><item><title>COSCUP 2013</title><link>https://blog.amowu.com/posts/2013-08-06-coscup-2013/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2013-08-06-coscup-2013/</guid><description>今年是第二次參加開源人年度聚會 COSCUP，很幸運的在 47 秒內搶到限量 500 張的門票，和去年比較不一樣的地方：</description><pubDate>Tue, 06 Aug 2013 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;今年是第二次參加&lt;a href=&quot;http://coscup.org/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;開源人年度聚會 COSCUP&lt;/a&gt;，很幸運的在 47 秒內搶到限量 500 張的門票，和&lt;a href=&quot;https://blog.amowu.com/posts/2012-08-19-coscup-2012/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;去年&lt;/a&gt;比較不一樣的地方：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;場地從&lt;a href=&quot;http://hssb.committee.sinica.edu.tw/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;中研院&lt;/a&gt;換到&lt;a href=&quot;http://www.ticc.com.tw/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;國際會議中心&lt;/a&gt;，應該是因為人數的關係，不過我猜 TICC 沒有下次了(笑)，期待明年的小巨蛋(誤)。&lt;/li&gt;
&lt;li&gt;八軌議程，同個時段最多有八場演講可以選擇！&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;分享一些這兩天簡單的筆記。&lt;/p&gt;
&lt;h2 id=&quot;day-1&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#day-1&quot;&gt;DAY 1&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;第一天有點小遲到，好險大會準備的 NFC 識別證快速通關，加上場地夠大，還很多位置可以坐。&lt;/p&gt;
&lt;h3 id=&quot;open-data-initiatives-fortaiwan&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#open-data-initiatives-fortaiwan&quot;&gt;Open Data Initiatives for Taiwan&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;一開始開場是由 行政院政務委員 &lt;a href=&quot;http://zh.wikipedia.org/wiki/%E5%BC%B5%E5%96%84%E6%94%BF&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;張善政&lt;/a&gt; 演講，主要是聊一些台灣政府目前 Open Data 的情況，認識了兩個網站：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://data.gov.tw/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;data.gov.tw&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://g0v.tw/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;g0v.tw&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;前者是政府的資料開放平台，後者是最近很紅的「寫程式改造社會」零時政府。&lt;/p&gt;
&lt;h3 id=&quot;座談會open-data面面觀&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#座談會open-data面面觀&quot;&gt;座談會：Open Data 面面觀&lt;/a&gt;&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;http://www.iis.sinica.edu.tw/~trc/public/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;莊庭瑞&lt;/a&gt;：政府 open data 才有機會知道資料是錯的，如果不公開，永遠沒有人會發現資料有錯。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;open-source-engine-powering-big-data-in-thecloud&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#open-source-engine-powering-big-data-in-thecloud&quot;&gt;Open Source Engine powering Big Data in the Cloud&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;完全聽不懂（失落），不過外國人念 Hadoop 聽起來好像在念 &lt;a href=&quot;https://www.google.com.tw/search?q=%E3%81%B1%E3%82%8B%E3%82%8B&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;ぱるる&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;416&quot; height=&quot;505&quot; src=&quot;https://blog.amowu.com/_astro/image1.DV92pHep_ZVEAy0.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image1.DV92pHep_ZVEAy0.webp 416w&quot; /&gt;&lt;/p&gt;
&lt;h3 id=&quot;開源硬體有什麼意義&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#開源硬體有什麼意義&quot;&gt;開源硬體有什麼意義?&lt;/a&gt;&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Facebook 的 OCP (Open Compute Project) 讓很多人開始對 Open Source Hardware 抱有憧憬，認為可以像 Open Source Software 一樣，激盪出很多新的設計和發明。事實上是這樣的嗎？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Open Software VS Open Hardware&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;擴充成本，硬體比軟體還高很多。&lt;/li&gt;
&lt;li&gt;軟體語言本質一樣，易學，硬體相反。&lt;/li&gt;
&lt;li&gt;軟體易模組化&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;OCP (Open Compute Project)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;只公開規格，沒開源。&lt;/li&gt;
&lt;li&gt;規格只適合大用戶。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;最後講者 &lt;a href=&quot;https://www.facebook.com/ben.jai&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Ben&lt;/a&gt; 介紹自家開發的服務 &lt;a href=&quot;http://evi.tw/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Evdience Taiwan&lt;/a&gt;，主要是透過電話來錄音，適合報料蒐證。&lt;/p&gt;
&lt;h3 id=&quot;g0vtw-零時政府從開放源碼到開放政府&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#g0vtw-零時政府從開放源碼到開放政府&quot;&gt;g0v.tw 零時政府：從開放源碼到開放政府&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;300&quot; height=&quot;214&quot; src=&quot;https://blog.amowu.com/_astro/image2.C--0tE-L_Z2tg8mm.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image2.C--0tE-L_Z2tg8mm.webp 300w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;講者為零時政府創辦人 &lt;a href=&quot;https://twitter.com/clkao&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;clkao&lt;/a&gt;，以 Open Source 比喻政府的觀點還蠻有趣的：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;民主 = 接受 patch&lt;/li&gt;
&lt;li&gt;立委 = committer&lt;/li&gt;
&lt;li&gt;政府 = code in action&lt;/li&gt;
&lt;li&gt;公民社會 = open source&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在成果展示中，我最喜歡&lt;a href=&quot;https://github.com/g0v/dev/wiki/Project-ChildNotFound.tw&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;失蹤兒少開放資料平台計畫&lt;/a&gt;這個點子，404 頁面與慈善公益結合。&lt;/p&gt;
&lt;h3 id=&quot;當設計遇上-opensource人人都可以是設計師&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#當設計遇上-opensource人人都可以是設計師&quot;&gt;當設計遇上 opensource，人人都可以是設計師&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;講者為 &lt;a href=&quot;https://www.facebook.com/kaiyu.kamm&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Kamm&lt;/a&gt;，主題跟設計相關，認識了一些網站跟專有名詞：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://www.wikihouse.cc/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;WikiHouse&lt;/a&gt; — 建築設計的 open source，這超讚，離自己組小木屋的距離又更近了。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.sketchup.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;SketchUp&lt;/a&gt; — Google 3d modding tool。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.rhino3d.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Rhinoceros&lt;/a&gt; — 3D modeling software。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.grasshopper3d.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Grasshopper&lt;/a&gt; — 搭配 Rhino 參數化建模的工具。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://zh.wikipedia.org/wiki/RepRap&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;RepRap&lt;/a&gt; — 開源的 3D 列印原型機。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.thingiverse.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Thingiverse&lt;/a&gt; — 3D 設計的社群。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E6%95%B0%E6%8E%A7%E6%9C%BA%E5%BA%8A&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;CNC&lt;/a&gt; — 電腦數值控制工具機，這裡應該指大型的 3D 成型機吧。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://zh.wikipedia.org/wiki/Pure_Data&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Pure Data&lt;/a&gt; — 不太確定，好像是 3D model 的一種格式。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;yet-another-introduction-to-gitfrom-the-bottomup&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#yet-another-introduction-to-gitfrom-the-bottomup&quot;&gt;Yet another introduction to Git — from the bottom up&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;http://ihower.tw/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;ihower&lt;/a&gt; 大大的演講，主要是講一些 Git 底層的運作原理，可是我聽不太懂 囧。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;如果你能不用 &lt;code&gt;git add&lt;/code&gt; 和 &lt;code&gt;git commit&lt;/code&gt; 來 commit code，就可以不用聽這場演講了。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;app-on-server-nas-上的-app開發與商業模式&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#app-on-server-nas-上的-app開發與商業模式&quot;&gt;App on Server: NAS 上的 App 開發與商業模式&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;後悔來聽這場，基本上就是在廣告 QNAP NAS 這台機器，應該去聽&lt;a href=&quot;http://alloyteam.github.io/AlloyPhoto/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;騰訊的 HTML5 圖像引擎&lt;/a&gt;或&lt;a href=&quot;https://www.moedict.tw/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;萌典&lt;/a&gt;才對。&lt;/p&gt;
&lt;h3 id=&quot;an-intro-of-web-scaffolding-tool-using-yeoman-generator&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#an-intro-of-web-scaffolding-tool-using-yeoman-generator&quot;&gt;An intro of web scaffolding tool using yeoman generator&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;250&quot; height=&quot;250&quot; src=&quot;https://blog.amowu.com/_astro/image3.DHVqbfGR_Zp6whb.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image3.DHVqbfGR_Zp6whb.webp 250w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;這場是介紹用 &lt;a href=&quot;http://yeoman.io/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Yeoman&lt;/a&gt; 打造一個屬於自己網站的 scaffold。&lt;/p&gt;
&lt;h3 id=&quot;一小時-rwd就上手&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#一小時-rwd就上手&quot;&gt;一小時 RWD 就上手&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;250&quot; height=&quot;85&quot; src=&quot;https://blog.amowu.com/_astro/image4.BN4_LO6H_1FSJul.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image4.BN4_LO6H_1FSJul.webp 250w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;這場的講者是最近很紅的裝置藝術師 &lt;a href=&quot;https://www.facebook.com/evenwu&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Even&lt;/a&gt; 大大，主要世界介紹一套不錯的 RWD column grid tool「&lt;a href=&quot;http://susy.oddbird.net/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;SUSY&lt;/a&gt;」。&lt;/p&gt;
&lt;h3 id=&quot;build-your-own-trello-within-200-lines-ofcode&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#build-your-own-trello-within-200-lines-ofcode&quot;&gt;Build your own Trello within 200 Lines of Code&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;這場主要是用「&lt;a href=&quot;http://www.meteor.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Meteor&lt;/a&gt;」快速開發出 &lt;a href=&quot;http://trello.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Trello&lt;/a&gt; 的原型，Meteor 最大的特色應該就是 client 跟 server 都在同一份 code 裡面。
另外認識了一個很棒的共筆網站「&lt;a href=&quot;http://www.icoding.co/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;iCoding&lt;/a&gt;」。&lt;/p&gt;
&lt;h3 id=&quot;angularjs-開發實戰&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#angularjs-開發實戰&quot;&gt;AngularJS 開發實戰&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;300&quot; height=&quot;111&quot; src=&quot;https://blog.amowu.com/_astro/image5.BysycXVF_ZWtmXt.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image5.BysycXVF_ZWtmXt.webp 300w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;這場講者是 &lt;a href=&quot;https://www.facebook.com/will.fans&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;保哥&lt;/a&gt;，主要是介紹 &lt;a href=&quot;http://angularjs.org/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;AngularJS&lt;/a&gt; 的核心架構，非常有幫助。&lt;/p&gt;
&lt;h2 id=&quot;day-2&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#day-2&quot;&gt;DAY 2&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;前一天結束一回到家就睡死了，所以第二天比較早起，早上是一連串跟遊戲開發相關的議程，非常期待。&lt;/p&gt;
&lt;h3 id=&quot;行動遊戲-app-開發與-open-sourcekamigami-的雙平台經驗&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#行動遊戲-app-開發與-open-sourcekamigami-的雙平台經驗&quot;&gt;行動遊戲 App 開發與 Open Source：Kamigami 的雙平台經驗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;主要是介紹講者 &lt;a href=&quot;https://twitter.com/littlebtc&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;笨笨的小B&lt;/a&gt; 團隊自行開發的遊戲引擎 Kamigami 的經驗分享：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/libgdx/libgdx&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;LibGDX&lt;/a&gt; — 類似 XNA 的 Android game framework。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://cocoapods.org/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;CocoaPods&lt;/a&gt; — iOS 套件管理。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://maven.apache.org/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Maven&lt;/a&gt; — Android 套件管理。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://nuget.codeplex.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;NuGet&lt;/a&gt; — .Net 套件管理。&lt;/li&gt;
&lt;li&gt;注意授權 License，建議只挑這幾個，BSD/MIT/Apache2/MS-PL。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;asynchronous-programming-with-combinator&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#asynchronous-programming-with-combinator&quot;&gt;Asynchronous programming with Combinator&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;講者是來自神來也的女性 Server 程式設計師！ (受小弟一拜)。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Memory leak 沒經驗的程式設計師容易漏掉，有經驗的程式設計師沒睡飽的話，也會漏掉。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;modern-game-engine-with-openssource&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#modern-game-engine-with-openssource&quot;&gt;Modern Game Engine with Opens Source&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;300&quot; height=&quot;169&quot; src=&quot;https://blog.amowu.com/_astro/image6.CZGYOsCy_t1SmV.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image6.CZGYOsCy_t1SmV.webp 300w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;講者 Owen 大大是 &lt;a href=&quot;http://acg.gamer.com.tw/acgDetail.php?s=60277&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Space Qube&lt;/a&gt; 的開發者！&lt;/p&gt;
&lt;p&gt;主要是介紹自行研發的 LynxEngine 遊戲引擎用到哪些技術：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Lua/LuaPlus/Mono (ScriptSystem)&lt;/li&gt;
&lt;li&gt;Box2D/Bullet/PhysX&lt;/li&gt;
&lt;li&gt;FreeImage&lt;/li&gt;
&lt;li&gt;Zlib&lt;/li&gt;
&lt;li&gt;wxWidgets&lt;/li&gt;
&lt;li&gt;bmFont&lt;/li&gt;
&lt;li&gt;FreeType&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;using-lua-to-build-a-component-based-architecture-for-gameapps&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#using-lua-to-build-a-component-based-architecture-for-gameapps&quot;&gt;Using Lua to Build a Component-based Architecture for Game Apps&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;上午場的壓軸，是 &lt;a href=&quot;http://blog.monkeypotion.net/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;半路&lt;/a&gt; 大大的演講！一大早出門忘記帶書來給他簽名了&amp;gt;”&amp;lt;。&lt;/p&gt;
&lt;p&gt;主要是介紹用組件式開發來解決物件繼承會發生的「鑽石問題」。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://luaforge.net/projects/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;LuaForge&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://luajit.org/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;LuaJIT&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.coronalabs.com/products/corona-sdk/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Corona SDK&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;寫太多 magic code 的話，哪一天睡不好，就是維護你程式的人在詛咒你。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;open-code-why-linuxs-openness-enabled-it-to-succeed-where-all-othersfailed&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#open-code-why-linuxs-openness-enabled-it-to-succeed-where-all-othersfailed&quot;&gt;Open Code: Why Linux’s openness enabled it to succeed where all others failed.&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;也是一場全英文的演講，請到 Linux kernal 開發者 Greg KH ，基本上完全聽不懂 (泣)。&lt;/p&gt;
&lt;p&gt;與現實世界相反，程式設計的世界中，low-level 的 programmer，level 一點都不 low，low-level 不是沒人要做的事，而是能做這些事的人不多。&lt;/p&gt;
&lt;h3 id=&quot;designspark-免費設計資源與-raspberry-pi新應用&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#designspark-免費設計資源與-raspberry-pi新應用&quot;&gt;DesignSpark 免費設計資源與 Raspberry Pi 新應用&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;103&quot; height=&quot;125&quot; src=&quot;https://blog.amowu.com/_astro/image7.Go14TGbe_276QrY.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image7.Go14TGbe_276QrY.webp 103w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;講者是來自 RS Components 的香港小姐，中文講得好像有點吃力 XD，介紹了一個很棒的網站「&lt;a href=&quot;http://www.designspark.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;DesignSpark&lt;/a&gt;」。&lt;/p&gt;
&lt;h3 id=&quot;前端工程師如何與團隊合作以開發-drupal專案為例&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#前端工程師如何與團隊合作以開發-drupal專案為例&quot;&gt;前端工程師如何與團隊合作：以開發 Drupal 專案為例&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;雖然對 Drupal 沒有興趣，但想聽聽 F2E 的團隊合作心得：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;常給 PM 建議，但決定權不在你。&lt;/li&gt;
&lt;li&gt;協助 Prototype。&lt;/li&gt;
&lt;li&gt;不斷溝通，持續調整&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.slideshare.net/adamp3wang/mockupwebconftw-2013&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;那些Mockup 沒告訴你的事&lt;/a&gt;。&lt;/li&gt;
&lt;li&gt;F2E是良心事業。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;raspberry-pi-and-thefuture&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#raspberry-pi-and-thefuture&quot;&gt;Raspberry PI and the future&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;最後一場，雖然對 RaspberryPI 有很興趣，但這場是英文。&lt;/p&gt;
&lt;h3 id=&quot;lightning-talk&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#lightning-talk&quot;&gt;Lightning Talk&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;因為今年的閃電秀過程太精彩了，忘記筆記，所以只挑幾個我想的起來分享：&lt;/p&gt;
&lt;h3 id=&quot;registrano&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#registrano&quot;&gt;&lt;/a&gt;&lt;a href=&quot;http://registrano.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Registrano&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;由 hlb 大大創辦，負責本次 COSCUP 線上訂票的服務，創下 47 秒 500 張門票秒殺的紀錄。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;「怎麼晚一分鐘就沒票了，報名網站在搞什麼」。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;logdown&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#logdown&quot;&gt;&lt;/a&gt;&lt;a href=&quot;http://logdown.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Logdown&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;最近很火的 markdown blog 服務。&lt;/p&gt;
&lt;h3 id=&quot;mrbus&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#mrbus&quot;&gt;&lt;/a&gt;&lt;a href=&quot;http://mrbus.tw/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Mr.Bus&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Python 正妹。（喂 ￣▽￣)==O)￣#)3￣)☆&lt;/p&gt;
&lt;h2 id=&quot;心得&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#心得&quot;&gt;心得&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;今年議程非常豐富，大致上聽了：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Game 4 場&lt;/li&gt;
&lt;li&gt;Open Data 3 場&lt;/li&gt;
&lt;li&gt;Web front-end 3 場&lt;/li&gt;
&lt;li&gt;Artist &amp;amp; Maker 3 場&lt;/li&gt;
&lt;li&gt;Cloud &amp;amp; Backend technology 2 場&lt;/li&gt;
&lt;li&gt;Tools &amp;amp; Utilities 2 場&lt;/li&gt;
&lt;li&gt;OS &amp;amp; Embedded System 1 場&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;本次心得：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;最近政府議題夯，大玩黑畫面梗。&lt;/li&gt;
&lt;li&gt;今年看到好多人跌倒 = =”。&lt;/li&gt;
&lt;li&gt;開源精神遍及 Open Data，Open Hardware，Open Design！&lt;/li&gt;
&lt;li&gt;遇上好多網路上 foloow 的大大 :D&lt;/li&gt;
&lt;li&gt;可以聽到其他業界朋友談論自己公司的八卦（咦？&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;參考&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#參考&quot;&gt;參考&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://gist.github.com/denny0223/6150643&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;COSCUP 2013 IRC Log&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hackpad.com/COSCUP-2013--evfJ0SJdCyQ&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;COSCUP 2013 梗全集&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://wiki.coscup.org/mediabuzz-2013&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;COSCUP 2013 Wiki&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>Tech</category></item><item><title>我想做的遊戲</title><link>https://blog.amowu.com/posts/2013-08-02-dream-game/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2013-08-02-dream-game/</guid><description>進入遊戲業兩年多了，但網誌上跟遊戲相關的文章卻很少，所以在截稿前夕，就來寫一篇跟遊戲有關的文章吧！</description><pubDate>Fri, 02 Aug 2013 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;前言&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#前言&quot;&gt;前言&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;進入遊戲業兩年多了，但網誌上跟遊戲相關的文章卻很少，所以在截稿前夕，就來寫一篇跟遊戲有關的文章吧！&lt;/p&gt;
&lt;p&gt;雖然這兩年間學到不少遊戲開發的相關知識，但這篇不寫技術，這篇純粹想聊聊「自己想做的遊戲」。&lt;/p&gt;
&lt;p&gt;每位遊戲開發者一定都會有自己想做的遊戲，但絕對不會是目前你工作上的這款(笑)，雖然嚮往成為獨立遊戲開發者，但看過 &lt;a href=&quot;https://blog.amowu.com/posts/2012-10-03-monkey-potion/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;半路叛逃&lt;/a&gt; 的分享後瞭解，獨立開發遊戲並不容易，儘管如此，還是想分享自己心中的那一款&lt;strong&gt;完美遊戲&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;簡單來講，有三個要素：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;遊戲&lt;/li&gt;
&lt;li&gt;生活服務&lt;/li&gt;
&lt;li&gt;教育&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;遊戲？生活服務？教育？在仔細解釋前，先介紹幾款遊戲給大家認識：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;OZ&lt;/li&gt;
&lt;li&gt;Greed Island&lt;/li&gt;
&lt;li&gt;動物之森&lt;/li&gt;
&lt;li&gt;神奇寶貝&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;oz&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#oz&quot;&gt;OZ&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;OZ 是在日本動畫 &lt;a href=&quot;http://zh.wikipedia.org/wiki/%E5%A4%8F%E6%97%A5%E5%A4%A7%E4%BD%9C%E6%88%B0&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;夏日大作戰&lt;/a&gt; （2009）中的一個虛擬世界社交網站。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;「OZ」與&lt;a href=&quot;http://zh.wikipedia.org/wiki/%E7%AC%AC%E4%BA%8C%E4%BA%BA%E7%94%9F&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;第二人生&lt;/a&gt;概念相似，但依照自己的經驗來講「OZ」應該更接近日本社交網站mixi的想法。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;由於「OZ」系統與現實世界之間有著極高的整合度，來自世界各地的許多企業與政府組織都於此設有部門來進行互動，這使得在作為虛擬世界的「OZ」混亂也陸陸續續影響到真實世界的生活。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;600&quot; height=&quot;335&quot; src=&quot;https://blog.amowu.com/_astro/image1.B6O6Q-48_ZiGaP1.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image1.B6O6Q-48_ZiGaP1.webp 600w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;雖然是虛構的故事，但 OZ 中有幾個特色還蠻有趣的：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;OZ 世界中擁有 10 億人口（比 Facebook 多 1 億），幾乎是每個人都有一組帳號&lt;/li&gt;
&lt;li&gt;Client 端有 PC，手機，TV 等，與近年來跨平台特性一致&lt;/li&gt;
&lt;li&gt;行政進駐，遊戲世界可以與&lt;strong&gt;現實服務&lt;/strong&gt;做連結，例如：納稅，防災&lt;/li&gt;
&lt;li&gt;企業進駐，直接在遊戲中的商城購物，也是一個與&lt;strong&gt;現實服務&lt;/strong&gt;連結的商務平台&lt;/li&gt;
&lt;li&gt;即時翻譯多國語言，這個感覺&lt;a href=&quot;http://www.techbang.com/posts/14214-general-translation-function-for-google-to-be-the-future-babel-fish&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;快實現&lt;/a&gt;了&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;其中行政與企業這塊，就是我上面提到的&lt;strong&gt;遊戲&lt;/strong&gt;結合&lt;strong&gt;生活服務&lt;/strong&gt;，近年網路服務也逐漸往資料開放（API）的方向發展，要在遊戲世界中實現這些服務已經不是夢想。&lt;/p&gt;
&lt;h2 id=&quot;greed-island&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#greed-island&quot;&gt;Greed Island&lt;/a&gt;&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;http://zh.wikipedia.org/wiki/%E8%B2%AA%E5%A9%AA%E4%B9%8B%E5%B3%B6&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;貪婪之島&lt;/a&gt; 是日本漫畫《&lt;a href=&quot;http://zh.wikipedia.org/wiki/Hunter_%C3%97_Hunter&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Hunter × Hunter&lt;/a&gt;》中的一個虛擬遊戲。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;600&quot; height=&quot;339&quot; src=&quot;https://blog.amowu.com/_astro/image2.CiVltXue_VN4UQ.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image2.CiVltXue_VN4UQ.webp 600w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;這是一個把現實世界當作遊戲的一個概念，遊戲中的角色，道具，事件都是用漫畫中的「念」製作出來的，當然，我們不可能用念能力來製作遊戲（笑），不過其實也可以把它看作 &lt;a href=&quot;http://zh.wikipedia.org/wiki/%E6%93%B4%E5%A2%9E%E5%AF%A6%E5%A2%83&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;擴增實境(AR)&lt;/a&gt; 的延伸應用，貪婪之島最令我著迷的概念就是，「只要照著遊戲循序漸進，玩家本身就會得到成長」，與我的座右銘「最有趣好玩的RPG，就是自己的人生」相呼應，是理想中的「充滿遊戲的現實世界」。&lt;/p&gt;
&lt;h2 id=&quot;動物之森&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#動物之森&quot;&gt;動物之森&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;這款就比較正常一點了，是由日本任天堂製作，我把它定義為治癒型輕鬆生活遊戲。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;這是個沒有固定劇情的開放遊戲，玩家可以在裡面獨自生活，不受預設的劇情、任務限制，許多人（小孩子或成人）享受這樣與有趣動物角色”對話”的遊玩樂趣。透過使用電玩主機的內部時鐘，這個遊戲可與真實時間相對映，因此有著實際的事件發生。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;600&quot; height=&quot;410&quot; src=&quot;https://blog.amowu.com/_astro/image3.C8VA3Sdj_Z1UKWDP.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image3.C8VA3Sdj_Z1UKWDP.webp 600w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;這款遊戲最早從超任時代就有了，一直到現在的 Wii，3DS，都還是熱門保證的暢銷遊戲，我自己是從 NDS 版開始接觸，可以把它想像成自由度很高的「牧場物語」，基本上遊戲沒有你需要完成的任務，不用打怪升級，只需要跟村民聊聊天，佈置房間，種花草，抓蟲鳥，然後收藏在博物館，很悠閒的一款遊戲，跟大部分遊戲比較不同的，也是我很喜歡的一個系統，就是與&lt;strong&gt;現實時間&lt;/strong&gt;同步，清晨到夜晚，春夏秋冬，都跟現實世界同步，特定日期還會有事件，例如聖誕節，所以每天都會滿懷期待的開啟遊戲，進入這個可愛的動物世界，雖然有下雨下雪這類天氣效果，但這個就不會跟現實世界同步了（雖然技術是做得到），總之，這是一款沒有壓力的遊戲，我很想做出這樣的遊戲！&lt;/p&gt;
&lt;h2 id=&quot;神奇寶貝&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#神奇寶貝&quot;&gt;神奇寶貝&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;上面才說到我想做輕鬆沒壓力的遊戲，結果這邊就要介紹這款要花大量時間培養寵物，練功破關的遊戲（笑）。&lt;/p&gt;
&lt;p&gt;和一般人不同，這應該是我第一款接觸的 RPG 遊戲（國小六年級），讓我深深著迷，廣大的世界冒險，豐富的神奇寶貝，平衡的遊戲架構，與現實玩家的交流互動，都是這款遊戲迷人的特色。&lt;/p&gt;
&lt;p&gt;不過，我提出這款遊戲想聊的不是上述這些，我想分享的是，這系列遊戲&lt;strong&gt;背後設計的世界觀&lt;/strong&gt;。神奇寶貝的遊戲世界，其實都是參照現實某個地區的地圖來做參考的。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;233&quot; src=&quot;https://blog.amowu.com/_astro/image4.DyU0tiwJ_ZFi7Ca.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image4.DyU0tiwJ_Z1w16IF.webp 640w, https://blog.amowu.com/_astro/image4.DyU0tiwJ_ZFi7Ca.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;以上圖為例，右邊是日本九州的地圖，左邊是 &lt;a href=&quot;http://zh.wikipedia.org/zh-tw/%E7%A5%9E%E5%A5%87%E5%AF%B6%E8%B2%9D_%E7%B4%85%E5%AF%B6%E7%9F%B3%C2%B7%E8%97%8D%E5%AF%B6%E7%9F%B3&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;神奇寶貝紅寶石&lt;/a&gt;的地圖，基本上就是把九州往左轉90°。 當然不純粹是只有地圖參考這樣而已，對應的地點也會根據現實該地方特色，埋一些伏筆在遊戲劇情裡面，以巴哈姆特玩家分享的 &lt;a href=&quot;http://forum.gamer.com.tw/Co.php?bsn=01647&amp;amp;sn=300144&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;這篇文章&lt;/a&gt;來舉例，遊戲中有三隻神獸，長的很像原子彈，而且三隻神獸都能學習到「爆炸」這個絕技，然後呢？有趣的是，把這三隻神獸在遊戲中出現的地點與九州地圖做對照，可以發現分別出現在「長崎」，「宮崎」和「大分」，這三個地方是第二次世界大戰中，發生過大規模空襲的地點！這種&lt;strong&gt;藉由從玩遊戲來學習知識&lt;/strong&gt;的概念我非常喜歡（好想以台灣為背景做一款遊戲啊）。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;600&quot; height=&quot;200&quot; src=&quot;https://blog.amowu.com/_astro/image5.C1Ha_KQP_ZgFPtQ.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image5.C1Ha_KQP_ZgFPtQ.webp 600w&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;所以我想要做怎樣的遊戲呢&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#所以我想要做怎樣的遊戲呢&quot;&gt;所以，我想要做怎樣的遊戲呢？&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;上面講的漏漏長，所以我到底想做怎樣的遊戲呢？老實講，我還沒有一個具體的架構和想法，但上面提到的遊戲中，都有我很欣賞，很想實現的概念在裡面：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;OZ：深度整合行政＋企業的服務在遊戲中&lt;/li&gt;
&lt;li&gt;貪婪之島：玩遊戲的過程中，玩家本身也會得到成長&lt;/li&gt;
&lt;li&gt;動物之森：與現實時間同步，沒有壓力的治愈系遊戲&lt;/li&gt;
&lt;li&gt;神奇寶貝：藉由遊戲設計，讓玩家學習知識&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;再回到開頭提到的，我想做的遊戲三大元素「生活服務」，「遊戲」和「教育」：&lt;/p&gt;
&lt;h3 id=&quot;生活服務&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#生活服務&quot;&gt;生活服務&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;與其說我想做遊戲，不如說我比較想做服務，一款提供現實世界服務的遊戲，可以在遊戲世界中看YouTube（電視），在遊戲中收發mail（信箱），在遊戲中閱讀RSS（報紙），在遊戲中購買 amazon（購物）等，近年來越來越多的網路服務都走向多螢一雲，跨平台的方向，也都有提供 API 讓開發者應用，節省許多自行研發的成本，加入這些元素有助於玩遊戲同時，也能得到現實的回饋。&lt;/p&gt;
&lt;h3 id=&quot;遊戲&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#遊戲&quot;&gt;遊戲&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://www.blogger.com/blogger.g?blogID=696354394590316778&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Gamification&lt;/a&gt; 這個詞最近很紅：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Gamification（遊戲化），網站開發者認為遊戲化是現代的一個趨勢，遊戲化就是把遊戲的元素注入生活，是要讓生活更有趣，以及讓大家打破對遊戲的刻板想法，就像是以前玩遊戲比較偏向逃避現實，但是現在遊戲把虛擬的世界帶到現實，像是最近的電影阿凡達，LBS程式Foursquare，都是把我們跟以往認為是虛擬的東西跟現實連接。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;乍看之下好像跟上面的生活服務是一樣的，差異是一個是「遊戲中結合服務應用」，一個是「服務中加入遊戲元素」。&lt;/p&gt;
&lt;p&gt;而我真正想要表達的，是結合這兩者，一款可以讓你&lt;strong&gt;得到現實成就與回饋的遊戲&lt;/strong&gt;，在遊戲中完成的每個事件，得到的每件獎勵，都是跟現實有連結的，不會只是一般打寶打怪升級這種一時的空虛快感。&lt;/p&gt;
&lt;h3 id=&quot;教育&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#教育&quot;&gt;教育&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;最後是教育，遊戲的開發在技術上來講絕對不會比其他領域來的輕鬆簡單，但是遊戲卻往往給人不務正業的觀念，尤其台灣大部份的遊戲就更別提了(看看那些電視上播的廣告)，想抬起胸膛跟親朋好友說我的工作是在製作遊戲都有點困難，加上近年來手機平板普及，開發遊戲門檻降低，雖然對我們來講是好事，但同時也降低遊戲品質，沒有內容的遊戲充斥市面，重點是還賺大錢，這樣還有誰會想專注在遊戲內涵的思考？所以我常常在想，如果我自己要做一款遊戲，我會想從給小朋友玩的遊戲開始做，想想童年那些美好的回憶，那是段最單純看待遊戲的時期，不論是在遊戲中也好，現實中也是，教育一直都是很重要的課題之一，如果一款遊戲能讓我們的孩子學習到有用的知識，不但小朋友們玩的快樂又有成就，父母親也肯掏錢投資在孩子的&lt;strong&gt;教育競爭&lt;/strong&gt;上。&lt;/p&gt;
&lt;h2 id=&quot;結語&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#結語&quot;&gt;結語&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;以上，聊了一大串外加一些小抱怨，雖然看似過於理想，但相似概念的遊戲一直都陸陸續續出現，現階段的我當然不可能把這個目標實現，只有持續關注遊戲的發展，不斷充實自己，收集所需的相關資料，當機會來臨的那一刻，才有能力好好把握住，感謝看完這篇文章的您，在此共勉之，加油！&lt;/p&gt;
&lt;h2 id=&quot;參考文章&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#參考文章&quot;&gt;參考文章&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://www.inside.com.tw/2011/01/19/gamification-future&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Gamification：遊戲化的未來&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>Tech</category></item><item><title>Web Components</title><link>https://blog.amowu.com/posts/2013-06-26-web-components/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2013-06-26-web-components/</guid><description>今天想要跟大家聊聊 Web Components 這個新玩具，說是新玩具，其實這個概念已經發展有段時間了，尤其筆者本身是玩 Flash 技術出身的，發現 Web Components 跟 Flex Component 差不多，所以就來分享一些心得。</description><pubDate>Wed, 26 Jun 2013 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;今天想要跟大家聊聊 Web Components 這個新玩具，說是新玩具，其實這個概念已經發展有段時間了，尤其筆者本身是玩 Flash 技術出身的，發現 Web Components 跟 &lt;a href=&quot;http://www.adobe.com/devnet/flex/articles/creating_components.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Flex Component&lt;/a&gt; 差不多，所以就來分享一些心得。&lt;/p&gt;
&lt;h2 id=&quot;web-components-是什麼&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#web-components-是什麼&quot;&gt;Web Components 是什麼？&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;簡單來講就是你可以客制化 HTML &lt;code&gt;&amp;lt;tag&amp;gt;&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;my-component&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;my-component&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;由 W3C 提出：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The component model for the Web.
When used in combination, Web Components enable Web application authors to define widgets with a level of visual richness and interactivity not possible with CSS alone, and ease of composition and reuse not possible with script libraries today.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;使用-web-components-有什麼好處&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#使用-web-components-有什麼好處&quot;&gt;使用 Web Components 有什麼好處？&lt;/a&gt;&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;鉴于许多 Web 框架通过暴露 JavaScript API 来构建用户界面，而构建用户界面实际上就是生成一堆 div 和 spans 标记，Web 组件是原生浏览器的解决方案因此不依赖于一个完整的框架。因此，与现在的一般依赖某个 Javscript 框架的 HTML 组件相比，Web组件希望能减少碎片。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;最近新起許多 MDV（Model-driven-view）這類 template 和 data binding 開發方式的框架（backbone、angular、ember），那 Web Components 跟這些框架有什麼不同呢？&lt;/p&gt;
&lt;p&gt;基本上就是希望藉由規格標準化，讓 Web application 的開發方式與架構更清晰。&lt;/p&gt;
&lt;h2 id=&quot;web-components-的瀏覽器支援情況&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#web-components-的瀏覽器支援情況&quot;&gt;Web Components 的瀏覽器支援情況？&lt;/a&gt;&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;尽管 Web 组件是一项有趣的新技术，但在浏览器们都支持它们（并且大部分用户都开始使用这些浏览器们）之前，其中的大部分功能还派不上用场。解决这个问题的一种方法是使用 polyfills。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;很遺憾，目前的瀏覽器普遍都還不支援完整的 Web Components，不過可以使用 Google I/O 2013 中釋出的 &lt;a href=&quot;http://www.polymer-project.org/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Polymer&lt;/a&gt; library，讓瀏覽器模擬 Web Components 環境嘗嘗鮮。&lt;/p&gt;
&lt;h2 id=&quot;使用-polymer-開發-web-components&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#使用-polymer-開發-web-components&quot;&gt;使用 Polymer 開發 Web Components&lt;/a&gt;&lt;/h2&gt;
&lt;h2 id=&quot;basics&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#basics&quot;&gt;Basics&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;530&quot; height=&quot;190&quot; src=&quot;https://blog.amowu.com/_astro/image1.B8A2dClV_13nyam.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image1.B8A2dClV_13nyam.webp 530w&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;component&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#component&quot;&gt;Component&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;530&quot; height=&quot;290&quot; src=&quot;https://blog.amowu.com/_astro/image2.BX6Cwq0N_Z10ViTA.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image2.BX6Cwq0N_Z10ViTA.webp 530w&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;1-simulate&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#1-simulate&quot;&gt;1. Simulate&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;載入 &lt;code&gt;platform.js&lt;/code&gt; 讓瀏覽器支援 Web Components 的功能。&lt;/p&gt;
&lt;h2 id=&quot;2-htmlimports&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#2-htmlimports&quot;&gt;2. HTML Imports&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;使用 &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; 和 &lt;code&gt;import&lt;/code&gt; 載入 Web Components：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;link&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rel&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;import&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;import-file.html&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2 id=&quot;3-customelements&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#3-customelements&quot;&gt;3. Custom Elements&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;使用自己建立的元件 &lt;code&gt;&amp;lt;my-component&amp;gt;&amp;lt;/my-component&amp;gt;&lt;/code&gt;，命名方式以 dash(-) 分開為原則：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;element&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;my-component&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;可以加入 &lt;code&gt;constructor&lt;/code&gt; 讓 javascript 使用， 例如：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;element&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;my-component&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;constructor&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;MyComponent&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;var myComponent = new MyComponent();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;也可以繼承其他元件：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;element&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;my-button&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;button&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2 id=&quot;4-initialize-polymer&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#4-initialize-polymer&quot;&gt;4. Initialize Polymer&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;在 &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; 內加入 &lt;code&gt;Polymer.register()&lt;/code&gt;，讓這個元件初始化。&lt;/p&gt;
&lt;h2 id=&quot;5-templates&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#5-templates&quot;&gt;5. Templates&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;定義元件的 HTML Markup，UI layout。&lt;/p&gt;
&lt;h2 id=&quot;6-decorators&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#6-decorators&quot;&gt;6. Decorators&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;使用 &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; 定義元件的 CSS 樣式，另外也可以載入外部樣式：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;element&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;my-component&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;link&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rel&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;stylesheet&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;my-component.css&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;template&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;…&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2 id=&quot;7-model-drivenviews&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#7-model-drivenviews&quot;&gt;7. Model Driven Views&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;使用 &lt;code&gt;{{}}&lt;/code&gt; 將 data binding 到 template，然後在初始化的時候定義變數，例如替這個元件建立一個公用的 &lt;code&gt;title&lt;/code&gt; 屬性：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;element&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;my-component&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;constructor&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;MyComponent&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;template&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&amp;gt;{{title}}&amp;lt;/&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;template&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Polymer.register(this, {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;title: &quot;Web Components&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;element&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;定義完後，就可以使用：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;my-component&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;Hello World!&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;my-component&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;or&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;var myComponent = new MyComponent();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;myComponent.title = &quot;Hello World!&quot;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2 id=&quot;結語&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#結語&quot;&gt;結語&lt;/a&gt;&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Web Component 将会横扫 Backbone、Ember、Angular、Knockout 等等这些框架。但是，接下来这几年，我们还是要用它们，因为很多 Web Component 的 API 只能在 Chrome 的 Canary 开发版本和Firefox的开发版本中使用。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;雖然引言中講的很聳動，但目前玩下來還是碰到不少問題。例如我想使用一些第三方的 jQuery Plugin 會無法動作等…，不過因為開發架構很接近 Flash，小弟認為應該會是未來 HTML5 RIA 的開發趨勢，所以目前還是會持續關注。&lt;/p&gt;
&lt;p&gt;以上是初步試玩 Polymer Web Components 的一些心得，更多的 feature 可以到 W3C 和 Polymer 網站看看，如果有錯誤的地方也歡迎糾正，祝大家有個愉快的開發旅途 :-)&lt;/p&gt;
&lt;h2 id=&quot;reference&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#reference&quot;&gt;Reference&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://www.w3.org/TR/2013/WD-components-intro-20130606/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Introduction to Web Components — W3C&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.polymer-project.org/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Polymer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=fqULJBBEVQE&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Web Components: A Tectonic Shift for Web Development — Google I/O 2013&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.webcomponentsshift.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Web Components — Google I/O&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.infoq.com/cn/news/2013/06/webcomponents&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Google 发布新一代Web UI库Polymer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.infoq.com/cn/news/2013/02/Twitter-Flight-Framework&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Twitter发布基于组件的轻量级JavaScript框架 — — Flight&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://twitter.github.io/flight/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Flight by Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.dartlang.org/articles/web-ui/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Web UI Package&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>Tech</category></item><item><title>從無到有：安裝＆自動部署 Ruby on Rails 網站</title><link>https://blog.amowu.com/posts/2013-05-28-install-auto-deploy-rails/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2013-05-28-install-auto-deploy-rails/</guid><description>這個月把網站從 Amazon EC2 搬回自己的 server，目前整個環境是 Linux（Ubuntu）+ Apache + Ruby on Rails 在跑，筆記一下從無到有的配置方法：</description><pubDate>Tue, 28 May 2013 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;這個月把網站從 Amazon EC2 搬回自己的 server，目前整個環境是 Linux（Ubuntu）+ Apache + Ruby on Rails 在跑，筆記一下從無到有的配置方法：&lt;/p&gt;
&lt;h2 id=&quot;install-virtualbox&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#install-virtualbox&quot;&gt;Install &lt;/a&gt;&lt;a href=&quot;https://www.virtualbox.org/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;VirtualBox&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;虛擬主機，另一個較有名的是 &lt;a href=&quot;http://www.vmware.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;VMWare&lt;/a&gt;，這裡是選用 VirtualBox 4.2.12 作 host。&lt;/p&gt;
&lt;p&gt;安裝之後設定一下配置，大部份都用預設的就可以，比較不一樣的地方有，記憶體 1024 MB，網路使用&lt;strong&gt;橋接界面卡&lt;/strong&gt;的方式，這樣可以在虛擬 OS 內連線取得固定 IP，這對使用自有網址來架設網站是很重要的。&lt;/p&gt;
&lt;h2 id=&quot;install-ubuntu&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#install-ubuntu&quot;&gt;Install &lt;/a&gt;&lt;a href=&quot;http://www.ubuntu.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Ubuntu&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;下載 Ubuntu 最新版本，這裡使用 13.04，然後將映像擋掛載在 VirtualBox 後啟動，照指示一步一步安裝。&lt;/p&gt;
&lt;p&gt;安裝完成後，記得先把系統更新裝一裝，之後執行：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;apt-get&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;update&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;apt-get&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;upgrade&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2 id=&quot;install-ssh&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#install-ssh&quot;&gt;Install SSH&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;安裝完 ubuntu 後，為了之後能遠端操作 server，所以接著安裝 SSH：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;apt-get&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;openssh-server&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ssh-keygen&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-t&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rsa&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;scp&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;.ssh/id_rsa.pub&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;SERVER_HOST_NAME:~/.ssh/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cat&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;.ssh/id_rsa.pub&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;.ssh/authorized_keys&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;註：第 2–4 步是為了之後登入 server 可以不用打密碼&lt;/p&gt;
&lt;p&gt;註：第 3 步的 &lt;code&gt;SERVER_HOST_NAME&lt;/code&gt; 記得換成自己的網址或 IP&lt;/p&gt;
&lt;h2 id=&quot;install-vim&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#install-vim&quot;&gt;Install vim&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;接著是安裝 vim，為了之後能在 terminal 上直接編輯文字檔案 or code：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;apt-get&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;remove&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;vim-tiny&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;apt-get&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;vim&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2 id=&quot;install-git&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#install-git&quot;&gt;Install Git&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;版本控制，因為之後部署網站都是直接從 &lt;a href=&quot;https://github.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;GitHub&lt;/a&gt; 拉下來，所以需要安裝 Git：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;apt-get&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-y&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;git-core&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;如果需要使用到 GitHub SSH 的話請參考這篇：&lt;a href=&quot;https://help.github.com/articles/generating-ssh-keys&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Generating SSH Keys&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;install-nvm-node&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#install-nvm-node&quot;&gt;Install NVM + Node&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;雖然我的網站還沒使用到 Node.js 的技術，但接下來要安裝的東西需要跑 javascript，所以這裡需要先安裝。&lt;/p&gt;
&lt;p&gt;NVM 是 Node.js 的版本管理套件，可以輕鬆安裝、移除、切換任意版本的 Node：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;apt-get&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;build-essential&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;libssl-dev&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;curl&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;clone&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;git://github.com/creationix/nvm.git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/.nvm&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;. ~/.nvm/nvm.sh&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;.bashrc&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bash&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;NVM 安裝完成後，接著安裝 Node，這裡使用 0.10.0 的版本：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;nvm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;v0.10.0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;nvm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;alias&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0.10.0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2 id=&quot;install-rvm--ruby-rails&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#install-rvm--ruby-rails&quot;&gt;Install RVM + Ruby + Rails&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;同 NVM, RVM 是 Ruby 的版本管理套件：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bash&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-s&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;stable&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;lt;(&lt;/span&gt;&lt;span&gt;curl&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-s&lt;/span&gt;&lt;span&gt; https://raw.github.com/wayneeseguin/rvm/master/binscripts/rvm-installer)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;[[ -s &quot;$HOME/.rvm/scripts/rvm&quot; ]] &amp;amp;&amp;amp; source &quot;$HOME/.rvm/scripts/rvm&quot;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/.bash&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;source&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/.bashrc&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bash&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;接著安裝 Ruby 1.9.3：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rvm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1.9.3&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rvm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;use&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--default&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1.9.3&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;接著安裝 Ruby on Rails：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;gem&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sqlite3&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--no-ri&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--no-rdoc&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;gem&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rails&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--no-ri&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--no-rdoc&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2 id=&quot;install-apache--passenger&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#install-apache--passenger&quot;&gt;Install Apache + Passenger&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;接著安裝 server，通常有 Apache 或 Nginx 兩種，這裡是選用 Apache：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;apt-get&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-y&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;apache2-mpm-prefork&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;apache2-prefork-dev&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;libapr1-dev&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;libaprutil1-dev&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;libcurl4-openssl-dev&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;安裝完 Apache server 後，要讓 Rails 能在上面 run，需要安裝 Passenger 套件：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;gem&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;passenger&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;passenger-install-apache2-module&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;在安裝完成後，會看到 LoadModule 的配置訊息，把它貼到 &lt;code&gt;/etc/apache2/conf.d/mod_rails&lt;/code&gt;，大致上會類似這樣：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;LoadModule&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;passenger_module&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/home/YOUR_RUBY_PATH/gems/passenger-4.0.2/libout/apache2/mod_passenger.so&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;PassengerRoot&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/home/YOUR_RUBY_PATH/gems/passenger-4.0.2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;PassengerDefaultRuby&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/home/YOUR_RVM_PATH/wrappers/ruby-1.9.3-p429/ruby&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;接著在 &lt;code&gt;/etc/apache2/sites-enabled&lt;/code&gt; 底下建立一個 &lt;code&gt;YOUR_APP.conf&lt;/code&gt;，加入以下設定：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;VirtualHost&lt;/span&gt;&lt;span&gt; *:80&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ServerName YOUR_DOMAIN_NAME&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;DocumentRoot YOUR_RAILS_PATH/public&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Directory&lt;/span&gt;&lt;span&gt; YOUR_RAILS_PATH/public&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;AllowOverride all&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Options -MultiViews&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;Directory&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;VirtualHost&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;接著重啟 Apache：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;a2dissite&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;service&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;apache2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;reload&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;service&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;apache2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rstart&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;apache2ctl&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;restart&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;大致上 server 環境都建置好了，最後要設定自動化部署工具，使用 Capistrano 這個套件：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;gem&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;capistrano&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;安裝完之後，前往你的 Rails 目錄：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;capify&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;指令完成之後 Rails 目錄會新增 &lt;code&gt;config/deploy.rb&lt;/code&gt;，開啟並設定一些部署參數，大致上會如下：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;set &lt;/span&gt;&lt;span&gt;:application&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;YOUR_RAILS_APP_NAME&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;set &lt;/span&gt;&lt;span&gt;:repository&lt;/span&gt;&lt;span&gt;,  &lt;/span&gt;&lt;span&gt;&quot;YOUR_GIT_REPOSITORY_PATH&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;set &lt;/span&gt;&lt;span&gt;:branch&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;master&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;set &lt;/span&gt;&lt;span&gt;:scm&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;:git&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;set &lt;/span&gt;&lt;span&gt;:user&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;YOUR_GIT_USERNAME&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;set &lt;/span&gt;&lt;span&gt;:port&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;22&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;set &lt;/span&gt;&lt;span&gt;:deploy_to&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;YOUR_SERVER_APP_PATH&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;set &lt;/span&gt;&lt;span&gt;:deploy_via&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;:remote_cache&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;set &lt;/span&gt;&lt;span&gt;:use_sudo&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;role &lt;/span&gt;&lt;span&gt;:web&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;YOUR_WEB_SERVER_DOMAIN&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;role &lt;/span&gt;&lt;span&gt;:app&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;YOUR_APP_SERVER_DOMAIN&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;role &lt;/span&gt;&lt;span&gt;:db&lt;/span&gt;&lt;span&gt;,  &lt;/span&gt;&lt;span&gt;&quot;YOUR_DB_SERVER_DOMAIN&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;…&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;接著可以開始部署了：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cap&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;deploy:setup&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cap&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;deploy:cold&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;下完以上指令後，server 上的目錄會出現 &lt;code&gt;current&lt;/code&gt;，&lt;code&gt;release&lt;/code&gt; 這些資料夾，current 這個目錄就是最新部署的位置，前往這個目錄，安裝從 Git 拉下來的 Rails：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bundle&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;這樣就完成所有 server 部署的配置了，之後只要網站有更新，直接執行以下部署指令，server 就會從 Git 上自動更新：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cap&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;deploy&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;如果以上步驟都沒發生問題，網站也成功運行，別忘了使用 VirtualBox 的&lt;strong&gt;快照&lt;/strong&gt;功能，儲存一份當前狀態，這樣之後就算 server 被搞爛了也能迅速恢復到最初狀態 :-)&lt;/p&gt;
&lt;p&gt;小撇步：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;不習慣 Ubuntu 的視窗桌面，可以使用 &lt;code&gt;Ctrl-Alt-F1&lt;/code&gt; 切換到 Console mode，&lt;code&gt;Ctrl-Alt-F7&lt;/code&gt; 切回視窗模式&lt;/li&gt;
&lt;li&gt;有時會發生 server 網路斷線的情況，雖然不知道這招是不是對的，但用到現在網路還蠻穩定的，開啟 &lt;code&gt;/etc/ppp/options&lt;/code&gt;，將 &lt;code&gt;lcp-echo-failure&lt;/code&gt; 值設高一點，這裡設 15&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;參考文章：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;http://ihower.tw/rails3/index.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Ruby on Rails 實戰聖經&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;</content:encoded><category>Tech</category></item><item><title>如何寫一個 Arduino Library？</title><link>https://blog.amowu.com/posts/2013-04-26-how-to-create-arduino-library/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2013-04-26-how-to-create-arduino-library/</guid><description>假設要寫一個摩斯密碼（telegraph）的 library：</description><pubDate>Fri, 26 Apr 2013 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;假設要寫一個摩斯密碼（telegraph）的 library：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;先在 Arduino 目錄底下（預設是&lt;code&gt;/Users/XXX/Documents/Arduino&lt;/code&gt;）建立一個 &lt;code&gt;libraries&lt;/code&gt; 資料夾&lt;/li&gt;
&lt;li&gt;建立一個資料夾取名為 &lt;code&gt;Telegraph&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;建立 &lt;code&gt;Telegraph.h&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;#ifndef&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Telegraph_h&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;#define&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Telegraph_h&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Telegraph&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;public:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;Telegraph&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;send_message&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;char*&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;message&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;span&gt; // 要公開給別人使用的 method.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;private:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;#endif&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;建立 &lt;code&gt;Telegraph.cpp&lt;/code&gt;（舊版使用 &lt;code&gt;WProgram.g&lt;/code&gt;，新版使用 &lt;code&gt;Arduino.h&lt;/code&gt;）：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;#if&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;defined&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;ARDUINO&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ARDUINO&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;100&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;#include&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Arduino.h&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;#else&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;#include&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;WProgram.h&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;#endif&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;#include&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Telegraph.h&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Telegraph&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;Telegraph&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Telegraph&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;send_message&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;char*&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;message&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;到這邊基本上已經寫好一個 library 了，接下來開啟一個新的 Ardiono 專案來使用：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;#include&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;lt;Telegraph.h&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Telegraph&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;telegraph&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;setup&lt;/span&gt;&lt;span&gt;() {}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;loop&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;telegraph.&lt;/span&gt;&lt;span&gt;send_message&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;Hello world&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;更多詳細的範例原始碼可以參考這個 &lt;a href=&quot;https://github.com/amowu/arduimo/tree/master/Telegraph&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;GitHub repository&lt;/a&gt;。&lt;/p&gt;</content:encoded><category>Tech</category></item><item><title>Hello Arduino!</title><link>https://blog.amowu.com/posts/2013-04-14-hello-arduino/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2013-04-14-hello-arduino/</guid><description>說來慚愧，去年才知道有 Arduino 這麼好玩的東西，連國外小朋友都知道它有多火紅！（不過最近好像是某樹莓派比較牛）。</description><pubDate>Sun, 14 Apr 2013 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;說來慚愧，去年才知道有 Arduino 這麼好玩的東西，連國外小朋友都知道它有多火紅！（不過最近好像是某樹莓派比較牛）。&lt;/p&gt;
&lt;p&gt;好歹以前也是電子相關科系的，想說入門應該不難，回老家把塵封已久的高職工具包翻出來，今年要試作一隻簡單的機器人！既然決心已定，就開始做一些行前準備，買了幾本書跟一些工具來練習：&lt;/p&gt;
&lt;h2 id=&quot;arduino快速上手指南&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#arduino快速上手指南&quot;&gt;&lt;/a&gt;&lt;a href=&quot;http://www.anobii.com/books/Arduino%E5%BF%AB%E9%80%9F%E4%B8%8A%E6%89%8B%E6%8C%87%E5%8D%97/9789866076510/0160e1cadeb309e4e7/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Arduino快速上手指南&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;200&quot; height=&quot;280&quot; src=&quot;https://blog.amowu.com/_astro/image1.Ts1X_Rx7_Z26vHVh.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image1.Ts1X_Rx7_Z26vHVh.webp 200w&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;prototyping-lab邊做邊學arduino的運用實例&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#prototyping-lab邊做邊學arduino的運用實例&quot;&gt;&lt;/a&gt;&lt;a href=&quot;http://www.anobii.com/books/Prototyping_Lab%E3%80%8C%E9%82%8A%E5%81%9A%E9%82%8A%E5%AD%B8%E3%80%8D%EF%BC%8CArduino%E7%9A%84%E9%81%8B%E7%94%A8%E5%AF%A6%E4%BE%8B/9789866076428/01314863f2de56513c/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Prototyping Lab「邊做邊學」Arduino的運用實例&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;200&quot; height=&quot;280&quot; src=&quot;https://blog.amowu.com/_astro/image2.Bn9-oDgy_L3c0M.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image2.Bn9-oDgy_L3c0M.webp 200w&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;make&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#make&quot;&gt;&lt;/a&gt;&lt;a href=&quot;http://www.makezine.com.tw/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Make&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;200&quot; height=&quot;280&quot; src=&quot;https://blog.amowu.com/_astro/image3.B0rlvldN_Z1Ie1Gc.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image3.B0rlvldN_Z1Ie1Gc.webp 200w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;由馥林文化代理出版，國際中文版的雜誌，一季一期，訂閱一年約一千台幣，裡面的內容很豐富，強力推薦！&lt;/p&gt;
&lt;h2 id=&quot;入門工具包&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#入門工具包&quot;&gt;入門工具包&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;雖然自己有一些零件，但是要能完成書上的作品還是有缺，所以買了一個簡單的工具組合，基本上拍賣網站都能找到，價格大約都在一千元台幣上下。&lt;/p&gt;
&lt;h2 id=&quot;猜數字遊戲&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#猜數字遊戲&quot;&gt;猜數字遊戲&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;522&quot; src=&quot;https://blog.amowu.com/_astro/image4.DdK0sj0h_Z1KQSot.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image4.DdK0sj0h_ZsJIA3.webp 640w, https://blog.amowu.com/_astro/image4.DdK0sj0h_Z1KQSot.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;這是我的第一個實作，也是 &lt;strong&gt;Arduino 快速上手指南&lt;/strong&gt;的第一章，主要的玩法是先用第一顆按鈕按出你要猜的數字，LED 會以二進位的方式顯示，決定後再按下另外一顆按鈕，Arduino 會隨機產生一組數字，如果玩家猜中的話，LED 燈就會閃爍。&lt;/p&gt;
&lt;p&gt;這個小電路做完後基本上可以了解：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如何在電腦上寫程式, 並上傳到 Arduino 運作&lt;/li&gt;
&lt;li&gt;基本的電路零件知識，ex：LED、push button&lt;/li&gt;
&lt;li&gt;如何利用 Serial 來 debug&lt;/li&gt;
&lt;li&gt;如何利用 Serial 來傳送和接收資料&lt;/li&gt;
&lt;li&gt;如何使用網路上別人寫好的 Library&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;碰到的問題&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#碰到的問題&quot;&gt;碰到的問題&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;一開始電腦一直抓不到板子，後來發現我的 USB 線是充電專用的 囧&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;推薦工具&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#推薦工具&quot;&gt;推薦工具&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://fritzing.org/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Fritzing&lt;/a&gt; — 畫電路圖的工具&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;373&quot; src=&quot;https://blog.amowu.com/_astro/image5.BcmSdNmq_Z13O7dq.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image5.BcmSdNmq_EJjc0.webp 640w, https://blog.amowu.com/_astro/image5.BcmSdNmq_Z13O7dq.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;700&quot; height=&quot;529&quot; src=&quot;https://blog.amowu.com/_astro/image6._QrtWPlj_Z1cHU5e.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/image6._QrtWPlj_Z1gRBzK.webp 640w, https://blog.amowu.com/_astro/image6._QrtWPlj_Z1cHU5e.webp 700w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Source code 跟之後的專案都會放在 &lt;a href=&quot;https://github.com/amowu/arduimo&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;GitHub&lt;/a&gt; 上，歡迎有興趣的人一起交流 :-)&lt;/p&gt;</content:encoded><category>Tech</category></item><item><title>getDefinitionByName with swc</title><link>https://blog.amowu.com/posts/2013-03-06-getdefinitionbyname-with-swc/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2013-03-06-getdefinitionbyname-with-swc/</guid><description>一般 Flash 程式開發時，取資源的方式有兩種：</description><pubDate>Wed, 06 Mar 2013 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;一般 Flash 程式開發時，取資源的方式有兩種：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;.swf&lt;/li&gt;
&lt;li&gt;.swc&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;.swf 檔屬於 Runtime 時從外部載進來取用的方式，通常會使用 &lt;code&gt;Loader&lt;/code&gt; 來作處理：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;loader&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;Loader &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Loader&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;request&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;URLRequest &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;URLRequest&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;YOUR_SWF_PATH&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;loader &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Loader&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;loader.&lt;/span&gt;&lt;span&gt;load&lt;/span&gt;&lt;span&gt;(request);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;loader.contentLoaderInfo.&lt;/span&gt;&lt;span&gt;addEventListener&lt;/span&gt;&lt;span&gt;(Event.COMPLETE, onLoaderComplete);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; function &lt;/span&gt;&lt;span&gt;onLoaderComplete&lt;/span&gt;&lt;span&gt;(event&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;Event)&lt;/span&gt;&lt;span&gt;:void&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;loader&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;LoaderInfo &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; event.currentTarget as LoaderInfo;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;loader.&lt;/span&gt;&lt;span&gt;removeEventListener&lt;/span&gt;&lt;span&gt;(Event.COMPLETE, onLoaderComplete);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; function &lt;/span&gt;&lt;span&gt;getClassReference&lt;/span&gt;&lt;span&gt;(className&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;String)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;Class&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; loader.contentLoaderInfo.&lt;/span&gt;&lt;span&gt;getDefinition&lt;/span&gt;&lt;span&gt;(className) as Class;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;.swc 擋則是 Compiler 時就會一起包進來，要取用內部元件通常就直接 &lt;code&gt;new&lt;/code&gt; 出來就可以：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;component&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;CLASS_NAME &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CLASS_NAME&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;如果想要動態取得元件的話會碰到一些問題，一般來講會用 &lt;code&gt;getDefinitionByName(CLASS_NAME:String)&lt;/code&gt; 這個 method：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; flash.utils.getDefinitionByName;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ClassReference&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;Class &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;getDefinitionByName&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;CLASS_NAME&quot;&lt;/span&gt;&lt;span&gt;) as Class;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;obj&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;Object &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ClassReference&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;不過編譯完執行後會碰到錯誤，解決方式如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Project &amp;gt; Properties&lt;/li&gt;
&lt;li&gt;Flex Compiler &amp;gt; 加入參數 &lt;code&gt;-include-libraries=YOUR_ABSOLUTE_SWC_PATH.swc&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>Tech</category></item><item><title>Sublime Text 2 Shortcuts (Mac OSX)</title><link>https://blog.amowu.com/posts/2013-03-04-sublime-shortcuts-mac/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2013-03-04-sublime-shortcuts-mac/</guid><description>Default</description><pubDate>Mon, 04 Mar 2013 00:00:00 GMT</pubDate><content:encoded>&lt;h3 id=&quot;general&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#general&quot;&gt;General&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Command palette&lt;code&gt;⌘&lt;/code&gt; + &lt;code&gt;⇧&lt;/code&gt; + &lt;code&gt;P&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Toggle side bar&lt;code&gt;⌘&lt;/code&gt; + &lt;code&gt;K&lt;/code&gt; + &lt;code&gt;B&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;goto&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#goto&quot;&gt;Goto&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Goto anything&lt;code&gt;⌘&lt;/code&gt; + &lt;code&gt;P&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Goto symbol&lt;code&gt;⌘&lt;/code&gt; + &lt;code&gt;R&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Goto line&lt;code&gt;⌃&lt;/code&gt; + &lt;code&gt;G&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;view&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#view&quot;&gt;View&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Destraction Free Mode&lt;code&gt;^&lt;/code&gt; + &lt;code&gt;⇧&lt;/code&gt; + &lt;code&gt;⌘&lt;/code&gt; + &lt;code&gt;F&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;edit&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#edit&quot;&gt;Edit&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Select line&lt;code&gt;⌘&lt;/code&gt; + &lt;code&gt;L&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Select word&lt;code&gt;⌘&lt;/code&gt; + &lt;code&gt;D&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Delete from cursor to end of line&lt;code&gt;⌘&lt;/code&gt; + &lt;code&gt;K&lt;/code&gt; + &lt;code&gt;K&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Delete from cursor to start of line&lt;code&gt;⌘&lt;/code&gt; + &lt;code&gt;K&lt;/code&gt; + &lt;code&gt;⌫&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Delete line&lt;code&gt;⌃&lt;/code&gt; + &lt;code&gt;⇧&lt;/code&gt; + &lt;code&gt;K&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Duplicate line&lt;code&gt;⌘&lt;/code&gt; + &lt;code&gt;⇧&lt;/code&gt; + &lt;code&gt;D&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Join lines&lt;code&gt;⌘&lt;/code&gt; + &lt;code&gt;J&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Upper case&lt;code&gt;⌘&lt;/code&gt; + &lt;code&gt;K&lt;/code&gt; + &lt;code&gt;U&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Lower case&lt;code&gt;⌘&lt;/code&gt; + &lt;code&gt;K&lt;/code&gt; + &lt;code&gt;L&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Jump to matching brackets&lt;code&gt;⌃&lt;/code&gt; + &lt;code&gt;M&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Select content into brackets&lt;code&gt;⌃&lt;/code&gt; + &lt;code&gt;⇧&lt;/code&gt; + &lt;code&gt;M&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Comment&lt;code&gt;⌘&lt;/code&gt; + &lt;code&gt;/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Block comment&lt;code&gt;⌘&lt;/code&gt; + &lt;code&gt;⌥&lt;/code&gt; + &lt;code&gt;/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Autocomplete&lt;code&gt;⌃&lt;/code&gt; + &lt;code&gt;Space&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Insert line before&lt;code&gt;⌘&lt;/code&gt; + &lt;code&gt;⇧&lt;/code&gt; + &lt;code&gt;↩&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Insert line after&lt;code&gt;⌘&lt;/code&gt; + &lt;code&gt;↩&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Move up line&lt;code&gt;⌃&lt;/code&gt; + &lt;code&gt;⌘&lt;/code&gt; + &lt;code&gt;Up&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Move down line&lt;code&gt;⌃&lt;/code&gt; + &lt;code&gt;⌘&lt;/code&gt; + &lt;code&gt;Down&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Fold&lt;code&gt;⌘&lt;/code&gt; + &lt;code&gt;⌥&lt;/code&gt; + &lt;code&gt;[&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Unfold&lt;code&gt;⌘&lt;/code&gt; + &lt;code&gt;⌥&lt;/code&gt; + &lt;code&gt;]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;html&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#html&quot;&gt;HTML&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Close tag&lt;code&gt;⌘&lt;/code&gt; + &lt;code&gt;⌥&lt;/code&gt; + &lt;code&gt;.&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Select content into tag&lt;code&gt;⌘&lt;/code&gt; + &lt;code&gt;⇧&lt;/code&gt; + &lt;code&gt;A&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;find&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#find&quot;&gt;Find&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Find&lt;code&gt;⌘&lt;/code&gt; + &lt;code&gt;F&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Replace&lt;code&gt;⌘&lt;/code&gt; + &lt;code&gt;⌥&lt;/code&gt; + &lt;code&gt;F&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Find next occurrence of current word&lt;code&gt;⌘&lt;/code&gt; + &lt;code&gt;⌥&lt;/code&gt; + &lt;code&gt;G&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Select all occurrences of current word for multiple editing&lt;code&gt;⌘&lt;/code&gt; + &lt;code&gt;⌃&lt;/code&gt; + &lt;code&gt;G&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Find in files&lt;code&gt;⌘&lt;/code&gt; + &lt;code&gt;⇧&lt;/code&gt; + &lt;code&gt;F&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;splits&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#splits&quot;&gt;Splits&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Single column&lt;code&gt;⌘&lt;/code&gt; + &lt;code&gt;⌥&lt;/code&gt; + &lt;code&gt;1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Two columns&lt;code&gt;⌘&lt;/code&gt; + &lt;code&gt;⌥&lt;/code&gt; + &lt;code&gt;2&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Grid (4 groups)&lt;code&gt;⌘&lt;/code&gt; + &lt;code&gt;⌥&lt;/code&gt; + &lt;code&gt;5&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;focus group&lt;code&gt;⌃&lt;/code&gt; + &lt;code&gt;Num&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Move file to group&lt;code&gt;⌃&lt;/code&gt; + &lt;code&gt;⇧&lt;/code&gt; + &lt;code&gt;Num&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Select tab&lt;code&gt;⌘&lt;/code&gt; + &lt;code&gt;Num&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Switch tab&lt;code&gt;⌃&lt;/code&gt; + &lt;code&gt;tab&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;custom&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#custom&quot;&gt;Custom&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Reveal in side bar&lt;code&gt;⌃&lt;/code&gt; + &lt;code&gt;⌘&lt;/code&gt; + &lt;code&gt;R&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;package&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#package&quot;&gt;Package&lt;/a&gt;&lt;/h3&gt;
&lt;h3 id=&quot;emmet&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#emmet&quot;&gt;&lt;/a&gt;&lt;a href=&quot;https://github.com/sergeche/emmet-sublime&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Emmet&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Expand abbreviation&lt;code&gt;Tab&lt;/code&gt; or &lt;code&gt;⌃&lt;/code&gt; + &lt;code&gt;E&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;easymotion&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#easymotion&quot;&gt;&lt;/a&gt;&lt;a href=&quot;https://github.com/tednaleid/sublime-EasyMotion&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;EasyMotion&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Jump&lt;code&gt;⌘&lt;/code&gt; + &lt;code&gt;;&lt;/code&gt; + &lt;code&gt;a-zA-Z0-9&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Select all text between cursor&lt;code&gt;⌘&lt;/code&gt; + &lt;code&gt;⇧&lt;/code&gt; + &lt;code&gt;;&lt;/code&gt; + &lt;code&gt;a-zA-Z0-9&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;sass-build&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#sass-build&quot;&gt;&lt;/a&gt;&lt;a href=&quot;https://github.com/jaumefontal/SASS-Build-SublimeText2&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Sass Build&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Build&lt;code&gt;⌘&lt;/code&gt; + &lt;code&gt;B&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;jsformat&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#jsformat&quot;&gt;&lt;/a&gt;&lt;a href=&quot;https://github.com/jdc0589/JsFormat&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;JsFormat&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Format javascript&lt;code&gt;⌃&lt;/code&gt; + &lt;code&gt;⌥&lt;/code&gt; + &lt;code&gt;F&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>Tech</category></item><item><title>我的數位生活 2012</title><link>https://blog.amowu.com/posts/2013-02-28-my-cloud-2012/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2013-02-28-my-cloud-2012/</guid><description>這次不談程式的東西，來聊一些 2012 年的數位生活心得，這幾年許多應用快速雲端化加上智慧型裝置普及率提高，幾乎大部份的工作都可以離開 PC 來完成了，剛好在去年我達成了 Apple 家族四天王（iPhone、iPad、AppleTV、Macbook（制霸！所以在這邊整理分享…</description><pubDate>Thu, 28 Feb 2013 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;這次不談程式的東西，來聊一些 2012 年的數位生活心得，這幾年許多應用快速雲端化加上智慧型裝置普及率提高，幾乎大部份的工作都可以離開 PC 來完成了，剛好在去年我達成了 Apple 家族四天王（iPhone、iPad、AppleTV、Macbook（制霸！所以在這邊整理分享 2012 年用了哪些服務。&lt;/p&gt;
&lt;p&gt;因為我手上的裝置都是蘋果，所以下面介紹的服務會以 iOS 系列為主，Android 的朋友抱歉囉。&lt;/p&gt;
&lt;p&gt;我在找想用的服務時會盡量挑以下幾點符合的：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;**多螢一雲，**有提供各平台的 app、網頁，或桌面應用軟體&lt;/li&gt;
&lt;li&gt;**社群化，**擁有自己的社交互動圈，或整合 twitter、facebook 等服務&lt;/li&gt;
&lt;li&gt;**手機為主 PC 為輔，**優先挑選手機 app 好用的服務&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;另外，根據服務性質的不同，也會看是適合哪個平台來選擇，例如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;iPhone&lt;/strong&gt; 人在外面，ex：工作、通勤等，通常生產力相關的工具會比較適合在這時候使用&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;iPad&lt;/strong&gt; 在家裡，但不是電腦前，ex: 沙發、床上，通常使用閱讀，影片，繪畫軟體較適合&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Apple TV&lt;/strong&gt; 在客廳，不過軟體不多，通常都拿來看影劇和聽音樂居多&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PC&lt;/strong&gt; 電腦或筆電，通常都是拿來做較複雜的事情，ex：整理、規劃、編輯&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;OK，在了解完我挑選應用服務的方式後，下面列出的就是我要分享的清單：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;知識管理&lt;/li&gt;
&lt;li&gt;生產力管理&lt;/li&gt;
&lt;li&gt;人脈管理&lt;/li&gt;
&lt;li&gt;財務管理&lt;/li&gt;
&lt;li&gt;休閒娛樂&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;知識管理&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#知識管理&quot;&gt;知識管理&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;資訊爆炸的時代，每天有非常多的新訊息從四處而來，不應該漫無目的的吞嚥，需要過濾有用的資訊來吸收。&lt;/p&gt;
&lt;h3 id=&quot;google-readerweb&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#google-readerweb&quot;&gt;Google Reader &lt;/a&gt;&lt;a href=&quot;https://www.google.com/reader/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;web&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;我一天最主要的新聞來源不是報紙或入口網站，而是 Google Reader，它會自動把我訂閱網站的新文章送到我眼前，是經過過濾對我而言有用的資訊。&lt;/p&gt;
&lt;p&gt;所有我想關注的網站，都會經由 RSS 發送最新文章到 Google Reader 上，我從 2010 年開始使用到現在，訂閱網站數 200 個左右，目前一天大概會有 400 至 600 篇新文章，當然不會每天都把它讀完，所以分類很重要，以下是我在訂閱一個網站時的判斷標準：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;**重要程度：**依照網站文章對你的重要性分一顆星到三顆星&lt;/li&gt;
&lt;li&gt;**語言：**依照網站語言來分類，ex: 中文、英文、日文&lt;/li&gt;
&lt;li&gt;**類型：**一般分類，ex：攝影、科技、設計&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所以當我訂閱網站時，最少會給它三個標籤來分類, 例如我每天必讀的 &lt;a href=&quot;http://wired.tw/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;WIRED.tw&lt;/a&gt;，我給他的分類就是 &lt;code&gt;✭✭✭中文科技&lt;/code&gt;，每天一定會把三顆星的文章看完，剩下的如果沒時間讀, 就毫不猶豫的把它設定成&lt;strong&gt;已閱讀&lt;/strong&gt;吧。&lt;/p&gt;
&lt;p&gt;將 Google Reader 這個基石穩定後, 之後的很多服務都可以跟它串聯：&lt;/p&gt;
&lt;h3 id=&quot;reeder-iphoneipad&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#reeder-iphoneipad&quot;&gt;Reeder &lt;/a&gt;&lt;a href=&quot;http://reederapp.com/iphone/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;iPhone&lt;/a&gt; &lt;a href=&quot;http://reederapp.com/ipad/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;iPad&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;簡單講，這是一款 iPhone 和 iPad 的 Google Reader app，類似應用在 app store 很多，試用過幾套後選擇這款，速度流暢品質高，平常搭交通車就看手機，睡前在床上就看平板，讓你隨時閱讀有興趣的文章。&lt;/p&gt;
&lt;h3 id=&quot;zite-ios&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#zite-ios&quot;&gt;Zite &lt;/a&gt;&lt;a href=&quot;http://itunes.com/apps/zite/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;iOS&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Zite 這款 app 就有點不一樣，他會根據你的 Google Reader 訂閱網站來分析一些你會有興趣的文章，算是 Google Reader 的球探，幫你找出還沒訂閱的資訊來源。Zite 除了分析你的 Google Reader 外，還可以導入像 Twitter、Facebook 和接下來會提到的 Pocket 來分析你平常看的文章，加強過濾呈現的文章。&lt;/p&gt;
&lt;h3 id=&quot;pocketwebios&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#pocketwebios&quot;&gt;Pocket`&lt;/a&gt;&lt;a href=&quot;https://www.blogger.com/getpocket.com&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;web&lt;/a&gt; &lt;a href=&quot;https://itunes.apple.com/app/read-it-later-pro/id309601447&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;iOS&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Pocket 是一個稍後閱讀服務，類似網站也不少，我從去年才轉用 Pocket，之前是 Instapaper。&lt;/p&gt;
&lt;p&gt;如果比喻 Google Reader 是一份我想看的報紙，那 Pocket 就是我剪下的文章收集盒，每天有這麼多的文章呈現在你面前，但是當你讀到一篇不錯的文章時，想要先保存下來，之後在細細品嘗時，就是 Pocket 的強項了，Pocket 提供瀏覽器外掛，當你在網路上發現有趣但還沒時間讀的文章時，就可以把它先暫存起來。&lt;/p&gt;
&lt;p&gt;所以最後我真正想讀的文章會是在 Pocket 這個收集箱裡面，Google Reader 只是我快速瀏覽的地方，每看到一篇有趣的標題時，就會把它加到 Pocket，然後再一次好好的把它讀完，讀完的文章點一下&lt;strong&gt;打勾&lt;/strong&gt;的按鈕將它封存起來，盡量不要讓存在 Pocket 的待讀文章留太多。&lt;/p&gt;
&lt;h3 id=&quot;evernote-webios&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#evernote-webios&quot;&gt;Evernote &lt;/a&gt;&lt;a href=&quot;https://itunes.apple.com/app/read-it-later-pro/id309601447&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;web&lt;/a&gt; &lt;a href=&quot;https://itunes.apple.com/us/app/evernote/id281796108&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;iOS&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;前面講到 Google Reader 是一份報紙，Pocket 是文章收集盒的話，那 Evernote 就是你的剪貼簿了！&lt;/p&gt;
&lt;p&gt;Evernote 這幾年相當火紅，用途很廣，尤其日本有不少針對 Evernote 深入使用的相關書籍，而我自己本身是將 Evernote 拿來當數位筆記本來使用，會把一些有用的精華文章筆記後收藏起來，Evernote 同樣提供一些有用的瀏覽器外掛讓我們使用更方便，其中最推薦的是 &lt;a href=&quot;http://evernote.com/intl/zh-tw/clearly/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Clearly&lt;/a&gt; 跟 &lt;a href=&quot;http://evernote.com/intl/zh-tw/webclipper/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Evernote Clipper&lt;/a&gt; 這個套件，它可以先把網頁轉成好閱讀的純圖文，然後提供畫底線工具增加筆記方便，再將編輯過的文章同步到 Evernote，之後若是 Google 搜尋的關鍵字已經有相關筆記時，就可以直接派上用場了，目前我自己主要將筆記本分成這幾類：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;學習筆記本&lt;/strong&gt; 用來放一些程式設計相關的筆記，例如一些工具的安裝流程&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;知識筆記本&lt;/strong&gt; 收集一些跟日常相關的筆記，ex：理財、健康&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;點子筆記本&lt;/strong&gt; 當你在路上突然有了一些 idea，趕快拿起手機簡單記一下，回到電腦前再詳細編輯&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;圖鑑筆記本&lt;/strong&gt; 通常拿來放一些網頁的設計圖片，或是一些工具的列表&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;名言筆記本&lt;/strong&gt; 會把一些很喜歡的名言放在這本筆記&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;笑話筆記本&lt;/strong&gt; 顧名思義，裡面全部放一些收集來，令人會心一笑的笑話&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;食譜筆記本&lt;/strong&gt; 簡單的食譜筆記，不過發現很少使用，後來都是使用&lt;a href=&quot;http://icook.tw/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;愛料理&lt;/a&gt;之類的網站較多&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;旅遊筆記本&lt;/strong&gt; 為了去年第一次去日本自助旅遊而開的筆記本，從頭開始規劃很有成就感！&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;工作筆記本&lt;/strong&gt; 一些工作上會用到的注意事項，ex：列印機安裝步驟&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;delicious-webiphone&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#delicious-webiphone&quot;&gt;Delicious &lt;/a&gt;&lt;a href=&quot;https://delicious.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;web&lt;/a&gt; &lt;a href=&quot;https://itunes.apple.com/us/app/delicious-official-app/id580295142&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;iPhone&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;跟 Google Reader 不同，Delicious 是一個單純的書籤收藏服務，但它不是很好用，類似服務做得更好的有一堆，但已經是老字號了所以就，加上現在的瀏覽器都已經內建同步書籤的功能，所以 Delicious 已經不是很重要了，好處就是使用者多，當你收藏一個網站後可以看到其他人對這個網站的評論和分類建議，當我從 Google Reader、Pocket 或者其他地方看到了一個不錯的新網站，我就會使用 &lt;a href=&quot;https://goo.gl/8fYoh&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Delicious.com&lt;/a&gt; 這個外掛來加入書籤，連分類都幫你分好了，方便之後能快速搜尋。&lt;/p&gt;
&lt;h3 id=&quot;blogger-webios&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#blogger-webios&quot;&gt;Blogger &lt;/a&gt;&lt;a href=&quot;https://www.blogger.com/www.blogger.com&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;web&lt;/a&gt; &lt;a href=&quot;https://itunes.apple.com/us/app/blogger/id459407288&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;iOS&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;當然只是閱讀新知是不夠的，定期寫一些文章回饋社會也是很重要的，訓練聽讀寫說中的&lt;strong&gt;寫&lt;/strong&gt;技能，網誌服務也是很多，Wordpress 跟 Blogger 應該算是比較大的，而我自己已經使用 Google 的 Blogger 約三年左右，雖然彈性不比自己架的 Wordpress 大，外掛也少得可憐，但是穩定，平常寫寫文章，看看分析其實就夠了。&lt;/p&gt;
&lt;h2 id=&quot;生產力管理&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#生產力管理&quot;&gt;生產力管理&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;不管是工作還是日常生活，好好運用生產力工具能幫助你大大提升效率。&lt;/p&gt;
&lt;h3 id=&quot;gmail-web&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#gmail-web&quot;&gt;Gmail &lt;/a&gt;&lt;a href=&quot;https://mail.google.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;web&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;我想大部份的人應該都是用 Gmail 來收發信吧，直到最近這幾年智慧型裝置起來，我自己本身是使用 iPhone 內建的郵件，我把所有私人信箱跟公司信箱都整合在 Gmail，然後再經由 Exchange 同步到手機，所以只需要一組 Gmail 帳號我就可以管理所有來信了。&lt;/p&gt;
&lt;h3 id=&quot;google-calendarweb&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#google-calendarweb&quot;&gt;Google Calendar &lt;/a&gt;&lt;a href=&quot;https://www.google.com/calendar/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;web&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;行事曆部分跟信箱差不多，已經把私人跟公司行事曆整合到 Google 月曆，然後再同步到手機行事曆統一管理，像會議通知這種需即時知道的事情還是以手機為主會比較適合，以前都是需要在電腦前開著桌面月曆之類的軟體才能掌握行程，現在智慧型裝置時代已經變成時間快到時會主動通知你的方便模式，所以才會以手機為主，PC 為輔，手機就是你的專屬秘書。&lt;/p&gt;
&lt;h3 id=&quot;reminders-web&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#reminders-web&quot;&gt;Reminders &lt;/a&gt;&lt;a href=&quot;https://www.icloud.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;web&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;這種待辦事項的軟體我也用了很多種，最後 iOS5 內建後我就一直用到現在，功能上雖然陽春，但已經夠用了，目前我把它規劃成三種清單在使用：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;繳費清單&lt;/strong&gt; 一些定期要繳的費用，ex：房租、水電、手機網路費&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;購物清單&lt;/strong&gt; 想到要買的東西就加到這份清單吧，到大賣場時檢查特別方便，ex：垃圾袋、蛋&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;待辦清單&lt;/strong&gt; 其它不屬於上面的提醒事項就加到這份清單吧，ex：剪頭髮，換機油&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;notes-web&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#notes-web&quot;&gt;Notes &lt;/a&gt;&lt;a href=&quot;https://www.icloud.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;web&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;這個手機內建的備忘錄和 Evernote 不同，比較適合記錄一些可拋棄的小記事，手邊又剛好沒有紙筆的時候，例如主管突然交代的文件列印之類的。另外，用錄音的方式記下來也頗方便唷。&lt;/p&gt;
&lt;h3 id=&quot;dropbox-webios&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#dropbox-webios&quot;&gt;Dropbox &lt;/a&gt;&lt;a href=&quot;https://www.dropbox.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;web&lt;/a&gt; &lt;a href=&quot;https://itunes.apple.com/us/app/dropbox/id327630330&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;iOS&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;雖然 Dropbox 跟 Google Drive 這兩個看似一樣的東西，但仔細用過後還是有一些差異，我個人的用法如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Dropbox&lt;/strong&gt; 同步/備份電腦上大部份的檔案，與其他人分享資料夾共同協作&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Google Drive&lt;/strong&gt; 儲存/開啟信箱的附件檔案&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;人脈管理&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#人脈管理&quot;&gt;人脈管理&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;google-contactsweb&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#google-contactsweb&quot;&gt;Google Contacts &lt;/a&gt;&lt;a href=&quot;https://www.google.com/contacts/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;web&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;跟 Gmail 在一起的聯絡人管理服務，看起來很陽春，可是為了方便寄信，所以還是繼續在用，一樣可以經由 Exchange 同步到 iPhone，不過一些項目跟大頭貼同步後會有問題，但不影響使用，基本上認識了新朋友或是在 Facebook 找到老朋友，我都會馬上在 Google 聯絡人上建檔，個人資訊也會盡量齊全，例如生日會自動同步到行事曆，電話號碼也可以直接用手機播打，聯絡地址也能直接開啟地圖找路，是個有價值的投資。&lt;/p&gt;
&lt;h3 id=&quot;facebook-webios&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#facebook-webios&quot;&gt;Facebook &lt;/a&gt;&lt;a href=&quot;https://www.facebook.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;web&lt;/a&gt; &lt;a href=&quot;https://itunes.apple.com/tw/app/facebook/id284882215&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;iOS&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;雖然在隱私方面一直有爭議，但不可否認因為 Facebook 的關係，我找回很多已經失聯的老同學，並且當下還能繼續和親戚好友同事保持聯繫，而類似的社交服務我也用不少，看似性質相同，但還是可以做區別：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Facebook 真實的社交圈互動&lt;/li&gt;
&lt;li&gt;Twitter 碎碎念，關注名人&lt;/li&gt;
&lt;li&gt;Google+ 參與一些社群的議題&lt;/li&gt;
&lt;li&gt;Linkedin 業界相關的人脈&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;line-webiphone&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#line-webiphone&quot;&gt;LINE &lt;/a&gt;&lt;a href=&quot;http://line.naver.jp/zh-hant/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;web&lt;/a&gt; &lt;a href=&quot;https://itunes.apple.com/us/app/line/id443904275&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;iPhone&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;去年迅速竄起的火紅通訊服務，用來和關係較好的朋友即時聊天打屁用，基本上用了 Facebook 和 LINE 之後，MSN 就已經可以冰箱了，加上豐富有趣的表情符號跟積極擴張服務的 LINE，未來不容小覷。&lt;/p&gt;
&lt;h2 id=&quot;財務管理&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#財務管理&quot;&gt;財務管理&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;随手记-web-iphoneipad&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#随手记-web-iphoneipad&quot;&gt;随手记 &lt;/a&gt;&lt;a href=&quot;http://www.feidee.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;web&lt;/a&gt; &lt;a href=&quot;https://itunes.apple.com/tw/app/sui-shou-ji-zhuan-ye-ban-for/id373454750&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;iPhone&lt;/a&gt; &lt;a href=&quot;https://itunes.apple.com/tw/app/sui-shou-ji-ji-zhang-li-cai/id475317031&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;iPad&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;這是一款記帳軟體，難得在這麼多的服務之中看到一款中國大陸的軟體，我從大學時期就開始用過不少相關軟體，這款隨手記不管是品質、設計、功能、更新上都無話可說，而且也是少數提供三螢一雲的記帳服務，用了兩年下來，它的分析功能讓我在理財規劃上得到不少幫助，清楚瞭解你的錢到底都花到哪裡去，經過 2012 年的檢討後，我決定今年要改變新的理財方式，參考了&lt;a href=&quot;https://goo.gl/Ci1kZ&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;六個罐子理財法&lt;/a&gt;後，打算將每個月的薪資用下面的方式作分配：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;生活帳戶&lt;/strong&gt; (55%) 基本上這裡的錢就是我一個月可以花的預算，食衣住行生活費，餘額存回定存&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;定存帳戶&lt;/strong&gt; (12%) 長期儲蓄&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;投資帳戶&lt;/strong&gt; (10%) 可以動用的投資現金&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;教育帳戶&lt;/strong&gt; (08%) 投資自己的帳戶，用來買一些學習用的書籍等，每個月餘額會存回定存帳戶&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;娛樂帳戶&lt;/strong&gt; (08%) 非生活的花費全部歸在這，ex：看電影，一樣每個月餘額存回定存&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;預備帳戶&lt;/strong&gt; (04%) 緊急預備金，保持在三個月生活費，剩餘用來孝親，公益，人情往來等&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;活存帳戶&lt;/strong&gt; (03%) 短期的儲蓄，ex：旅遊基金&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;休閒娛樂&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#休閒娛樂&quot;&gt;休閒娛樂&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;itunes-match&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#itunes-match&quot;&gt;iTunes Match&lt;/a&gt;&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;音樂是人類最偉大的發明 No music no life&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;不管是工作，搭交通，在家，音樂對我來講非常的重要，本來一開始是用 Google Music，優點是有 Web 界面可以使用，但是遲遲沒有好用的 iOS app，加上後來買了 Apple TV 之後，經過評估，還是投向 iTunes Match 的懷抱，iTunes Match 可以將你的所有音樂傳上 iCloud，並且還有機會轉成高音質，之後不管是用手機、平板、電視，都可以直接把你的音樂庫搬出來聽，而且沒有容量限制，讓隨身攜帶的 16G iPhone 也能隨時播放 3000 首歌！&lt;/p&gt;
&lt;h3 id=&quot;lastfm-webiphone&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#lastfm-webiphone&quot;&gt;Last.fm &lt;/a&gt;&lt;a href=&quot;https://www.last.fm/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;web&lt;/a&gt; &lt;a href=&quot;https://itunes.apple.com/us/app/last.fm/id284916679&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;iPhone&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;另外提個有趣的服務，Last.fm 是一個音樂的社交服務，安裝外掛之後可以紀錄你的音樂收聽紀錄，幫你分析並推薦符合你興趣的新歌給你。&lt;/p&gt;
&lt;h3 id=&quot;tunein-radio-webios&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#tunein-radio-webios&quot;&gt;TuneIn Radio &lt;/a&gt;&lt;a href=&quot;http://tunein.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;web&lt;/a&gt; &lt;a href=&quot;https://itunes.apple.com/us/app/tunein-radio-pro-listen-to/id319295332&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;iOS&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;電台廣播服務，當不知道要聽什麼音樂的時候我就會開個廣播電台來聽聽，有時會意外發現不少好聽的新歌唷！&lt;/p&gt;
&lt;h3 id=&quot;soundhound-ios&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#soundhound-ios&quot;&gt;SoundHound &lt;/a&gt;&lt;a href=&quot;https://itunes.apple.com/us/app/soundhound/id284972998&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;iOS&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;不小心讓你聽到一首好聽的音樂，但你卻不知道歌名時，就可以拿出手機開啟 SoundHound，它會幫你分析並找出符合的音樂資訊給你，還會附帶 YouTube 影片讓你看喲！&lt;/p&gt;
&lt;h3 id=&quot;youtube-webios&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#youtube-webios&quot;&gt;YouTube &lt;/a&gt;&lt;a href=&quot;https://www.youtube.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;web&lt;/a&gt; &lt;a href=&quot;https://itunes.apple.com/us/app/youtube/id544007664&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;iOS&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;YouTube 就不用介紹了，全世界最大的影音分享社群，其中&lt;strong&gt;稍後觀看&lt;/strong&gt;功能很重要，就像前面提到的，沒時間看完影片的時候可以先暫存起來，等搭車時再拿出手機，在床上時拿出平板，在沙發上就看 Apple TV，任何時間，任何地方都可以繼續觀賞這些影片。&lt;/p&gt;
&lt;h3 id=&quot;podcast-ios&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#podcast-ios&quot;&gt;Podcast &lt;/a&gt;&lt;a href=&quot;https://itunes.apple.com/tw/app/podcasts/id525463029&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;iOS&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Apple 出品的 Podcast 雖然評價一直不高，但上面還是有很多很棒的廣播內容可以訂閱，例如 TED 系列，還有繁體中文字幕喲，而且可以直接在 Apple TV 上看，非常值得！&lt;/p&gt;
&lt;h3 id=&quot;itunes-uios&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#itunes-uios&quot;&gt;iTunes U &lt;/a&gt;&lt;a href=&quot;https://itunes.apple.com/us/app/itunes-u/id490217893&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;iOS&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;同樣是 Apple 的服務，iTunes U 主要提供大學院校的課程讓使用者觀看學習，很多很棒的內容，例如史丹佛大學的 iOS 開發課程系列，收獲良多！&lt;/p&gt;
&lt;h3 id=&quot;google-map-webiphone&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#google-map-webiphone&quot;&gt;Google Map &lt;/a&gt;&lt;a href=&quot;https://maps.google.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;web&lt;/a&gt; &lt;a href=&quot;https://itunes.apple.com/tw/app/google-maps/id585027354&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;iPhone&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Google 地圖我想已經成為你我生活中不可或缺的重要服務吧，尤其對我這種路痴而言，規劃路線、街景，還有完整的商家資料等，都是很有用處資訊，而且 iPhone 版的 app 也終於上架了，不過因為新法規上路，騎車的時候不能使用手機，讓我對之後要推出的 Google Glass 更加期待了。&lt;/p&gt;
&lt;h3 id=&quot;foursquare-webiphone&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#foursquare-webiphone&quot;&gt;Foursquare &lt;/a&gt;&lt;a href=&quot;https://foursquare.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;web&lt;/a&gt; &lt;a href=&quot;https://itunes.apple.com/us/app/foursquare/id306934924&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;iPhone&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Foursqure 也是一個近期火紅的打卡服務，除了打卡收集成就徽章這個有趣的地方外，我去年去高雄跟日本前都會利用它提供的清單功能來事前規劃，每到一個地方直接開啟預先規劃好的清單打卡，方便之外還可以檢視和分享自己的旅行足跡，非常實用！&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://goo.gl/oPzAb&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;2012 高雄&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://goo.gl/Gt8de&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;2012 日本東京&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;foodspotting-webiphone&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#foodspotting-webiphone&quot;&gt;Foodspotting &lt;/a&gt;&lt;a href=&quot;http://www.foodspotting.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;web&lt;/a&gt; &lt;a href=&quot;https://itunes.apple.com/us/app/foodspotting/id350727118&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;iPhone&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;美食地圖分享社群，不過國內使用率不高，可以連結 Facebook 帳號同步分享你吃過的美食。&lt;/p&gt;
&lt;h3 id=&quot;picasa-web&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#picasa-web&quot;&gt;Picasa &lt;/a&gt;&lt;a href=&quot;https://picasaweb.google.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;web&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Google 的相簿服務，雖然 Flickr 比較多人使用，但我只是放一些旅遊的照片，拍的也不專業，就拿 Picasa 來管理相簿了，而且我都直接用手機拍照，然後使用 Google+ 的自動上傳功能，有時間在電腦前就可以直接整理所有照片，非常方便，當然它提供的功能也一點都不馬虎，人臉辨識、影像處理、地點事件分類，社群分享等都很有用。&lt;/p&gt;
&lt;h3 id=&quot;instagram-webiphone&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#instagram-webiphone&quot;&gt;Instagram &lt;/a&gt;&lt;a href=&quot;http://instagram.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;web&lt;/a&gt; &lt;a href=&quot;https://itunes.apple.com/tw/app/instagram/id389801252&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;iPhone&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;不同於 Picasa, Instagram 比較偏向生活中隨處發現的小照片分享，通常可以從一個人拍的相片中看出個人生活風格，是個很有趣的服務。&lt;/p&gt;
&lt;h3 id=&quot;vine-iphone&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#vine-iphone&quot;&gt;Vine &lt;/a&gt;&lt;a href=&quot;https://itunes.apple.com/us/app/vine-make-a-scene/id592447445&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;iPhone&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;簡單說是短片分享的 Instagram，類似服務還有 Viddy，不過老實講我不是很常使用，因為沒有拍短片的習慣 囧，都是無聊看看別人分享的有趣影片居多。&lt;/p&gt;
&lt;h3 id=&quot;anobii-webios&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#anobii-webios&quot;&gt;Anobii &lt;/a&gt;&lt;a href=&quot;http://www.anobii.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;web&lt;/a&gt; &lt;a href=&quot;https://itunes.apple.com/app/anobii/id327440393&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;iOS&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;書籍閱讀分享社群，在書店看到一本想買的書，可以直接用手機掃瞄條碼加入到欲購書單，觀望中的書也可以看看別人留的書評，根據你的書櫃分析出你可能會想看的書籍，很棒的服務！&lt;/p&gt;
&lt;h3 id=&quot;ibook&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#ibook&quot;&gt;iBook&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;雖然台灣的 app store 上沒什麼書可以下載，但支援 pdf 跟 epub 匯入，所以還是可以閱讀許多電子書籍，iPad 必備軟體！&lt;/p&gt;
&lt;h3 id=&quot;newsstand&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#newsstand&quot;&gt;Newsstand&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;跟 iBook 比較起來，這個書報攤的雜誌數量就比較豐富了，直接訂閱最新雜誌，解決去舊雜誌佔空間的問題，而且很多都有免費預覽版，也是 iPad 必備軟體之一！&lt;/p&gt;
&lt;p&gt;呼，寫到最後發現越寫越短 囧，不過後面的東西一開始本來不打算寫的 :P，精華都在前面就講完了，有任何想法非常歡迎留言給我，感謝！以上。&lt;/p&gt;</content:encoded><category>Tech</category></item><item><title>Flash Builder 4.7 iOS Debug</title><link>https://blog.amowu.com/posts/2013-01-28-flash-builder-ios-debug-scout/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2013-01-28-flash-builder-ios-debug-scout/</guid><description>這個月剛好玩到 flash on iOS，在部署到 iPad 時碰到些問題，筆記一下最後解決的步驟。</description><pubDate>Mon, 28 Jan 2013 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;這個月剛好玩到 flash on iOS，在部署到 iPad 時碰到些問題，筆記一下最後解決的步驟。&lt;/p&gt;
&lt;p&gt;Flash Builder 4.7 提供三種方式部署和測試你的程式:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;On AIR Simulator&lt;/li&gt;
&lt;li&gt;On iOS Simulator&lt;/li&gt;
&lt;li&gt;On device&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;開發環境:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Mac OSX 10.8.2&lt;/li&gt;
&lt;li&gt;XCode 4.5.2&lt;/li&gt;
&lt;li&gt;Flash Builder 4.7 Premium&lt;/li&gt;
&lt;li&gt;Adobe AIR SDK 3.5&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;STEP:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;先開啟 Xcode 確定有更新到最新版，否則模擬器會有問題。&lt;/li&gt;
&lt;li&gt;到&lt;strong&gt;美國&lt;/strong&gt;Adobe官網下載安裝 &lt;a href=&quot;http://www.adobe.com/products/flash-builder.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Flash Builder 4.7&lt;/a&gt;，可試用兩個月。&lt;/li&gt;
&lt;li&gt;前往 /Applications/Flash Builder 4.7/sdk/ 底下。&lt;/li&gt;
&lt;li&gt;複製一份 4.6.0 的資料夾命名為 4.6.0_AIR35。&lt;/li&gt;
&lt;li&gt;下載最新 &lt;a href=&quot;http://www.adobe.com/devnet/air/air-sdk-download.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;AIR SDK&lt;/a&gt; 到剛建立的資料夾。&lt;/li&gt;
&lt;li&gt;開啟 Terminal&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cd /Applications/Flash Builder 4.7/sdk/4.6.0_AIR35/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sudo tar -jxvf &amp;lt;下載的AIR SDK&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;準備好 Apple Developer 的 .p12 和 .mobileprovision。&lt;/li&gt;
&lt;li&gt;Debug your app!!&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;最後發現 Adobe 還提供一個免費又強力的效能檢測工具 &lt;a href=&quot;http://gaming.adobe.com/technologies/scout/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Adobe Scout&lt;/a&gt;。
 不過一開始碰到不少問題，Flex 專案沒辦法跟 Scout 連上，最後 Google 到的解決方法:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;確定你有安裝 python 的環境。&lt;/li&gt;
&lt;li&gt;下載 &lt;a href=&quot;https://github.com/adobe/telemetry-utils&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;telemetry-utils&lt;/a&gt;。&lt;/li&gt;
&lt;li&gt;開啟 Terminal cd 到 telemetry-utils 資料夾底下。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;add-opt-in.py &amp;lt;YOUR FLEX bin .swf file&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;然後再 Run 就可以看到 Scout 的結果了。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;缺點是每次重新 Build 的時候就失效了，需要再下一次指令，期待之後會改善。&lt;/p&gt;</content:encoded><category>Tech</category></item><item><title>AS3 Class Enumeration</title><link>https://blog.amowu.com/posts/2012-10-03-as3-class-enumeration/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2012-10-03-as3-class-enumeration/</guid><description>筆記筆記..</description><pubDate>Wed, 03 Oct 2012 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;筆記筆記..&lt;/p&gt;
&lt;p&gt;一般在 AS3 要列舉 Object 的 properties 時，通常會用下面這種寫法：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;//Get an XML description of this class&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;//and return the variable types as XMLList with e4x&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; varList&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;XMLList&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; flash.utils.&lt;/span&gt;&lt;span&gt;describeType&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;VO&lt;/span&gt;&lt;span&gt;)..variable;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;for&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; i&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt;; i &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt; varList.&lt;/span&gt;&lt;span&gt;length&lt;/span&gt;&lt;span&gt;(); i&lt;/span&gt;&lt;span&gt;++&lt;/span&gt;&lt;span&gt;){&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;//Show the name and the value&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;trace&lt;/span&gt;&lt;span&gt;(varList[i].@name&lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;&apos;:&apos;&lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;VO&lt;/span&gt;&lt;span&gt;[varList[i].@name]);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;不過在處理 VO（Value Object）時則會出問題，因為它是 Class，後來在這篇&lt;a href=&quot;http://www.learnosity.com/techblog/index.cfm/2008/2/6/AS3--Looping-over-properties-of-a-class&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;文章&lt;/a&gt;找到解法：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;for&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; key&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;object&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;trace&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;key&lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;&apos;:&apos;&lt;/span&gt;&lt;span&gt;+ &lt;/span&gt;&lt;span&gt;object&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;key&lt;/span&gt;&lt;span&gt;]);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;</content:encoded><category>Tech</category></item><item><title>讀書心得 半路叛逃</title><link>https://blog.amowu.com/posts/2012-10-03-monkey-potion/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2012-10-03-monkey-potion/</guid><description>要在台灣看到遊戲開發的相關書籍其實不容易，其中獨立遊戲開發者的心路歷程更是少之又少。</description><pubDate>Wed, 03 Oct 2012 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;要在台灣看到遊戲開發的相關書籍其實不容易，其中獨立遊戲開發者的心路歷程更是少之又少。&lt;/p&gt;
&lt;p&gt;難得最近書店上出現了一本程式出身的遊戲制作人 半路(&lt;a href=&quot;http://blog.monkeypotion.net/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;猴子靈藥&lt;/a&gt;站長)大 的甘苦談，二話不說就買回家看了~&lt;/p&gt;
&lt;p&gt;從小就喜歡玩遊戲的我，一直認為遊戲制作人是一個很夢幻的工作，如果我會一直待在遊戲業的話，也希望未來能成為一位獨立遊戲開發者，做自己想做的遊戲！&lt;/p&gt;
&lt;p&gt;獨立遊戲開發者 最近還蠻受注目的，尤其前陣子 &lt;a href=&quot;http://buy.indiegamethemovie.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Indie Game: The Movie&lt;/a&gt; 這部紀錄片的播出，真的令人熱血沸騰。&lt;/p&gt;
&lt;p&gt;花了兩天中秋假期，把這本《&lt;a href=&quot;https://goo.gl/Mf32x&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;半路叛逃 App遊戲制作人的1000告白&lt;/a&gt;》啃完，收獲良多，因此上來寫一篇簡單的書摘。&lt;/p&gt;
&lt;p&gt;“1980年代前是經歷過經濟成長起飛的父母長輩，1980後是成長於物質與資訊不虞匱乏的我們”&lt;/p&gt;
&lt;p&gt;上一代眼裡認為我們是早熟的一群，因為一切的成長來得太快，但其實隨著學歷要求提高，新鮮人起薪越來越低的情況，延後了我們踏入社會的時間，事實上心理狀態相對起來是晚熟的。&lt;/p&gt;
&lt;p&gt;這點台灣跟日本很像，反觀大陸和韓國比較像前者，他們還在衝，還在拼，所以無論是政府還是我們真的都必須在加把勁努力。&lt;/p&gt;
&lt;p&gt;其它像30歲焦慮症，工作責任制等…半路在書中也有一些見解。&lt;/p&gt;
&lt;p&gt;“早期國產單機遊戲的輝煌年代不在，如今的遊戲業如同製造業工廠般，都在把寶貴的研發能量投注於尋求「可複製的成功模式」，將遊戲「產線化」”。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;為什麼台灣做不出百萬銷量的遊戲呢?&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;只鎖定台灣市場，三國，武俠，博弈，這些都是用到不能再爛的老梗了，別白白浪費iOS，Android 平台為我們開發者鋪好的通路，全球超過一億台智慧裝置的使用者在等著你，“世界從來沒有離我們這麼近過！”&lt;/li&gt;
&lt;li&gt;只想複製別人的成功模式，緊盯著排行版前10名，然後致敬一款。&lt;/li&gt;
&lt;li&gt;以為做App是低成本的事，魔鬼藏在細節，這裡有一句話我很喜歡，“會寫程式跟畫圖的人滿街都是，真正能夠做到磨亮細節的才是人才”，學習了。&lt;/li&gt;
&lt;li&gt;想以量取勝，因為現在的遊戲價格低廉，為了賺錢以數量取勝，亂槍打鳥而失去了品質，其中提到一個概念，「最小可行產品」，不過這在製作遊戲原型可行，用在市場會有問題。&lt;/li&gt;
&lt;li&gt;只是大遊戲的濃縮版，應該回到最初玩遊戲的感動，而不是將大團隊的開發方式原封不動的搬過來。&lt;/li&gt;
&lt;li&gt;工廠化思維，只想找低風險的遊戲做。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;為什麼你不該離職創業搞App&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;以為日常習慣不需要改變，需要過著沒有收入的苦日子，必須放棄過去奢侈的享受。&lt;/li&gt;
&lt;li&gt;以為擁有百萬價值的創意，遊戲業中最不缺的就是「創意力」，還需要加上「執行力」才行。&lt;/li&gt;
&lt;li&gt;以為可以全部自己來，創業最需要也最重要的就是伙伴，“球 不是一個人就能踢底”。&lt;/li&gt;
&lt;li&gt;以為只要專心做出遊戲就好，在小團隊中，「通才型」的伙伴會勝過「專才型」。&lt;/li&gt;
&lt;li&gt;以為可以第一款遊戲就賺錢，別一開始就想做出你夢想中的遊戲。&lt;/li&gt;
&lt;li&gt;以為可以享受自由的生活，還是需要維持規律的日常生活，才是開發遊戲的長久之計。&lt;/li&gt;
&lt;li&gt;以為一個人獨處很簡單，“你需要專心，學會一個人獨處，至少不討厭和自己在一起”，離開喧囂的辦公室，沒有同事和你打屁聊八卦，獨處時會有很多幫倒忙的誘惑影響你，沒有自我規範的話很容易迷失。&lt;/li&gt;
&lt;li&gt;以為不需要家人的支持，當你身處低潮失落狀態時，只有家人的愛能幫助你度過難關。&lt;/li&gt;
&lt;li&gt;以為可以跟隨成功者的腳步，當遊戲App流行熱潮退去後，你是否會後悔當初的選擇？&lt;/li&gt;
&lt;li&gt;以為創業就可以安心地做自己，需要參與社群，認識其他同伴，聆聽他們的經驗與忠告。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;書上有提到 Scrum，主要聊了像為什麼一個工作的週期要以2週到4週，以及每日早上立會的原因，對想瞭解敏捷式開發的人有很大的幫助。&lt;/p&gt;
&lt;p&gt;創業最難的就是找到好的伙伴，半路在一開始尋覓團隊成員時碰壁，他將原因歸納為三點：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;他們擁有平順穩定的正職工作。&lt;/li&gt;
&lt;li&gt;我的王道不等於他們的王道。&lt;/li&gt;
&lt;li&gt;彼此缺乏合作動機。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;“當你想獨立創業，沒有資金，沒有作品，沒有經歷時，要如何說服伙伴加入你的團隊呢？”&lt;/p&gt;
&lt;p&gt;“將遊戲完整開發完成只是上半場，行銷才是決定勝敗的下半場”&lt;/p&gt;
&lt;p&gt;App Store 「審核通過日」不應該設定為「上架販售日」。&lt;/p&gt;
&lt;p&gt;開戰前四大法寶：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一定要拍遊戲影片，最合理的預告片長度為1分鐘左右。&lt;/li&gt;
&lt;li&gt;必須要有官方網站，即使再小型的作品，也應該為它申請一個獨立的.com網址。&lt;/li&gt;
&lt;li&gt;遊戲圖示第一重要，截圖第二。&lt;/li&gt;
&lt;li&gt;發售前的宣傳準備。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;每款 App 都擁有50組「推廣代碼」，需要好好利用。&lt;/p&gt;
&lt;p&gt;第一次遊戲世界大戰：單機 v.s. MMORPG&lt;/p&gt;
&lt;p&gt;第二次遊戲世界大戰，三大勢力：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;網頁遊戲，Flash &amp;amp; HTML5&lt;/li&gt;
&lt;li&gt;社交遊戲，Facebook&lt;/li&gt;
&lt;li&gt;手機遊戲，iOS &amp;amp; Android&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;目前 App 主要獲利方式：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;高定價，$6.99以上，國際級遊戲公司機會較大。&lt;/li&gt;
&lt;li&gt;中定價，&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;2.99到2.99到&lt;/span&gt;&lt;span class=&quot;katex-html&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;2.99&lt;/span&gt;&lt;span class=&quot;mord cjk_fallback&quot;&gt;到&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;4.99，適合較具知名度的小型團隊或獨立開發者。&lt;/li&gt;
&lt;li&gt;低定價，$0.99，已是一片紅海，需要天時地利人和。&lt;/li&gt;
&lt;li&gt;免費，內置廣告，需要龐大的下載數與每日活躍用戶。&lt;/li&gt;
&lt;li&gt;免費，內置商城，平衡設計難度頗高。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;遊戲玩家心理學 「巴托爾測試」將玩家分為四種類型：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;成就者，喜歡升級衝裝。&lt;/li&gt;
&lt;li&gt;社交者，喜歡組隊聊天。&lt;/li&gt;
&lt;li&gt;殺手，喜歡工會戰爭。&lt;/li&gt;
&lt;li&gt;探險者，喜歡探險遊戲世界，也是目前大部份遊戲最缺的類型。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;結語：&lt;/p&gt;
&lt;p&gt;在遊戲進入網路世代後，因不時出現青少年鬥毆，詐騙，色情暴力廣告等因素，導致社會大眾普遍對遊戲的負面觀只有增而無減。&lt;/p&gt;
&lt;p&gt;身為遊戲開發者的我們應該避免這樣的事情持續發生，玩遊戲明明是一件很快樂的事情！&lt;/p&gt;
&lt;p&gt;「我已經決定這輩子要成為遊戲設計師」，很佩服半路大的這份堅定決心，這句話目前的我還沒有勇氣說出口，也許還要個兩三年，也許還要個十年，也許一輩子都沒機會，但能確定的是，做遊戲也是一件很快樂的事情！&lt;/p&gt;
&lt;p&gt;“你有多久沒有用盡一切力氣，不計成敗的去做一件事情了呢？”&lt;/p&gt;</content:encoded><category>Tech</category></item><item><title>COSCUP 2012</title><link>https://blog.amowu.com/posts/2012-08-19-coscup-2012/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2012-08-19-coscup-2012/</guid><description>今年很幸運的有搶到入場資格，這也是小弟第一次參加 COSCUP，趁現在剛結束滿腔熱血的狀態下來寫篇心得文。</description><pubDate>Sun, 19 Aug 2012 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;今年很幸運的有搶到入場資格，這也是小弟第一次參加 &lt;a href=&quot;http://coscup.org/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;COSCUP&lt;/a&gt;，趁現在剛結束滿腔熱血的狀態下來寫篇心得文。&lt;/p&gt;
&lt;p&gt;今年 COSCUP 一共有兩天的行程，在中研院人文館舉辦，每個時段都有 3~4 場的分享，不過常常碰到某個時段三場都想聽，也常常碰到某個時段的題目都沒興趣…orz&lt;/p&gt;
&lt;h2 id=&quot;day1&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#day1&quot;&gt;Day1&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;第一天忘了做筆記，只好憑記憶跟拿些圖來湊一湊&lt;/p&gt;
&lt;p&gt;早上第一場是微軟傳教士分享的 Windows Azure感覺還不錯，而且有繁體中文的介面。&lt;/p&gt;
&lt;p&gt;第二場我去聽了 Fred 的 Nodee.js OS，不過我覺得他自己改的 Linux 比較炫。&lt;/p&gt;
&lt;p&gt;第三場請到 Penk 大大來 demo 他的平板 UI，印象深刻的一句話是&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;科技業是唯一比女性時尚還追求時尚的行業&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我的解讀是幹這行每天都在學新技術 囧..&lt;/p&gt;
&lt;p&gt;第四場超讚，講者 demo 他用 javascript 寫的超任模擬器，眼淚都快飆出來了，而且老師很幽默:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;目前國外有人正在研究如何用 javascript 寫出一個超任模擬器，如果他成功，那會是全世界第三個，因為前面兩個被我寫出來了XDDD&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;接下來的下午場雖然都有去聽，可是比較沒有什麼印象的點，只記得最後閃電秀的大大們XD，你們實在太讚了!&lt;/p&gt;
&lt;h2 id=&quot;day2&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#day2&quot;&gt;Day2&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;難得有位置，明明很晚到…&lt;/p&gt;
&lt;p&gt;第一場請到廣達的Ted 先生，本來以為會很無聊，但從頭聽到尾，學到蠻多東西的:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;研發的目的 1.不想被外界surprise 2.想給外界surprise&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;不相信外星人就無法創造飛行車&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;要把複雜的東西變簡單其實是最不簡單的&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;設計一個你最討厭的東西 試著把它做好&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;第二場去聽 Tool Objects in jQuery，主要重點:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;用 Queue 取代巢狀 Callback&lt;/li&gt;
&lt;li&gt;Deferred 可以看做是 Call by future&lt;/li&gt;
&lt;li&gt;$.when 可以用來管理複數非同步的結果&lt;/li&gt;
&lt;li&gt;pipe 跟 when 的關係可以看作串聯與並聯&lt;/li&gt;
&lt;li&gt;一些 Callback 常用的 function: add fire once memory unique stopOnFalse differed&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;第三場的場次是我最掙扎的，有Heroku，有Emacs，有Twitter Bootstrap，還有手機控制家電，都是我好想聽的主題呀呀呀呀，最後選了Bootstrap，因為最近想玩它，主要重點:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Grid system&lt;/li&gt;
&lt;li&gt;nav bar fixed top inner&lt;/li&gt;
&lt;li&gt;Button group dropdown&lt;/li&gt;
&lt;li&gt;tab nav list&lt;/li&gt;
&lt;li&gt;Label badge pull right&lt;/li&gt;
&lt;li&gt;Modal&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;第四場聽了 javascriptMVC，雖然我對 Backbone.js 比較有興趣，不過還是有幾個有趣的點:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CSS sass less&lt;/li&gt;
&lt;li&gt;Data CRUD (create read update delete)&lt;/li&gt;
&lt;li&gt;View embeddedjs&lt;/li&gt;
&lt;li&gt;StealJs&lt;/li&gt;
&lt;li&gt;fixture&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;第五場聽 HTC 的 CI&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Jenkins CI&lt;/li&gt;
&lt;li&gt;Testopia&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;第六場聽 Yahoo 的 Hadoop&lt;/p&gt;
&lt;p&gt;第七場聽 Livescript (想聽Coffee script)&lt;/p&gt;
&lt;p&gt;第八場比較振作了，聽了 HTML5 on Windows8，好想把 &lt;a href=&quot;https://goo.gl/Sbfhy&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Gu Morning&lt;/a&gt; 上架到 Windows store T^T&lt;/p&gt;
&lt;p&gt;最後閃電秀一樣沒讓人失望，笑料滿出XD，愉快的兩天一切都要感謝辛苦的 COSCUP 人員及贊助廠商，期待明年!&lt;/p&gt;</content:encoded><category>Tech</category></item><item><title>Development Tools</title><link>https://blog.amowu.com/posts/2012-08-02-development-tools/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2012-08-02-development-tools/</guid><description>不知不覺，這個月的文章還沒想好要寫什麼，七月就過了…</description><pubDate>Thu, 02 Aug 2012 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;不知不覺，這個月的文章還沒想好要寫什麼，七月就過了…&lt;/p&gt;
&lt;p&gt;這一篇我打算分享一些最近在關注的玩意兒。&lt;/p&gt;
&lt;h2 id=&quot;trello&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#trello&quot;&gt;&lt;/a&gt;&lt;a href=&quot;https://trello.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Trello&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Trello 是 Joel 團隊開發，一個專案管理的工具，當然，他是雲端網頁，所以在任何地方都可以掌握團隊的專案進度。&lt;/p&gt;
&lt;p&gt;Trello 可以使用 Google 的帳號登入，登入後可以根據專案的開發流程自訂專屬的 Board ，每個 Board 內可以貼上各個項目的 Card，並且可以和專案成員即時協作。&lt;/p&gt;
&lt;p&gt;自己本身大概用過兩三套這種 PMS 的產品，Trello 簡單易用，一個人獨立開發使用也很方便，算是敏捷式開發上不可或缺的好工具。&lt;/p&gt;
&lt;p&gt;另外，Trello 也有提供 iPhone 的版本：&lt;/p&gt;
&lt;h2 id=&quot;sublime&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#sublime&quot;&gt;&lt;/a&gt;&lt;a href=&quot;http://www.sublimetext.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Sublime&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Sublime 是一個很棒的程式碼編輯器，前一陣子在這篇 &lt;a href=&quot;http://tutorialzine.com/2012/07/battle-of-the-tools-which-is-the-best-code-editor/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Which is the Best Code Editor?&lt;/a&gt; 得到很高的分數。&lt;/p&gt;
&lt;p&gt;我用過之後也是愛不釋手，漂亮的配色，安裝容易的眾多外掛，不錯的開啟速度，總而言之，大力推薦!!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://www.qianduan.net/essential-to-sublime-the-text-2-plugins.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;一些必不可少的Sublime Text 2插件&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://lucifr.com/139235/sublime-text-2-useful-shortcuts/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Sublime Text 2 实用快捷键&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;cloud9-ide&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#cloud9-ide&quot;&gt;&lt;/a&gt;&lt;a href=&quot;https://c9.io/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Cloud9 IDE&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;C9 也是一個寫程式的好工具，可是跟 Sublime 不同的地方，它是雲端網頁版的編輯器。&lt;/p&gt;
&lt;p&gt;C9 支援的語言也不少，甚至可以直接跑 Node.js ，並且也支援 Github 的 Repository clone。&lt;/p&gt;
&lt;p&gt;不過這幾天用下來發現速度不理想，常常會進入 Offline 狀態。&lt;/p&gt;
&lt;h2 id=&quot;heroku&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#heroku&quot;&gt;&lt;/a&gt;&lt;a href=&quot;http://www.heroku.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Heroku&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Heroku 是一個雲端應用的 host 平台，支援的語言有 PHP Ruby Python Node.js。&lt;/p&gt;
&lt;p&gt;Heroku 和 Facebook 合作，讓開發者開發 Facebook 應用程式的時候可以直接連結 Heroku 的空間，並且支援 Git ，目前初步試用下來感覺很不錯。&lt;/p&gt;
&lt;h2 id=&quot;travis-ci&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#travis-ci&quot;&gt;&lt;/a&gt;&lt;a href=&quot;http://travis-ci.org/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Travis CI&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Travis CI，顧名思義，是提供持續整合(Continuous Integration)和每日建構的服務。&lt;/p&gt;
&lt;p&gt;不過目前還沒機會使用，所以還不是很清楚它的詳細內容。&lt;/p&gt;
&lt;p&gt;Travis CI 可以結合 Github，讓你隨時知道專案的測試狀況。&lt;/p&gt;</content:encoded><category>Tech</category></item><item><title>讀書心得 約耳趣談軟體</title><link>https://blog.amowu.com/posts/2012-06-12-joel-on-software/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2012-06-12-joel-on-software/</guid><description>以下是節錄一些這本書覺得還不錯的點</description><pubDate>Tue, 12 Jun 2012 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;以下是節錄一些這本書覺得還不錯的點&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;激勵是有害的，主要是說考績制度對程式設計師是不通的&lt;/li&gt;
&lt;li&gt;工作切換有害無益，讓程式設計師只專注一件事情&lt;/li&gt;
&lt;li&gt;絕對不能把程式碼重寫（這裡不是指重構）&lt;/li&gt;
&lt;li&gt;冰山一角理論，冰山有 90% 是在水面下，大部分的軟體，那些漂亮的使用者介面通常只占 10% 的工作，而背後 90% 的程式設是看不到的，如果再考慮一半時間都在抓蟲，那使用者介面就只剩 5%，如果只計算介面中的視覺部分，那客戶真正看到的，只有 1%……，這並不是秘密，真正的秘密是非程式人員根本不知道這件事……&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;約耳測試：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;你有使用原始碼控制系統嗎?？
SVN、CVS、Git、Mercurial…&lt;/li&gt;
&lt;li&gt;你能用一個步驟建出所有結果嗎？
準備一個 Script 擋，只要執行這個腳本，就能一次搞定從最新原始碼快照到自動建立釋出產品的過程&lt;/li&gt;
&lt;li&gt;你有進行每日編譯嗎？
提交原始碼到版本控制系統前，一定要編譯並且沒有出現錯誤，因為別人也想下班&lt;/li&gt;
&lt;li&gt;你有沒有問題資料庫？
記錄已知的 Bug 清單，每筆 Bug 需記錄：&lt;/li&gt;
&lt;li&gt;重現問題的完整步驟&lt;/li&gt;
&lt;li&gt;應該看到的結果&lt;/li&gt;
&lt;li&gt;實際看到的結果&lt;/li&gt;
&lt;li&gt;被指派的負責人&lt;/li&gt;
&lt;li&gt;你會先把問題都修好之後，才寫新的程式嗎？
愈晚修正問題，之後付出的代價成本愈高&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;你有一份最新的時程表嗎？
程式設計師討厭排時程，但牽扯到業務人員的決策規劃，擁有時程可以強迫自己決定要作哪些功能，並剔除不重要的功能，以避免過度膨脹&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;使用一些 PMS 工具&lt;/li&gt;
&lt;li&gt;時程表簡單就好&lt;/li&gt;
&lt;li&gt;每個功能應該包含多項任務（Task）&lt;/li&gt;
&lt;li&gt;只有實際要寫該程式的程式設計人員,才能排出該項目的時程&lt;/li&gt;
&lt;li&gt;要把任務分的很細（以小時為單位）&lt;/li&gt;
&lt;li&gt;紀錄最初和目前的估計&lt;/li&gt;
&lt;li&gt;每天更新已消耗時間&lt;/li&gt;
&lt;li&gt;把休假時間算進去&lt;/li&gt;
&lt;li&gt;把除錯時間算進去&lt;/li&gt;
&lt;li&gt;把整合時間算進去&lt;/li&gt;
&lt;li&gt;把緩衝時間算進去&lt;/li&gt;
&lt;li&gt;絕對不讓 PM 縮短估計時間&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;你有寫規格嗎？例如：GDD、TDD
又是一件程式設計師討厭的事情，設計初期的階段還看不出來，愈後期程式碼一多修正的代價就愈高，應貫徹沒有規格就不寫程式的原則&lt;/li&gt;
&lt;li&gt;程式設計人員有沒有安靜的工作環境？
需要讓程式設計師進入沈浸狀態（in the zone），因為這時候是最能全神貫注，生產力最高的狀態，所以，要有安靜的環境！！！&lt;/li&gt;
&lt;li&gt;你有沒有用市面上最好的工具？
你需要兩個以上的螢幕，以及編譯程式不會讓你抓狂的電腦配備&lt;/li&gt;
&lt;li&gt;你有沒有測試人員？
省下測試人員的錢並不是真正的節省，因為你會付出更慘痛的代價，這裡本書第 22 章有非常有趣的見解&lt;/li&gt;
&lt;li&gt;是否在面試時要求面試的對象試寫程式？
你會不請魔術師表演幾招就直接僱用他嗎，當然不會&lt;/li&gt;
&lt;li&gt;是否進行過走廊使用性測試？
隨機找幾位使用者試用你的產品，他們可以幫你發現程式中 95% 應該注意到的使用性問題&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;約耳認為軟體開發有各種不同的領域，他把它分為五個世界：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;熱縮封膜軟體（Shrinkwrap）
簡單來說就是一般常看到的軟體，商業軟體，共享軟體或開放源碼軟體等，特色就是使用者眾多，通常都有替代商品，所以開發者必須把東西做得更簡單易用才行&lt;/li&gt;
&lt;li&gt;內部用軟體
這類軟體通常是公司針對特定狀況下能執行就可以了，因此開發起來較容易，但開發者較不容易從中得到成就&lt;/li&gt;
&lt;li&gt;嵌入式軟體
這種軟體的特性是被放在硬體裡，且幾乎無法更新，因此品質要求會比平常高出許多&lt;/li&gt;
&lt;li&gt;拋棄式軟體
為了產生其他東西而暫時創造的軟體，當達到目的後就不會用到了，例如格式轉檔軟體&lt;/li&gt;
&lt;li&gt;遊戲軟體
遊戲開發的經濟學是打擊型，有些遊戲擊出安打，但更多的是三振，要賺大錢必須用很多的遊戲來平衡失敗的損失，和嵌入式軟體類似，版本修正代價大，所以品質要求高&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;紙上原型製作的重要性&lt;/li&gt;
&lt;li&gt;別花太多時間想抽象的架構問題，邊開火邊移動！！！&lt;/li&gt;
&lt;li&gt;寫程式並不是量產，也不是工匠技藝，它是一種設計&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;約耳認為面試人員應該問的問題：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;簡介&lt;/li&gt;
&lt;li&gt;最近專案的問題
尋找熱情
好的人選會仔細地把事情由各個層面解釋清楚
如果專案是由多個人一齊負責, 尋找擔任領導角色的跡象&lt;/li&gt;
&lt;li&gt;不可能的問題&lt;/li&gt;
&lt;li&gt;程式問題&lt;/li&gt;
&lt;li&gt;你有什麼問題嗎?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;小員工也能做大事，這本書是關於軟體管理，不過有時候你並沒有那個權力去改變組織，如果你只是圖騰柱底層的一位程式設計人員，約耳給你一些建議：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;去做就是了
沒有每日編譯伺服器嗎？自己架一台就對了&lt;/li&gt;
&lt;li&gt;利用病毒性行銷的威力
團隊沒有在用版本管理系統？
自己先用吧，等問題發生之後，他們就會來找你了&lt;/li&gt;
&lt;li&gt;建立一個卓越圈
找一些支持你的人一起改善吧&lt;/li&gt;
&lt;li&gt;讓笨蛋無害
有時候會有天才花兩個星期寫出一點點程式，而且爛到不可思議，這時候你會想花 15 分鐘把這段程式重新寫過，請忍住，因為這是把這個白癡拖住幾個月的機會，這段期間他不會再造成其他地方傷害了&lt;/li&gt;
&lt;li&gt;遠離干擾環境&lt;/li&gt;
&lt;li&gt;提升自己的價值&lt;/li&gt;
&lt;/ol&gt;</content:encoded><category>Tech</category></item><item><title>App Center Tutorial</title><link>https://blog.amowu.com/posts/2012-06-03-app-center-tutorial/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2012-06-03-app-center-tutorial/</guid><description>本篇翻譯自 Facebook 開發者頁面 的 App Center Tutorial</description><pubDate>Sun, 03 Jun 2012 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;本篇翻譯自 &lt;a href=&quot;https://developers.facebook.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Facebook 開發者頁面&lt;/a&gt; 的 &lt;a href=&quot;https://developers.facebook.com/docs/guides/appcenter/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;App Center Tutorial&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Facebook App Center 是一個大型的社交應用程式中心。App Center 是協助你的 Facebook Apps、 手機 apps 和使用 Facebook Login 的網站 的管道。Facebook 鼓勵所有開發人員在這個 App Center 建立您的 app 細節內容頁面。&lt;/p&gt;
&lt;p&gt;這篇文章將引導開發者配置 apps 的過程：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;建立一個 App 細節頁面&lt;/li&gt;
&lt;li&gt;上傳圖片&lt;/li&gt;
&lt;li&gt;網站和手機網頁的特別注意事項&lt;/li&gt;
&lt;li&gt;提交您的 app 到 App Center&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;如果你還沒有在 Facebook 平台上建立一個應用程序，你應該參考 &lt;a href=&quot;https://developers.facebook.com/docs/guides/canvas/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Apps on Facebook&lt;/a&gt;， &lt;a href=&quot;https://developers.facebook.com/docs/guides/mobile/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Facebook Mobile&lt;/a&gt; 或 &lt;a href=&quot;https://developers.facebook.com/docs/guides/web/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Facebook for Websites&lt;/a&gt; 開發指南。&lt;/p&gt;
&lt;p&gt;本篇假定您已經熟悉 &lt;a href=&quot;https://developers.facebook.com/apps&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Facebook App Dashboard&lt;/a&gt; 的操作及基本設置，並且已閱讀 &lt;a href=&quot;https://developers.facebook.com/docs/appcenter/guidelines/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;App Center Guidelines&lt;/a&gt; 。以確保 App Center 審查上市後，用戶在使用上有一個良好的體驗。&lt;/p&gt;
&lt;h2 id=&quot;建立一個-app細節頁面&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#建立一個-app細節頁面&quot;&gt;建立一個 App 細節頁面&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;所有的開發人員應在 &lt;a href=&quot;https://developers.facebook.com/apps&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;App Dashboard&lt;/a&gt; 創建一個應用程式的詳細頁面。使用者可以經由這個頁面的資訊和截圖了解這個應用程式，然後開始登入使用。使用者通過認證之後，應用程式就會發送到他們的 Facebook 、網站或手機上。&lt;/p&gt;
&lt;p&gt;要在 App Center 上架，App 細節頁面是必須的。這也將成為非 Facebook 用戶搜索時的目標頁面。&lt;/p&gt;
&lt;p&gt;要訪問 App Center 的設置，到 &lt;a href=&quot;https://developers.facebook.com/apps&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;App Dashboard&lt;/a&gt; 單擊在左側的 App Center 選項。&lt;/p&gt;
&lt;p&gt;在這裡你可以輸入顯示在 App Center 應用程式的詳細訊息。包括應用程式的顯示名稱， &lt;a href=&quot;https://developers.facebook.com/docs/appcenter/categories/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;類別&lt;/a&gt; ，副標題和說明文字等等..。&lt;/p&gt;
&lt;p&gt;請確保您的應用程式所支援的平台顯示。如果您正在測試 iOS 應用程式，且與您的 Facebook應用程式是相同的 App ID，但它仍然在開發中，你應該在細節頁面中隱藏 iOS 的平台配置。&lt;/p&gt;
&lt;p&gt;註：如果您的網站支援 &lt;a href=&quot;https://developers.facebook.com/docs/guides/web/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Facebook Login&lt;/a&gt; 並且同時擁有 &lt;a href=&quot;https://developers.facebook.com/docs/guides/canvas/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Facebook App&lt;/a&gt;，那麼只需要顯示 &lt;a href=&quot;https://developers.facebook.com/docs/guides/canvas/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Facebook App&lt;/a&gt; 在 App Center 即可，以減少用戶混淆。&lt;/p&gt;
&lt;p&gt;最後，如果您之前尚未在設置協助訊息。則需要輸入 &lt;a href=&quot;https://www.facebook.com/about/privacy/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;隱私政策的URL&lt;/a&gt;， &lt;a href=&quot;https://www.facebook.com/legal/terms&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;服務條款URL&lt;/a&gt; 和用戶支援電子郵件地址或URL。&lt;/p&gt;
&lt;h2 id=&quot;上傳圖片&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#上傳圖片&quot;&gt;上傳圖片&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;開發人員需要上傳圖示(icons)，橫幅(banners)和遊戲畫面(screenshots)。這些隨應用程式支持的平台可能會有所不同。例如，在 iOS 應用程式將需要上傳。圖片選項將依據適當的平台自動顯示在 App Center 選項中。&lt;/p&gt;
&lt;p&gt;當您設計圖片時：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;將您的應用程式名稱放置於橫福中。&lt;/li&gt;
&lt;li&gt;預留一些空間給重要的圖形。&lt;/li&gt;
&lt;li&gt;不要在圖片的邊界放置 Logo 和文字。&lt;/li&gt;
&lt;li&gt;橫幅圖片必須填滿整個框架（例如，沒有空格，沒有圓角，無邊框）。&lt;/li&gt;
&lt;li&gt;圖示(Icons)可以有圓角（建議使用帶透明度和PNG，不要GIF）。&lt;/li&gt;
&lt;li&gt;請參閱 &lt;a href=&quot;https://developers.facebook.com/docs/appcenter/guidelines/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;App Center Guidelines&lt;/a&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;App Center 支持以下的手機螢幕解析度：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;MDPI：240×480&lt;/li&gt;
&lt;li&gt;HDPI：480×800&lt;/li&gt;
&lt;li&gt;XHDPI：640×960以上&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;封面圖片：&lt;/p&gt;
&lt;p&gt;設計封面圖片時，開發人員應該注意，128x128 圖示會稍微掩蓋住封面圖像的左下方。&lt;/p&gt;
&lt;p&gt;遊戲畫面：&lt;/p&gt;
&lt;p&gt;開發人員可以提交每個平台5張截圖的（Web，Canvas，手機 Web，iPhone，iPad，Android），至少須提交3個平台的遊戲截圖。&lt;/p&gt;
&lt;p&gt;內容必須顯示真正的遊戲畫面，不能包含任何推銷資訊或橫幅。App Center 支持的截圖尺寸為 320×320 到 2048×2048 pixel 之間。&lt;/p&gt;
&lt;p&gt;如果截圖的長寬比不適合在 App Center 的顯示框的長寬比，我們將自行縮放截圖。&lt;/p&gt;
&lt;p&gt;畫面的選擇，根據用戶使用的裝置來查看頁面。如果您在 iPhone 上查看頁面，iPhone 的截圖會優先出現。如果應用程式不支援的 iOS 則手機 Web 的截圖會優先出現等。&lt;/p&gt;
&lt;p&gt;有2個截圖的佈局設計：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一個直擺方向(portrait)+兩個橫擺方向(landscapes)。&lt;/li&gt;
&lt;li&gt;三個 橫擺方向(landscapes)。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;授權authorization&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#授權authorization&quot;&gt;授權(Authorization)&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;在 App Center 的應用程式細節頁面中顯示新用戶所需的權限。可以被視為 &lt;a href=&quot;https://developers.facebook.com/docs/opengraph/authentication/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;驗證對話框(Auth Dialog)&lt;/a&gt; 中的一個版本，因此很容易直接從 App Center 為新用戶安裝你的應用程式。&lt;/p&gt;
&lt;p&gt;在 App Center 下設置**權限(Permissions)**部分時，可以遵循以下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;用戶與好友的權限(User &amp;amp; Friend Permissions)：這些&lt;a href=&quot;http://permissions/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;權限&lt;/a&gt;將顯示在 App Center 的應用程式的細節頁面。&lt;/li&gt;
&lt;li&gt;擴展權限(Extended Permissions)： 用戶通過點擊應用程式的細節頁面後，將出現對話框的提示驗證這些可選的&lt;a href=&quot;https://developers.facebook.com/docs/reference/api/permissions/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;權限&lt;/a&gt;，這意味著用戶可以&lt;strong&gt;取消&lt;/strong&gt;這些權限，然後仍授權您的應用程式。&lt;/li&gt;
&lt;li&gt;Auth Token Parameter：當用戶通過應用程式的細節頁面授權您的應用程式，我們將通過您的應用程式，在這裡指定 Auth Token 的格式。可用的格式：&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;請注意，有些用戶將直接從 App Center 導向到應用程式的細節頁面，其他用戶仍然可以通過直接輸入網址或通過 &lt;a href=&quot;https://developers.facebook.com/docs/reference/dialogs/feed/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Feed&lt;/a&gt;， &lt;a href=&quot;https://developers.facebook.com/docs/requests/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Requests&lt;/a&gt;和 &lt;a href=&quot;https://www.facebook.com/advertising/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;廣告&lt;/a&gt;導到您的應用程。在這些情況下，你仍然需要通過現有的 &lt;a href=&quot;https://developers.facebook.com/docs/opengraph/authentication/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;驗證對話框(Auth Dialog)&lt;/a&gt; 授權處理。&lt;/p&gt;
&lt;h2 id=&quot;canvas-app-的開發者for-canvas-developers&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#canvas-app-的開發者for-canvas-developers&quot;&gt;Canvas App 的開發者(For &lt;/a&gt;&lt;a href=&quot;https://developers.facebook.com/docs/guides/canvas/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Canvas Developers&lt;/a&gt;)&lt;/h2&gt;
&lt;p&gt;大多數 Canvas 應用程式是將新用戶通過客戶端重導向驗證對話框。提交到 App Center，用戶仍然可以通過直接輸入網址瀏覽到應用程式，應仍保持授權的需要功能。開發人員必須確保他們在這兩種情況下的同一組的權限要求; App Center 權限配置應符合開發商已經要求的 &lt;a href=&quot;https://developers.facebook.com/docs/reference/dialogs/oauth/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;OAuth Dialog&lt;/a&gt; 參數。如果這些不匹配，用戶會看到兩種授權在提示，將造成用戶體驗混亂，導致您的應用程式無法在 App Center 上市。&lt;/p&gt;
&lt;h2 id=&quot;ios-與-android-的開發者for-ios-andandroid&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#ios-與-android-的開發者for-ios-andandroid&quot;&gt;iOS 與 Android 的開發者(For &lt;/a&gt;&lt;a href=&quot;https://developers.facebook.com/docs/mobile/ios/build/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;iOS&lt;/a&gt; and &lt;a href=&quot;https://developers.facebook.com/docs/mobile/android/build/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Android&lt;/a&gt;)&lt;/h2&gt;
&lt;p&gt;手機應用程式必須使用單點登錄（SSO）是在 App Center 上市的資格。只提供一個無縫的登入過程後，通過應用程式的細節頁面點擊列出在 App Center 的手機應用程式。&lt;/p&gt;
&lt;p&gt;人們將能夠瀏覽 Facebook.com 的 App Center，和 Facebook 的手機應用。當人們要安裝原生的手機應用程式，他們將直接從 Apple 的 App Store 或 Google Play 下載應用程式。App Center 聯繫手機應用商場，旨在推動 iOS，Android 和手機網路巨大社交應用的成長。&lt;/p&gt;
&lt;h2 id=&quot;網頁與手機網頁的開發者for-website-and-mobile-web-developers&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#網頁與手機網頁的開發者for-website-and-mobile-web-developers&quot;&gt;網頁與手機網頁的開發者(For &lt;/a&gt;&lt;a href=&quot;https://developers.facebook.com/docs/guides/web/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Website&lt;/a&gt; and &lt;a href=&quot;https://developers.facebook.com/docs/guides/mobile/web/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Mobile Web&lt;/a&gt; Developers)&lt;/h2&gt;
&lt;p&gt;在 App Center 的應用程式的細節頁面，將採取授權的用戶才到達您的網站上的方式，但重要的是要確保這些用戶有個人化的體驗經驗，當他們通過從 App Center 點擊您網站上的資訊。用戶不應該點擊第二次 登錄到 Facebook 按鈕，而是應該看到一些個人化的資訊，以優化用戶體驗。&lt;/p&gt;
&lt;p&gt;下面的程式碼範例將檢查用戶是否已授權應用程式和顯示出個人資訊的歡迎訊息，例如 ‘Welcome, Constantin!’.。&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;head&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;&amp;gt;Example&amp;lt;/&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;head&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;     &lt;/span&gt;&lt;span&gt;&amp;lt;!-- Load the Facebook JavaScript SDK --&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;fb-root&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;src&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;//connect.facebook.net/en_US/all.js&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;welcomeMessage&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;text/javascript&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;       &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// Initialize the Facebook JavaScript SDK&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;       &lt;/span&gt;&lt;/span&gt;&lt;span&gt;FB.init({&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;         &lt;/span&gt;&lt;/span&gt;&lt;span&gt;appId: &apos;APP_ID&apos;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;status: true,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;       &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;       &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// Check if the current user is logged in&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;       &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// and has authorized the app&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;       &lt;/span&gt;&lt;/span&gt;&lt;span&gt;FB.getLoginStatus(checkLoginStatus);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;       &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// Check the result of the user&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;       &lt;/span&gt;&lt;/span&gt;&lt;span&gt;function checkLoginStatus(response) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;         &lt;/span&gt;&lt;/span&gt;&lt;span&gt;if(response &lt;/span&gt;&lt;span&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span&gt; response.status == &apos;connected&apos;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;           &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// The user is connected to Facebook&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;           &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// and has authorized the app.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;           &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// Now personalize the user experience&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;           &lt;/span&gt;&lt;/span&gt;&lt;span&gt;FB.api(&apos;/me&apos;, function(response) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;             &lt;/span&gt;&lt;/span&gt;&lt;span&gt;var message = document.getElementById(&apos;welcomeMessage&apos;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;             &lt;/span&gt;&lt;/span&gt;&lt;span&gt;message.innerHTML = &apos;Welcome, &apos; + response.first_name + &apos;!&apos;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;           &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;         &lt;/span&gt;&lt;/span&gt;&lt;span&gt;} else {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;           &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// The user has not authenticated your app,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;           &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// proceed with your normal (anonymous) flow.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;         &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;       &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;更多如何檢測用戶的Facebook狀態可以參考 &lt;a href=&quot;https://developers.facebook.com/docs/reference/javascript/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;JavaScript SDK&lt;/a&gt;， &lt;a href=&quot;https://developers.facebook.com/docs/reference/javascript/FB.getLoginStatus/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;FB.getLoginStatus&lt;/a&gt; 文件，這個 &lt;a href=&quot;https://developers.facebook.com/blog/post/2012/05/08/how-to--improve-the-experience-for-returning-users/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;網誌文章&lt;/a&gt; 的詳細資訊。&lt;/p&gt;
&lt;h2 id=&quot;提交您的-app-到-appcenter&quot;&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#提交您的-app-到-appcenter&quot;&gt;提交您的 app 到 App Center&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;對於一個應用程式在 App Center 上市，必須滿足您的應用程式的 &lt;a href=&quot;https://developers.facebook.com/docs/appcenter/guidelines/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;guidelines&lt;/a&gt; 。Facebook將審查圖片，應用程式的細節訊息，和用戶體驗。如果您的應用程式被拒絕，我們將解釋為什麼並給你一些改變的建議。一旦您修正，可以在 App Center 重新提交上市。&lt;/p&gt;
&lt;p&gt;開發人員可以提交之前預覽他們的應用程序的詳細信息頁面，在頁面頂部點擊”預覽”鏈接。這將顯示一個實時預覽：&lt;/p&gt;
&lt;p&gt;開發人員可以提交他們的應用程式，通過點擊 App Center 的頂上方按鈕。一旦提交將會顯示一條訊息，提交正在等待審查。一旦你的應用程式已經批准，將更新此訊息。&lt;/p&gt;
&lt;p&gt;注意：一旦您提交您的應用程式的細節頁面，設定值不能被改變，直到提交批准或取消提交。一旦你的應用程式的細節頁面在 App Center 上市，頁面上的所有未來的變化也將受到審查。&lt;/p&gt;
&lt;p&gt;這些提交過程是所有應用程式要在 App Center 上市所需的，但所有的應用程式仍可以繼續使用 Facebook 的 API，Bookmarks， Requests， Timeline 和 News Feed。提交到 App Center 不是必需的，但鼓勵大家這麼做，作為應用中心提供了一個獲取新用戶的重要機遇。&lt;/p&gt;</content:encoded><category>Tech</category></item><item><title>How to list the properties of a javascript object</title><link>https://blog.amowu.com/posts/2012-05-19-how-to-list-properties-of-javascript/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2012-05-19-how-to-list-properties-of-javascript/</guid><description>本篇參考 stackoverflow 的 How to list the properties of a javascript object</description><pubDate>Sat, 19 May 2012 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;本篇參考 &lt;a href=&quot;http://stackoverflow.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;stackoverflow&lt;/a&gt; 的 &lt;a href=&quot;http://stackoverflow.com/questions/208016/how-to-list-the-properties-of-a-javascript-object&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;How to list the properties of a javascript object&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;要如何知道 Javascript 物件擁有哪些 proerty?&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; dog &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;name: &lt;/span&gt;&lt;span&gt;&apos;Lucky&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;age: &lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;breeds: &lt;/span&gt;&lt;span&gt;&apos;Shiba Inu&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;上面是一個小狗的物件，裡面有一些自訂的屬性 &lt;code&gt;&apos;name&apos;&lt;/code&gt;, &lt;code&gt;&apos;age&apos;&lt;/code&gt;, &lt;code&gt;&apos;breeds&apos;&lt;/code&gt; 等等…&lt;/p&gt;
&lt;p&gt;那要怎麼在程式中知道這些屬性呢?&lt;/p&gt;
&lt;p&gt;比較新的瀏覽器(IE9, FireFox, Chrome…)可直接使用 &lt;a href=&quot;https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/keys&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Object.keys&lt;/a&gt; 這個方法&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; keys &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; Object.&lt;/span&gt;&lt;span&gt;keys&lt;/span&gt;&lt;span&gt;(dog);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;或者自己寫&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;getKeys&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;obj&lt;/span&gt;&lt;span&gt;){&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;   &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; keys &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; [];&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;   &lt;/span&gt;&lt;span&gt;for&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; key &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; obj){&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;keys.&lt;/span&gt;&lt;span&gt;push&lt;/span&gt;&lt;span&gt;(key);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;   &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; keys;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; keys &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;getKeys&lt;/span&gt;&lt;span&gt;(dog);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;這樣 keys 就會是一個有所有屬性的陣列&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;keys &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;&quot;name&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;age&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;breeds&quot;&lt;/span&gt;&lt;span&gt;];&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;參考文章：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://stackoverflow.com/questions/208016/how-to-list-the-properties-of-a-javascript-object&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;How to list the properties of a javascript object&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>Tech</category></item><item><title>Javascript and this</title><link>https://blog.amowu.com/posts/2012-05-15-javascript-and-this/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2012-05-15-javascript-and-this/</guid><description>筆記一個 javascript 的特性。</description><pubDate>Tue, 15 May 2012 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;筆記一個 javascript 的特性。&lt;/p&gt;
&lt;p&gt;通常在使用一些 method 的時候參數都帶有 Callback Function：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;click(callbackFunction)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;如果在這個 Callback Function 裡面直接使用 this 是沒有辦法的：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;callbackFunction&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;doSomethiing&lt;/span&gt;&lt;span&gt;(); &lt;/span&gt;&lt;span&gt;// undefined&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;必須先在在外層定義 this 為一個變數才可以：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; self &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;callbackFunction&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;self.&lt;/span&gt;&lt;span&gt;doSomethiing&lt;/span&gt;&lt;span&gt;(); &lt;/span&gt;&lt;span&gt;// OK!!&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;參考文章：&lt;a href=&quot;http://blog.darkthread.net/post-2009-03-11-js-this-and-closure.aspx&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Javascript — 淺談this與Closure&lt;/a&gt;&lt;/p&gt;</content:encoded><category>Tech</category></item><item><title>Javascript message on Facebook post feed</title><link>https://blog.amowu.com/posts/2012-03-04-javascript-message-on-facebook-post/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2012-03-04-javascript-message-on-facebook-post/</guid><description>之前在使用Facebook API的時候碰到的問題,，筆記一下。</description><pubDate>Sun, 04 Mar 2012 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;之前在使用Facebook API的時候碰到的問題,，筆記一下。&lt;/p&gt;
&lt;p&gt;如果在沒有登入Facebook的情況下開啟 Feed Dialog 發佈訊息，
會被導到一個莫名其妙的頁面，上面只有一串 javascript 如下&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;text/javascript&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;window.location.href=&quot;fbconnect:\/\/success?&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;post_id=XXXXXX;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;這個問題我碰到兩次：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一次是在網頁版直接使用 &lt;a href=&quot;https://developers.facebook.com/docs/reference/dialogs/feed/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Graph API call&lt;/a&gt; 的方式開啟 Feed Dialog&lt;/li&gt;
&lt;li&gt;一次是在 iOS Facebook SDK 中使用 Feed Dialog&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;iOS好像是在4.0以下才會發生，不太確定&lt;/p&gt;
&lt;p&gt;解決方法：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;找到呼叫 Graph API 的地方 ( iOS SDK 應該在 Dialog 相關的檔案裡)&lt;/li&gt;
&lt;li&gt;將 &lt;a href=&quot;https://m.facebbok.com/dialog/feed?xxxxxx&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://m.facebbok.com/dialog/feed?xxxxxx&lt;/a&gt; 的 m 改成 www&lt;/li&gt;
&lt;li&gt;還是不行的話就把 https 改成 http 試試，應該可以解決~&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;參考：&lt;a href=&quot;http://stackoverflow.com/questions/8471762/javascript-message-on-facebook-connect-post-to-feed-in-iphone-ipad-app&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Javascript message on Facebook Connect post to feed in iPhone/iPad app&lt;/a&gt;&lt;/p&gt;</content:encoded><category>Tech</category></item><item><title>Use getPixelRatio for HTML5 on retina</title><link>https://blog.amowu.com/posts/2012-03-04-use-getpixelratio-for-html5-on-retina/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2012-03-04-use-getpixelratio-for-html5-on-retina/</guid><description>如果用 HTML5 配 WebView 來作 App 的話，在 iPhone4 跟一些 Android 手機會碰到的解析度問題。</description><pubDate>Sun, 04 Mar 2012 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;如果用 HTML5 配 WebView 來作 App 的話，在 iPhone4 跟一些 Android 手機會碰到的解析度問題。&lt;/p&gt;
&lt;p&gt;iPhone4 的 Retina 是 960 x 640，但是在 WebView 底下會以 480 x 320 來顯示。&lt;/p&gt;
&lt;p&gt;所以須要利用到 javascript 的 getPixelRatio() 來作調整：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;/**&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* Return current device resolution pixel ratio, Default is 1.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* &lt;/span&gt;&lt;span&gt;@return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{number}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;getDevicePixelRatio&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (window.devicePixelRatio &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;undefined&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; window.devicePixelRatio;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;這裡寫一支 function 來取得裝置的 PixelRatio，一般通常都 return 1，解析度較大的手機才會回傳其他數值，例如 iPhone4 則回傳 2。&lt;/p&gt;
&lt;p&gt;最後寫一支 initDeviceViewport 的 function 來塞值給 viewport metadata tag，例如這裡 iPhone4 會將 viewport 的 scale 全部縮放為 0.5 倍。&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;/**&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* Initialize device viewport meta tag to full screen size.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* &lt;/span&gt;&lt;span&gt;@author&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;http:&lt;/span&gt;&lt;span&gt;//jsway.se/?p=150&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;initDeviceViewport&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; scale &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; ratio &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;getDevicePixelRatio&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (ratio &lt;/span&gt;&lt;span&gt;!==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;scale &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; ratio;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;scale &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; scale.&lt;/span&gt;&lt;span&gt;toFixed&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; viewport &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; document.&lt;/span&gt;&lt;span&gt;getElementById&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;viewport&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; vp_width &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;width=device-width; &quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; vp_initial_scale &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;initial-scale=&quot;&lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;scale&lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;&quot;; &quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; vp_maximum_scale  &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;maximum-scale=&quot;&lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;scale&lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;&quot;; &quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; vp_minimum_scale &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;minimum-scale=&quot;&lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;scale&lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;&quot;; &quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; vp_user_scalable &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;user-scalable=0;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;viewport.attributes.content.value &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;vp_width&lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;vp_initial_scale&lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;vp_minimum_scale&lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;vp_maximum_scale&lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;vp_user_scalable;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;參考： &lt;a href=&quot;http://jsway.se/?p=150&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;RETINA DISPLAY AND WEB DEVELOPMENT&lt;/a&gt;&lt;/p&gt;</content:encoded><category>Tech</category></item><item><title>Hello PureMVC！</title><link>https://blog.amowu.com/posts/2011-03-29-hello-puremvc/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2011-03-29-hello-puremvc/</guid><description>第一次聽到 PureMVC 的時候大概是兩年前，那時候就對這套 MVC Framework 很感興趣，不過當時對於它的架構是有看沒有懂，就一直拖到了現在。</description><pubDate>Tue, 29 Mar 2011 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;第一次聽到 PureMVC 的時候大概是兩年前，那時候就對這套 MVC Framework 很感興趣，不過當時對於它的架構是有看沒有懂，就一直拖到了現在。&lt;/p&gt;
&lt;p&gt;最近因為工作需要用到的關係，所以又再次學習它，經過閱覽無數前輩分享的文章之後，終於初步了解它的運作模式了，因此分享這邊學習心得筆記。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;什麼是 MVC ?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;一個設計方法（Design pattern 也稱設計模式）。&lt;/p&gt;
&lt;p&gt;想初步了解的朋友可以參考 &lt;a href=&quot;http://zh.wikipedia.org/wiki/MVC&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;維基百科的說明&lt;/a&gt; 。&lt;/p&gt;
&lt;p&gt;網路也有許多相關文章，大略內容就是將程式開發架構分為 Model、View 和 Controller 三個部份的開發方式，建議可以閱讀 &lt;a href=&quot;http://findbook.tw/book/9789867794529/basic&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;深入淺出設計模式&lt;/a&gt; 這本書。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;什麼是 PureMVC ?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;一個跨多語言的 MVC Framework，其中我們要用到的是 Actionscript 3.0 版本，這是他們的 &lt;a href=&quot;http://puremvc.org/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;官方網站&lt;/a&gt; 。&lt;/p&gt;
&lt;p&gt;下圖是他的架構圖，除了 MVC 外，它也用到不少其他 design pattern，第一次看可能會霧煞煞，沒關係，我們下面慢慢介紹。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; width=&quot;843&quot; height=&quot;590&quot; src=&quot;https://blog.amowu.com/_astro/cover.C1d6oBIa_1NoIyM.webp&quot; srcset=&quot;https://blog.amowu.com/_astro/cover.C1d6oBIa_1pyXX1.webp 640w, https://blog.amowu.com/_astro/cover.C1d6oBIa_Z10I0Tp.webp 750w, https://blog.amowu.com/_astro/cover.C1d6oBIa_BQmCT.webp 828w, https://blog.amowu.com/_astro/cover.C1d6oBIa_1NoIyM.webp 843w&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Facade 可以看做是 MVC 之間的共同溝通管道，可以減少程式之間的耦合。&lt;/p&gt;
&lt;p&gt;Command 就是扮演 Controller 的角色，PureMVC 寫出來的程式通常都會是以傳送(send)通知(Notification)的方式在進行動作，而 Command 就是負責執行它收到的通知所需要做的事情。&lt;/p&gt;
&lt;p&gt;Proxy 從字面上看就是 Model 的代理者，不管是 C 還是 V 要取得使用資料，都必須經過它，這樣的好處是增加後端資料的可抽換性。&lt;/p&gt;
&lt;p&gt;Mediator 可以看做是統一視覺元件的介面，透過它更新或取得視覺元件的狀態。&lt;/p&gt;
&lt;p&gt;OK，大致上說明了 PureMVC 的概念之後，我們開始撰寫第一個程式吧，我參考了 &lt;a href=&quot;http://blog.ben.idv.tw/2009/08/puremvc.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;邦邦的部落格-初探pureMVC這篇文章&lt;/a&gt; ，淺而易懂，是一個簡單的搜尋小程式。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;配置 PureMVC 環境&lt;/strong&gt; :&lt;/p&gt;
&lt;p&gt;這裡我們使用 FlashDevelop 這套好用的免費軟體來開發， &lt;a href=&quot;http://www.flashdevelop.org/community/viewforum.php?f=11&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;下載FlashDevelop&lt;/a&gt; 。&lt;/p&gt;
&lt;p&gt;接下來到 PureMVC 的網站， &lt;a href=&quot;http://puremvc.org/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;下載 PureMVC 的 library&lt;/a&gt; 。&lt;/p&gt;
&lt;p&gt;下載完可以看到 bin 資料夾裡面有個 swc 檔，待會要用的到它。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;建立 Project&lt;/strong&gt; :&lt;/p&gt;
&lt;p&gt;安裝並開啟FlashDevelop，開啟新專案。&lt;/p&gt;
&lt;p&gt;這裡我們選擇 AS3 Project 來開發。&lt;/p&gt;
&lt;p&gt;專案建立完後的架構如下，之後新增的 as 檔都放在 src 資料夾下。&lt;/p&gt;
&lt;p&gt;將剛剛下載的 swc 檔放到專案的 lib 資料夾底下。&lt;/p&gt;
&lt;p&gt;然後右鍵加入 Library，這布記得要做唷，不然無法使用 PureMVC 的 API。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;開始 Coding&lt;/strong&gt; :&lt;/p&gt;
&lt;p&gt;這個程式會有以下四種 Notification (通知) :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;STARTUP — 程式啟動&lt;/li&gt;
&lt;li&gt;INITED — 程式初始化結束&lt;/li&gt;
&lt;li&gt;SEARCH — 搜尋資料&lt;/li&gt;
&lt;li&gt;RESULT — 搜尋結果&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;還有三個視覺元件 :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;searchTxt — 搜尋字串的輸入欄位&lt;/li&gt;
&lt;li&gt;searchBtn — 送出搜尋字串的按鈕&lt;/li&gt;
&lt;li&gt;resultTxt — 顯示搜尋結果的欄位&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;一開始我們先寫把 Model 的部分，這個 class 主要負責提供搜尋所需的資料，為了簡化學習，我們只回傳一字串 “ XXX Hello PureMVC! “。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;DataProxy.as&lt;/strong&gt; :&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;package&lt;/span&gt;&lt;span&gt; puremvc.model&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; org.puremvc.as3.interfaces.&lt;/span&gt;&lt;span&gt;IP&lt;/span&gt;&lt;span&gt;roxy;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; org.puremvc.as3.patterns.proxy.Proxy;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; puremvc.ApplicationFacade;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/**&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* DataProxy&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* 儲存資料的 Model&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;DataProxy&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Proxy&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;implements&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;IProxy&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; PROXY_NAME&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;String &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;DataProxy&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; function &lt;/span&gt;&lt;span&gt;DataProxy&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;super&lt;/span&gt;&lt;span&gt;(PROXY_NAME, &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; function &lt;/span&gt;&lt;span&gt;search&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;query&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;):void {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;// 提供外部使用的搜尋 Method.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;// 此方法並未真正實作任何搜尋程式.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;// 暫時以 query + &quot; Hello PureMVC!&quot; 作為搜尋結果.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;result&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;String &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; query &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot; Hello PureMVC!&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;Object &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; { };&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;body[&lt;/span&gt;&lt;span&gt;&quot;result&quot;&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; result;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;// 將結果包裝成 body[&quot;result&quot;] 後傳送 NOTIFICATION_RESULT 通知&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.facade.&lt;/span&gt;&lt;span&gt;sendNotification&lt;/span&gt;&lt;span&gt;(ApplicationFacade.NOTIFICATION_RESULT, body);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;再來我們寫 View 的部分，MainMediator 主要負責接收 INITED 跟 RESULT 這兩個通知。&lt;/p&gt;
&lt;p&gt;INITED 代表程式初始化結束，當初始化結束的通知發出後，MainMediator 就會需要偵聽事件的視覺元件加入偵聽。&lt;/p&gt;
&lt;p&gt;RESULT 代表搜尋結果，當這個通知發出後，MainMediator 會將搜尋結果顯示到 resultTxt 這個 TextField元件上。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;MainMediator.as&lt;/strong&gt; :&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;package&lt;/span&gt;&lt;span&gt; puremvc.view&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; flash.events.&lt;/span&gt;&lt;span&gt;M&lt;/span&gt;&lt;span&gt;ouse&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt;vent;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; org.puremvc.as3.interfaces.IMediator;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; org.puremvc.as3.interfaces.INotification;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; org.puremvc.as3.patterns.mediator.Mediator;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; puremvc.ApplicationFacade;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/**&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* MainMediator&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* 主程式視覺介面&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;MainMediator&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Mediator&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;implements&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;IMediator&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; MEDIATOR_NAME&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;String &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;MainMediator&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; function &lt;/span&gt;&lt;span&gt;MainMediator&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;viewComponent&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;Object&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;super&lt;/span&gt;&lt;span&gt;(MEDIATOR_NAME, viewComponent);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;override public function &lt;/span&gt;&lt;span&gt;listNotificationInterests&lt;/span&gt;&lt;span&gt;():Array {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;// 此 Mediator 關注哪幾個 NOTIFICATION&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ApplicationFacade.NOTIFICATION_INITED,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ApplicationFacade.NOTIFICATION_RESULT&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;];&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;override public function &lt;/span&gt;&lt;span&gt;handleNotification&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;notification&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;INotification&lt;/span&gt;&lt;span&gt;):void {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;// 當各 NOTIFICATION 產生通知時的動作&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;switch&lt;/span&gt;&lt;span&gt;(notification.&lt;/span&gt;&lt;span&gt;getName&lt;/span&gt;&lt;span&gt;())&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;                &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; ApplicationFacade.NOTIFICATION_INITED&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;                    &lt;/span&gt;&lt;span&gt;onInitedNotify&lt;/span&gt;&lt;span&gt;(notification);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;                    &lt;/span&gt;&lt;span&gt;break&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;                &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; ApplicationFacade.NOTIFICATION_RESULT&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;                    &lt;/span&gt;&lt;span&gt;onResultNotify&lt;/span&gt;&lt;span&gt;(notification);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;                    &lt;/span&gt;&lt;span&gt;break&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; function &lt;/span&gt;&lt;span&gt;onInitedNotify&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;notification&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;INotification&lt;/span&gt;&lt;span&gt;):void {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;// 當主程式初始化結束的動作&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;setListener&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; function get &lt;/span&gt;&lt;span&gt;app&lt;/span&gt;&lt;span&gt;():Main {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Main&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.viewComponent);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; function &lt;/span&gt;&lt;span&gt;onResultNotify&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;notification&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;INotification&lt;/span&gt;&lt;span&gt;):void {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;// 將取得的 body[&quot;result&quot;] 值顯示在 resultTxt 上&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;app.resultTxt.text &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; notification.&lt;/span&gt;&lt;span&gt;getBody&lt;/span&gt;&lt;span&gt;()[&lt;/span&gt;&lt;span&gt;&quot;result&quot;&lt;/span&gt;&lt;span&gt;];&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; function &lt;/span&gt;&lt;span&gt;setListener&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;Boolean&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;):void {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;// 設定是否偵聽 searchBtn Click 事件&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;app.searchBtn.&lt;/span&gt;&lt;span&gt;addEventListener&lt;/span&gt;&lt;span&gt;(MouseEvent.CLICK, onSearchBtnClick);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;app.searchBtn.&lt;/span&gt;&lt;span&gt;removeEventListener&lt;/span&gt;&lt;span&gt;(MouseEvent.CLICK, onSearchBtnClick);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; function &lt;/span&gt;&lt;span&gt;onSearchBtnClick&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;MouseEvent&lt;/span&gt;&lt;span&gt;):void {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;// 將 searchTxt 的值包裝成 body[&quot;keyword&quot;] 後發送 NOTIFICATION_SEARCH 的通知&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;Object &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; { };&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;body[&lt;/span&gt;&lt;span&gt;&quot;keyword&quot;&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; app.searchTxt.text;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.facade.&lt;/span&gt;&lt;span&gt;sendNotification&lt;/span&gt;&lt;span&gt;(ApplicationFacade.NOTIFICATION_SEARCH, body);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;接下來我們開始寫需要用到的 Command，這裡我們先寫負責處理 SEARCH 通知的 SearchCommand。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;SearchCommand.as&lt;/strong&gt; :&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;package&lt;/span&gt;&lt;span&gt; puremvc.controller&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; org.puremvc.as3.interfaces.&lt;/span&gt;&lt;span&gt;IC&lt;/span&gt;&lt;span&gt;ommand;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; org.puremvc.as3.interfaces.INotification;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; org.puremvc.as3.patterns.command.SimpleCommand;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; puremvc.model.DataProxy;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/**&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* SearchCommand&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* 接到搜尋通知後要做的事.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;SearchCommand&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;SimpleCommand&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;implements&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ICommand&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; function &lt;/span&gt;&lt;span&gt;SearchCommand&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;super&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;override public function &lt;/span&gt;&lt;span&gt;execute&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;notification&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;INotification&lt;/span&gt;&lt;span&gt;):void {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;// 向 MainMediator 取得 body[&quot;keyword&quot;] 對映的元件的值&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;query&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;String &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; notification.&lt;/span&gt;&lt;span&gt;getBody&lt;/span&gt;&lt;span&gt;()[&lt;/span&gt;&lt;span&gt;&quot;keyword&quot;&lt;/span&gt;&lt;span&gt;];&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;search&lt;/span&gt;&lt;span&gt;(query);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; function &lt;/span&gt;&lt;span&gt;search&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;query&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;):void {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;// 將要搜尋的值傳給 DataProxy 提供的 search Method&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;dataProxy&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;DataProxy &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;DataProxy&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.facade.&lt;/span&gt;&lt;span&gt;retrieveProxy&lt;/span&gt;&lt;span&gt;(DataProxy.PROXY_NAME));&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;dataProxy.&lt;/span&gt;&lt;span&gt;search&lt;/span&gt;&lt;span&gt;(query);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;再來是程式啟動 STARTUP 的 StartupCommand。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;StartupCommand.as&lt;/strong&gt; :&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;package&lt;/span&gt;&lt;span&gt; puremvc.controller&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; org.puremvc.as3.interfaces.&lt;/span&gt;&lt;span&gt;IC&lt;/span&gt;&lt;span&gt;ommand;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; org.puremvc.as3.interfaces.INotification;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; org.puremvc.as3.patterns.command.SimpleCommand;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; puremvc.ApplicationFacade;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; puremvc.model.DataProxy;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; puremvc.view.MainMediator;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/**&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* StartupCommand&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* 程式啟動時需要做的事情&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;StartupCommand&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;SimpleCommand&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;implements&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ICommand&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; function &lt;/span&gt;&lt;span&gt;StartupCommand&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;super&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;override public function &lt;/span&gt;&lt;span&gt;execute&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;notification&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;INotification&lt;/span&gt;&lt;span&gt;):void {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;// 註冊 Model DataProxy&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.facade.&lt;/span&gt;&lt;span&gt;registerProxy&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;DataProxy&lt;/span&gt;&lt;span&gt;());&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;// 註冊 View MainMediator&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;app&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;Main &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Main&lt;/span&gt;&lt;span&gt;(notification.&lt;/span&gt;&lt;span&gt;getBody&lt;/span&gt;&lt;span&gt;());&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.facade.&lt;/span&gt;&lt;span&gt;registerMediator&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;MainMediator&lt;/span&gt;&lt;span&gt;(app));&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;// 註冊接下來會用到的 Controller Command&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.facade.&lt;/span&gt;&lt;span&gt;registerCommand&lt;/span&gt;&lt;span&gt;(ApplicationFacade.NOTIFICATION_SEARCH, SearchCommand);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;// 通知初始化結束&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.facade.&lt;/span&gt;&lt;span&gt;sendNotification&lt;/span&gt;&lt;span&gt;(ApplicationFacade.NOTIFICATION_INITED);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;快結束了! 再來把程式的核心，ApplicationFacade 給完成就差不多了。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ApplicationFacade.as&lt;/strong&gt; :&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;package&lt;/span&gt;&lt;span&gt; puremvc&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; org.puremvc.as3.interfaces.&lt;/span&gt;&lt;span&gt;IF&lt;/span&gt;&lt;span&gt;acade;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; org.puremvc.as3.patterns.facade.Facade;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; puremvc.controller.StartupCommand;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/**&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* ApplicationFacade&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* 所有程式之間唯一的溝通橋樑.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* 使用到 Facade 和 Singleton 設計模式.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ApplicationFacade&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Facade&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;implements&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;IFacade&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;// 所有此程式會用到的列舉狀態&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;// - NOTIFICATION_STARTUP 程式啟動&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;// - NOTIFICATION_INITED  程式初始化結束&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;// - NOTIFICATION_SEARCH  發出搜尋的請求&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;// - NOTIFICATION_RESULT  產生結果的通知&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; NOTIFICATION_STARTUP&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;String &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;NOTIFICATION_STARTUP&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; NOTIFICATION_INITED&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;String &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;NOTIFICATION_INITED&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; NOTIFICATION_SEARCH&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;String &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;NOTIFICATION_SEARCH&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; NOTIFICATION_RESULT&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;String &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;NOTIFICATION_RESULT&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; function &lt;/span&gt;&lt;span&gt;getInstance&lt;/span&gt;&lt;span&gt;():ApplicationFacade {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (instance &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;) instance &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ApplicationFacade&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ApplicationFacade&lt;/span&gt;&lt;span&gt;(instance);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;override protected function &lt;/span&gt;&lt;span&gt;initializeController&lt;/span&gt;&lt;span&gt;():void {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;super&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;initializeController&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;// 當 ApplicationFacade 被第一次呼叫的時候&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;// 會執行這個 initializeController()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;// 所以在這裡我們註冊 NOTIFICATION_STARTUP 這個啟動 Command&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;registerCommand&lt;/span&gt;&lt;span&gt;(NOTIFICATION_STARTUP, StartupCommand);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/**&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;         &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* startup&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;         &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;         &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* 程式啟動.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;         &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;         &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* &lt;/span&gt;&lt;span&gt;@param&lt;/span&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;app&lt;/span&gt;&lt;span&gt; 主程式&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;         &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; function &lt;/span&gt;&lt;span&gt;startup&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;app&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;Main&lt;/span&gt;&lt;span&gt;):void {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;sendNotification&lt;/span&gt;&lt;span&gt;(NOTIFICATION_STARTUP, app);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;最後，將用到的視覺元件加一加，然後寫上一行 &lt;code&gt;ApplicationFacade.getInstance().startup(this)&lt;/code&gt; 就可以收工囉!!!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Main.as&lt;/strong&gt; :&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;package&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; flash.display.&lt;/span&gt;&lt;span&gt;S&lt;/span&gt;&lt;span&gt;imple&lt;/span&gt;&lt;span&gt;B&lt;/span&gt;&lt;span&gt;utton;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; flash.display.Sprite;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; flash.text.TextField;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; flash.text.TextFieldType;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; flash.events.Event;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; puremvc.ApplicationFacade;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/**&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* Main&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* 使用 PureMVC 來實作一個簡單的搜尋小程式.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Main&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Sprite&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; function &lt;/span&gt;&lt;span&gt;Main&lt;/span&gt;&lt;span&gt;():void&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (stage) &lt;/span&gt;&lt;span&gt;init&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;addEventListener&lt;/span&gt;&lt;span&gt;(Event.ADDED_TO_STAGE, init);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; function &lt;/span&gt;&lt;span&gt;init&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;Event&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;):void&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;removeEventListener&lt;/span&gt;&lt;span&gt;(Event.ADDED_TO_STAGE, init);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;// entry point&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;initComponents&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;// pureMVC 的進入點.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;// 使用唯一的 ApplicationFacade class 作為入口.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;// 之後所有的元件之間溝通都需要經過此 facade.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ApplicationFacade.&lt;/span&gt;&lt;span&gt;getInstance&lt;/span&gt;&lt;span&gt;().&lt;/span&gt;&lt;span&gt;startup&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;searchTxt&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;TextField;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;searchBtn&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;SimpleButton;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;resultTxt&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;TextField;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; function &lt;/span&gt;&lt;span&gt;initComponents&lt;/span&gt;&lt;span&gt;():void {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;// 初始化視覺元件(ViewCompoents)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;// searchTxt&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;searchTxt &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TextField&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;searchTxt.type &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; TextFieldType.INPUT;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;searchTxt.border &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;searchTxt.x &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;50&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;searchTxt.y &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;50&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;searchTxt.width &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;200&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;searchTxt.height &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;20&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;addChild&lt;/span&gt;&lt;span&gt;(searchTxt);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;// searchBtn&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;buttonStyle&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;Sprite &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Sprite&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;buttonStyle.graphics.&lt;/span&gt;&lt;span&gt;beginFill&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0xcccccc&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;buttonStyle.graphics.&lt;/span&gt;&lt;span&gt;drawRect&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;20&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;buttonStyle.graphics.&lt;/span&gt;&lt;span&gt;endFill&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;buttonTxt&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;TextField &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TextField&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;buttonTxt.text &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Search Button&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;buttonTxt.width &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;buttonTxt.height &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;20&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;buttonStyle.&lt;/span&gt;&lt;span&gt;addChild&lt;/span&gt;&lt;span&gt;(buttonTxt);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;searchBtn &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;SimpleButton&lt;/span&gt;&lt;span&gt;(buttonStyle, buttonStyle, buttonStyle, buttonStyle);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;searchBtn.x &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;260&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;searchBtn.y &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;50&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;addChild&lt;/span&gt;&lt;span&gt;(searchBtn);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;// resultTxt&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;resultTxt &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TextField&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;resultTxt.type &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; TextFieldType.DYNAMIC;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;resultTxt.border &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;resultTxt.x &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;50&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;resultTxt.y &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;80&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;resultTxt.width &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;200&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;resultTxt.height &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;200&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;addChild&lt;/span&gt;&lt;span&gt;(resultTxt);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;以上是我初步學習 PureMVC 後的心得，更進階一點的介紹可以參考官方的 Best Practice 或更多相關文章。&lt;/p&gt;
&lt;p&gt;範例原始檔：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://sites.google.com/site/amo26site/download/MyFirstPureMVC.rar?attredirects=0&amp;amp;d=1&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;MyFirstPureMVC.rar&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;參考文章：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://blog.ben.idv.tw/2009/08/puremvc.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;邦邦的部落格 — 初探 pureMVC&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://erinylin.blogspot.com/2011_03_01_archive.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Erin’s Blog — PureMVC 我也會系列&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>Tech</category></item><item><title>Develop Facebook Canvas Page with CodeIgniter</title><link>https://blog.amowu.com/posts/2011-03-11-develop-facebook-canvas-page-with/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2011-03-11-develop-facebook-canvas-page-with/</guid><description>這篇文章是介紹如何使用 CodeIgniter 這個 PHP Framework 來初步架構出 MVC 的環境, 並且開發一個簡單的 Facebook Canvas Page。</description><pubDate>Fri, 11 Mar 2011 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;這篇文章是介紹如何使用 CodeIgniter 這個 PHP Framework 來初步架構出 MVC 的環境, 並且開發一個簡單的 Facebook Canvas Page。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;什麼是 CodeIgniter 呢？&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;CodeIgniter 是開發 PHP 應用程式的 framework 及工具組。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;提供簡易的介面和邏輯結構來使用豐富的函式庫，其目的是讓你可以加 快開發速度。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;使用 CodeIgniter 只需要寫少少的程式，讓創造力可專注在專案開發。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://codeigniter.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;CodeIgniter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.codeigniter.org.tw/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;CodeIgniter 台灣&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;CodeIgniter 中文使用手冊 (&lt;a href=&quot;http://codeigniter.com/user_guide/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;英&lt;/a&gt;) (&lt;a href=&quot;http://codeigniter.org.tw/user_guide/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;中&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;什麼是 MVC 呢？&lt;/p&gt;
&lt;p&gt;簡而言之是一個程式設計模式（Design pattern），詳細介紹推薦參考這篇： &lt;a href=&quot;http://www.jaceju.net/resources/webmvc/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;透視 WebMVC&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;什麼是 &lt;a href=&quot;http://developers.facebook.com/docs/guides/canvas/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Facebook Canvas page&lt;/a&gt; ？&lt;/p&gt;
&lt;p&gt;簡單來說，就是 Facebook 的應用程式, 像開心農場之類直接嵌入在 Facebook 內的網頁，Facebook 的 API 很強大，可以作手機應用，網站應用等等，這篇我們就只討論 Canvas page 這個範圍，有興趣的可以到 &lt;a href=&quot;http://developers.facebook.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;開發者頁面&lt;/a&gt; 研究看看。&lt;/p&gt;
&lt;p&gt;進入本篇重點，開發流程我把它分成三個步驟，如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;下載 CodeIgniter 2.0&lt;/li&gt;
&lt;li&gt;建立 Facebook 應用程式&lt;/li&gt;
&lt;li&gt;開始編寫程式碼&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;1.下載 CodeIgniter 2.0&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;這裡我們使用 CodeIgniter 2.0.0 的版本來實作。&lt;/p&gt;
&lt;p&gt;前往 &lt;a href=&quot;http://www.codeigniter.org.tw/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;CodeIgniter 網站&lt;/a&gt;下載 &lt;a href=&quot;http://codeigniter.com/download.php&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;CodeIgniter 2.0&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;下載完可看到 zip 檔，解壓縮至你的網頁目錄吧&lt;/p&gt;
&lt;p&gt;CodeIgniter 的目錄內容大致如下&lt;/p&gt;
&lt;p&gt;開啟瀏覽器測試看看，成功的話會如下圖所示&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. 建立 Facebook 應用程式&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;前往 &lt;a href=&quot;http://developers.facebook.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Facebook 開發者頁面&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;點選上方 我的應用程式 連結進入&lt;/p&gt;
&lt;p&gt;然後再點選 建立新的應用程式 按鈕&lt;/p&gt;
&lt;p&gt;設定應用程式的名稱&lt;/p&gt;
&lt;p&gt;設定 相關介紹，大致上都可以先不用填&lt;/p&gt;
&lt;p&gt;我們要開發 Facebook Canvas，所以選 Facebook 集成 這個選項&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;app id 待會寫程式時會用到&lt;/li&gt;
&lt;li&gt;canvas name 也是，這是你應用程式的網址&lt;/li&gt;
&lt;li&gt;canvas url 則是填寫實際的網頁位址&lt;/li&gt;
&lt;li&gt;https 這部分我還沒測試過, 如果程式要在 https 的 Facebook 環境底下運作的話，則需要填寫&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;大致上的配置都完成了，儲存後前往你的 canvas page 看看結果如何&lt;/p&gt;
&lt;p&gt;沒問題的話，可以在 Facebook 裡面成功執行剛剛的 CodeIgniter 頁面&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. 開始編寫程式碼&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;OK，CodeIgniter 跟 Facebook 的環境都配置的差不多了，接下來進入 coding 的階段，我們將編寫下面這三個文件：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;welcome controller — 負責與 facebook model 溝通，處理使用者認證的部分，並把資料傳給 welcome view 作前端顯示&lt;/li&gt;
&lt;li&gt;welcome view — 將 welcome controller 傳過來的資料作顯示&lt;/li&gt;
&lt;li&gt;facebook model — 實作 facebook 認證，取得使用者資料的部分&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;welcome controller：&lt;/p&gt;
&lt;p&gt;開啟（application\controllers\welcome.php），在 &lt;code&gt;index()&lt;/code&gt; 這個 function 內編寫以下程式：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;index&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;// 載入 Facebook Model&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;$this&lt;/span&gt;&lt;span&gt;-&amp;gt;&lt;/span&gt;&lt;span&gt;load&lt;/span&gt;&lt;span&gt;-&amp;gt;&lt;/span&gt;&lt;span&gt;model&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;Facebook_model&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;facebook&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;// 判斷使用者是否認證此應用程式&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;( &lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;$this&lt;/span&gt;&lt;span&gt;-&amp;gt;&lt;/span&gt;&lt;span&gt;facebook&lt;/span&gt;&lt;span&gt;-&amp;gt;&lt;/span&gt;&lt;span&gt;is_auth&lt;/span&gt;&lt;span&gt;())&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;// 還未認證的情況下:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;//&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;// 需要將使用者導向 Facebook 的認證頁面,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;// 並提供認證所需相關資料.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;//&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;// $app_id Facebook    應用程式 ID&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;//&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;// $canvas_name    Canvas Page 填寫的名稱&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;//                 ex: http://apps.facebook.com/XXX&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;//&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;// $permission    需要使用者同意的權限資料&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;//                ex: email, user_birthday&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;// ref url:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;// http://developers.facebook.com/docs/authentication/permissions/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;$app_id &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;YOUR APP ID&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;$canvas_name &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;YOUR CANVAS NAME&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;$permission &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;email,user_birthday&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;$this&lt;/span&gt;&lt;span&gt;-&amp;gt;&lt;/span&gt;&lt;span&gt;facebook&lt;/span&gt;&lt;span&gt;-&amp;gt;&lt;/span&gt;&lt;span&gt;do_auth&lt;/span&gt;&lt;span&gt;($app_id, $canvas_name, $permission);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;// 已認證的情況下:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;//&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;// 向 Facebook model 取得使用者的資料,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;// 然後傳給 welcome_message View 作顯示.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;$me &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;$this&lt;/span&gt;&lt;span&gt;-&amp;gt;&lt;/span&gt;&lt;span&gt;facebook&lt;/span&gt;&lt;span&gt;-&amp;gt;&lt;/span&gt;&lt;span&gt;get_me&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;$data &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;array&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;&apos;me_id&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; $me&lt;/span&gt;&lt;span&gt;-&amp;gt;&lt;/span&gt;&lt;span&gt;id,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;&apos;me_name&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; $me&lt;/span&gt;&lt;span&gt;-&amp;gt;&lt;/span&gt;&lt;span&gt;name,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;&apos;me_email&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; $me&lt;/span&gt;&lt;span&gt;-&amp;gt;&lt;/span&gt;&lt;span&gt;email,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;&apos;me_birthday&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; $me&lt;/span&gt;&lt;span&gt;-&amp;gt;&lt;/span&gt;&lt;span&gt;birthday&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;$this&lt;/span&gt;&lt;span&gt;-&amp;gt;&lt;/span&gt;&lt;span&gt;load&lt;/span&gt;&lt;span&gt;-&amp;gt;&lt;/span&gt;&lt;span&gt;view&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;welcome_message&apos;&lt;/span&gt;&lt;span&gt;, $data);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;welcome view：&lt;/p&gt;
&lt;p&gt;開啟（application\views\welcome_message.php），修改內容如下：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;!&lt;/span&gt;&lt;span&gt;DOCTYPE&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;lang&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;en&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;head&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;meta&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;charset&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;utf-8&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;&amp;gt;Hello Canvas&amp;lt;/&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;head&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&amp;gt;Hello Canvas&amp;lt;/&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;ID: &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;?php echo $me_id; ?&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;Name: &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;?php echo $me_name; ?&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;Email: &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;?php echo $me_email; ?&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;Birthday: &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;?php echo $me_birthday; ?&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;facebook model：&lt;/p&gt;
&lt;p&gt;在（application\models\）底下新增 facebook_model.php，內容如下：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;?&lt;/span&gt;&lt;span&gt;php&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; ( &lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;defined&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;BASEPATH&apos;&lt;/span&gt;&lt;span&gt;)) &lt;/span&gt;&lt;span&gt;exit&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;No direct script access allowed&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;/**&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* Facebook Model&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Facebook_model&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CI_Model&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; $_cookie;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;__construct&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;parent::&lt;/span&gt;&lt;span&gt;__construct&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;/**&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* 判斷頁面是否認證&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* &lt;/span&gt;&lt;span&gt;@access&lt;/span&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* &lt;/span&gt;&lt;span&gt;@return&lt;/span&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt; 判斷是否認證,若頁面已認證則回傳TRUE, 反之FALSE&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;is_auth&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;$signed_request &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; $_REQUEST[&lt;/span&gt;&lt;span&gt;&quot;signed_request&quot;&lt;/span&gt;&lt;span&gt;];&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;list&lt;/span&gt;&lt;span&gt;($encoded_sig, $payload) &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;explode&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;.&apos;&lt;/span&gt;&lt;span&gt;, $signed_request, &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;$this&lt;/span&gt;&lt;span&gt;-&amp;gt;&lt;/span&gt;&lt;span&gt;_cookie &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;json_decode&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;base64_decode&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;strtr&lt;/span&gt;&lt;span&gt;($payload, &lt;/span&gt;&lt;span&gt;&apos;-_&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;+/&apos;&lt;/span&gt;&lt;span&gt;)), &lt;/span&gt;&lt;span&gt;TRUE&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;isset&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$this&lt;/span&gt;&lt;span&gt;-&amp;gt;&lt;/span&gt;&lt;span&gt;_cookie[&lt;/span&gt;&lt;span&gt;&quot;user_id&quot;&lt;/span&gt;&lt;span&gt;]);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;/**&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* 執行認證的動作&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* &lt;/span&gt;&lt;span&gt;@access&lt;/span&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* &lt;/span&gt;&lt;span&gt;@param&lt;/span&gt;&lt;span&gt;   &lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;  $app_id         應用程式 ID&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* &lt;/span&gt;&lt;span&gt;@param&lt;/span&gt;&lt;span&gt;   &lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;  $canvase_name   Canvas Page Name&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* &lt;/span&gt;&lt;span&gt;@param&lt;/span&gt;&lt;span&gt;   &lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;  $permission     使用者提供的資料&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* &lt;/span&gt;&lt;span&gt;@return&lt;/span&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;do_auth&lt;/span&gt;&lt;span&gt;($app_id, $canvas_name, $permission &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&apos;&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;$canvas_page &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;http://apps.facebook.com/&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt; $canvas_name &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;/&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;$auth_url &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;http://www.facebook.com/dialog/oauth?client_id=&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt; $app_id &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&amp;amp;redirect_uri=&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;urlencode&lt;/span&gt;&lt;span&gt;($canvas_page) &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&amp;amp;scope=&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt; $permission;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&amp;lt;script&amp;gt; top.location.href=&apos;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt; $auth_url &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&apos;&amp;lt;/script&amp;gt;&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;/**&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* 取得使用者資料&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* &lt;/span&gt;&lt;span&gt;@access&lt;/span&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* &lt;/span&gt;&lt;span&gt;@return&lt;/span&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;Object&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;get_me&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;$access_token &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;$this&lt;/span&gt;&lt;span&gt;-&amp;gt;&lt;/span&gt;&lt;span&gt;_cookie[&lt;/span&gt;&lt;span&gt;&apos;oauth_token&apos;&lt;/span&gt;&lt;span&gt;];&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;$me &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;json_decode&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;file_get_contents&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;https://graph.facebook.com/me?access_token=&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt; $access_token));&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; $me;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;/* End of file facebook_model.php */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;/* Location: ./application/models/facebook_model.php */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;初步的程式碼都打完了, 接著我們再一次連到應用程式看看，沒問題的話，初次會導向 Facebook 授權請求 的認證頁面，如下圖，並且會出現需要授權的 permission&lt;/p&gt;
&lt;p&gt;點選同意之後，沒意外的話，我們的第一個 Facebook canvas page 就出現囉！！！&lt;/p&gt;
&lt;p&gt;範例程式下載 : &lt;a href=&quot;https://sites.google.com/site/amo26site/download/Fb_with_CI.rar?attredirects=0&amp;amp;d=1&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Fb_with_CI.rar&lt;/a&gt;&lt;/p&gt;</content:encoded><category>Tech</category></item><item><title>IE6 Upgrade Notice</title><link>https://blog.amowu.com/posts/2009-08-23-ie6-upgrade-notice/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2009-08-23-ie6-upgrade-notice/</guid><description>有過網頁設計經驗的人，無論是部落格樣版或是其他頁面，都一定有個共同的痛，就是 IE6 這傢伙對 CSS 的呈現總是跟別人不同，偏偏它的市占率一直是最高的。</description><pubDate>Sun, 23 Aug 2009 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;有過網頁設計經驗的人，無論是部落格樣版或是其他頁面，都一定有個共同的痛，就是 IE6 這傢伙對 CSS 的呈現總是跟別人不同，偏偏它的市占率一直是最高的。&lt;/p&gt;
&lt;p&gt;通常解決這個問題的方法有兩種，一種是修改 CSS 直到版面看起來正常，另一種則是直接放棄，在網頁開頭就提醒使用者升級瀏覽器。&lt;/p&gt;
&lt;p&gt;我們今天要介紹的就是最後一種方法，為什麼我們要放棄 IE6 呢?&lt;/p&gt;
&lt;p&gt;因為網頁技術不斷的進步，不單單是 CSS 的問題，IE6 本身還有其他原因，它已經不適合這個世代了，加上最近許多知名網站也紛紛開始提倡升級 IE6，例如 You Tube、Twitter 和 Facebook 等，IE6 你真的玩完了，拜託還在使用 IE6 的人，請你快點更換瀏覽器吧！！（小聲:換 Firefox Chrome 吧）&lt;/p&gt;
&lt;p&gt;知名影音分享網站 YouTube 將停止對 IE6 的支援。&lt;/p&gt;
&lt;p&gt;連最近在台灣火熱的 Facebook 都要封殺 IE6 了，還在使用它的人，麻煩你快點更新吧！！！&lt;/p&gt;
&lt;p&gt;剛好之前看到一篇 &lt;a href=&quot;http://briian.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;重灌狂人&lt;/a&gt;寫的文章 “ &lt;a href=&quot;http://briian.com/?p=6382&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;在網站加入警告標語，建議 IE 6.0 的使用者升級&lt;/a&gt;”，裡面介紹了一些 IE 瀏覽器版本判斷的語法，可以分辨出使用者目前的 IE 瀏覽器是哪一個版本。&lt;/p&gt;
&lt;p&gt;例如我要讓 IE6 以下的使用者看見提示的話：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;!--[if lte IE 6]&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;這裡的內容只有IE6以下的使用者才看的見。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;![endif]--&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;只要將提示內容放在 &lt;code&gt;&amp;lt;!--[if lte IE 6]&amp;gt;&amp;lt;![endif]--&amp;gt;&lt;/code&gt; 之中，就可以讓還在用 IE6 的人，看到你要傳達給他的訊息了。&lt;/p&gt;
&lt;p&gt;語法說明：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;!--[if IE]&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;使用 IE 瀏覽器全部版本的人都看得見這裡的內容。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;![endif]--&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;!--[if IE 6]&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;只有 IE6 的使用者才能看見這裡的內容&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;![endif]--&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;!--[if lt IE 6]&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;這裡是小於 IE6 的版本使用者才能看到，不含 IE6。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;![endif]--&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;!--[if lte IE 6]&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;IE6 以下版本的使用者能看見內容，包含 IE6。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;![endif]--&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;!--[if gt IE 6]&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;這樣 IE6 以上版本的使用者會看見內容，但不包含 IE6。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;![endif]--&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;!--[if gte IE 6]&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;IE6 以上版本的使用者能看見內容，包含 IE6。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;![endif]--&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;上面的版本編號也可以換，如果把 &lt;code&gt;[if lte IE 6]&lt;/code&gt; 換成 &lt;code&gt;[if lte IE 7]&lt;/code&gt; 的話，就是 IE7 以下使用者能看見內容。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;**gt：**greater than（版本大於）&lt;/li&gt;
&lt;li&gt;**lt：**less than（版本小於）&lt;/li&gt;
&lt;li&gt;**gte：**greater than or equal（版本大於等於）&lt;/li&gt;
&lt;li&gt;**lte：**less than or equal（版本小於等於）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;最後分享一個自己寫的範本，加了 jquery 的 sideup 效果，有興趣的可以下載回去參考看看。&lt;/p&gt;
&lt;p&gt;演示範例：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://momo26.myweb.hinet.net/amo-studio/IE6Notice/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;IE6 Notice sample page&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;範本下載：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://sites.google.com/site/amo26site/download/IE6NoticeTemplate.rar?attredirects=0&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;IE6NoticeTemplate.rar&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;參考資料：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://briian.com/?p=6382&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;重灌狂人 — 在網站加入警告標語，建議IE 6.0的使用者升級！&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://jinnsblog.blogspot.com/2009/08/ie6-no-more-update-your-browser.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;靖˙技場 — IE6 No More § 請升級你的瀏覽器，不要再用過時的IE6啦！&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>Tech</category></item><item><title>Factory pattern 工廠模式</title><link>https://blog.amowu.com/posts/2009-08-20-factory-pattern/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2009-08-20-factory-pattern/</guid><description>距離上一篇 Strategy pattern 策略模式 已經兩個多月了，一直拖到現在才生出這篇文章…orz。</description><pubDate>Thu, 20 Aug 2009 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;距離上一篇 &lt;a href=&quot;https://blog.amowu.com/posts/2009-05-20-strategy-pattern-20/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Strategy pattern 策略模式&lt;/a&gt; 已經兩個多月了，一直拖到現在才生出這篇文章…orz。&lt;/p&gt;
&lt;p&gt;其實這篇 工廠模式應該要當作第一篇 Design pattern 的文章會比較好，因為這個模式很容易懂，但是 工廠模式中又細分出一些其他類似的模式，例如 抽象工廠模式 ，所以我把一些相關的資料都讀了一遍後，分三篇作介紹。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;工廠模式最主要的精神就是將 new Class 這個動作另外封裝成一個 Factory Class，這個 Factory Class 專門負責實體化這些類別。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;特地這樣做有什麼好處呢？&lt;/p&gt;
&lt;p&gt;舉個例子，假如我們現在有兩個繼承 Product 的類別，它們擁有 共同的方法 &lt;code&gt;Operation()&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;一般來講，我們如果要實體化 ProductA 或 ProductB 的話，會這樣寫：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;namespace&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;FactoryPattern&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Program&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Main&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;[] &lt;/span&gt;&lt;span&gt;args&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;Product&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;product&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ProductA&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;product.&lt;/span&gt;&lt;span&gt;Operation&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;這樣做有什麼缺點呢？&lt;/p&gt;
&lt;p&gt;如果我 product 型別要換成 ProductB 的話，就需要把第 7 行的 &lt;code&gt;new ProductA()&lt;/code&gt; 改成 &lt;code&gt;new ProductB()&lt;/code&gt; ，若是繼承 Product 的類別一多，以後程式碼的維護上會很麻煩。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;簡單工廠模式 Simple Factory Pattern&lt;/strong&gt; （又稱 靜態工廠模式 Static Factory Pattern ）：&lt;/p&gt;
&lt;p&gt;按照上圖，將擁有共同介面&lt;strong&gt;類別的實體化動作封裝在 Factory &lt;strong&gt;內，程式就可以在執行時&lt;/strong&gt;動態&lt;/strong&gt;決定要實體化哪個類別了。&lt;/p&gt;
&lt;p&gt;Factory class：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;namespace&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;SimpleFactoryPattern&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Factory&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Product&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CreateProduct&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;switch&lt;/span&gt;&lt;span&gt; (name)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;ProductA&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;          &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ProductA&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;ProductB&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;          &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ProductB&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;          &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Client：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;System&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;namespace&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;SimpleFactoryPattern&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Program&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Main&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;[] &lt;/span&gt;&lt;span&gt;args&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// productName可以動態決定要實體化的類別&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;productName&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;ProductA&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;Factory&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;productFactory&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Factory&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;Product&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;product&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; productFactory.&lt;/span&gt;&lt;span&gt;CreateProduct&lt;/span&gt;&lt;span&gt;(productName);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;product.&lt;/span&gt;&lt;span&gt;Operation&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;到這裡可以看看使用 Factory 後，和使用之前的程式碼做比較，最大的差別就是&lt;strong&gt;現在可以靠變數來決定要實體化的類別&lt;/strong&gt;了，若是需要改成 ProductB 的話，只需要將 productName 的值改成 “ProductB” 就行了。&lt;/p&gt;
&lt;p&gt;如果覺得上面的 &lt;code&gt;Factory productFactory = new Factory();&lt;/code&gt; 這行有點礙眼的話，還可以將程式碼更簡單話一點，就是把 Factory class 的 &lt;code&gt;CreateProduct()&lt;/code&gt; 方法改成 static：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Product&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CreateProduct&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;這樣就可以直接跳過 &lt;code&gt;new Factory()&lt;/code&gt; 的步驟，直接呼叫 &lt;code&gt;CreateProduct()&lt;/code&gt; 方法：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Main&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;[] &lt;/span&gt;&lt;span&gt;args&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;productName&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;ProductA&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;Product&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;product&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; Factory.&lt;/span&gt;&lt;span&gt;CreateProduct&lt;/span&gt;&lt;span&gt;(productName);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;product.&lt;/span&gt;&lt;span&gt;Operation&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;使用 簡單工廠模式 雖然方便，但也是有缺點的。&lt;/p&gt;
&lt;p&gt;如果每次新增一個 Product 子類別後，都必須修改 Factory class 中 CreateProduct() 的 switch 判斷式的話，這樣做不符合物件導向設計的 &lt;a href=&quot;http://en.wikipedia.org/wiki/Open/closed_principle&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Open-Closeed Principle(開放-封閉原則)&lt;/a&gt; 精神。&lt;/p&gt;
&lt;p&gt;什麼是開放-封閉原則呢?&lt;/p&gt;
&lt;p&gt;簡單說就是&lt;strong&gt;程式容易擴充新功能，但是不用修改原始碼&lt;/strong&gt;的意思。&lt;/p&gt;
&lt;p&gt;如果因為要擴充 ProductC class 進來，而修改了 Factory class 的話，這樣的做法並不是很好，所以比較正統的 工廠模式有另一種寫法，就是接著下一篇要介紹的 工廠方法模式 。&lt;/p&gt;
&lt;p&gt;要解決 簡單工廠模式的問題， 工廠方法模式 的做法是將 Factory 類別抽象化，讓每個 Product 子類別都有屬於自己的工廠類別，如上圖所示。&lt;/p&gt;
&lt;p&gt;Factory interface：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;namespace&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;FactoryMethodPattern&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;interface&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Factory&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;Product&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CreateProduct&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;ProductFactoryA class：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;namespace&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;FactoryMethodPattern&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ProductFactoryA&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;Factory&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Product&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CreateProduct&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ProductA&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;ProductFactoryB class：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;namespace&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;FactoryMethodPattern&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ProductFactoryB&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;Factory&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Product&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CreateProduct&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ProductB&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Client：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;System&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;namespace&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;FactoryMethodPattern&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Program&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Main&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;[] &lt;/span&gt;&lt;span&gt;args&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// ProductA&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;Factory&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;productFactoryA&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ProductFactoryA&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;Product&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;productA&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; productFactoryA.&lt;/span&gt;&lt;span&gt;CreateProduct&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;productA.&lt;/span&gt;&lt;span&gt;Operation&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// ProductB&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;Factory&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;productFactoryB&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ProductFactoryB&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;Product&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;productB&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; productFactoryB.&lt;/span&gt;&lt;span&gt;CreateProduct&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;productB.&lt;/span&gt;&lt;span&gt;Operation&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;這樣就解決 簡單工廠的擴充問題了，如果需要增加 ProductC 的話，只需要加入 繼承 Product 的ProductC class 和 繼承 Factory 的 ProductFactoryC class 就可以了，不需要動到其他程式碼。&lt;/p&gt;
&lt;p&gt;最後要介紹的是抽象工廠模式，這個模式其實跟上面的工廠方法是差不多的，只是增加了第二組Product的概念，我們來看看它的定義：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;抽象工廠模式，提供一個建立一系列相關物件的介面，而無需指定它們具體的類別。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;從上圖來看，我們有兩組產品，Product1 和 Product2，可以把它們想像成 Office 的 Word 和 Excel，這兩組產品如果要跨平台到 Mac 的話，就會分支出 Windows 版和 Mac 版的，但是軟體做的事情是一樣的，所以它們繼承了共同的方法，如下圖：&lt;/p&gt;
&lt;p&gt;看完上圖後，可以看出每個產品都有 Windows 和 Mac 兩種系列，所以我們可以為他們寫兩個工廠類別，一個專門負責生產 Windows，一個專門負責生產 Mac 的產品，來看看程式碼：&lt;/p&gt;
&lt;p&gt;Factory interface：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;namespace&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AbstractFactoryPattern&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;interface&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Factory&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 想像成CreateWindowsProduct()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;Product1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CreateProduct1&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 想像成CreateMacProduct()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;Product2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CreateProduct2&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;ProductFactoryA class：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;namespace&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AbstractFactoryPattern&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 想像成WordFactory&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ProductFactoryA&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;Factory&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Product1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CreateProduct1&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 生產Windows的Word&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Product1A&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Product2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CreateProduct2&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 生產Mac的Word&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Product2A&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;ProductFactoryB class：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;namespace&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AbstractFactoryPattern&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 想像成ExcelFactory&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ProductFactoryB&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;Factory&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Product1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CreateProduct1&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 生產Windows的Excel&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Product1B&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Product2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CreateProduct2&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 生產Mac的Excel&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Product2B&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Client：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;namespace AbstractFactoryPattern&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Program&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Main&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;[] &lt;/span&gt;&lt;span&gt;args&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;// Word工廠&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Factory&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;productFactoryA&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ProductFactoryA&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;// 製造Windows和Mac版本的Word&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Product1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;product1A&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; productFactoryA.&lt;/span&gt;&lt;span&gt;CreateProduct1&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Product2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;product2A&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; productFactoryA.&lt;/span&gt;&lt;span&gt;CreateProduct2&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;// 跑兩種版本的Word&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;product1A.&lt;/span&gt;&lt;span&gt;Operation&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;product2A.&lt;/span&gt;&lt;span&gt;Operation&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;// Excel工廠&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Factory&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;productFactoryB&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ProductFactoryB&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;// 製造Windows和Mac版本的Excel&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Product1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;product1B&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; productFactoryB.&lt;/span&gt;&lt;span&gt;CreateProduct1&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Product2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;product2B&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; productFactoryB.&lt;/span&gt;&lt;span&gt;CreateProduct2&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;// 跑兩種版本的Excel&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;product2B.&lt;/span&gt;&lt;span&gt;Operation&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;product1B.&lt;/span&gt;&lt;span&gt;Operation&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;這樣就完成 抽象工廠模式了，將 Factory 抽象化後， Client 端就能利用多型的方法實體化 Product，而不需要知道具體的類別就能操作它，這就是 抽象工廠 的優點。&lt;/p&gt;
&lt;p&gt;到這邊其實已經把 工廠模式都介紹完了，不過其實上面的 抽象工廠 還是有不完美的地方，例如我們現在加入了一個新的 Product 系列 ProductC（可以想像成 Linux 版本的 Office），那麼我們除了要寫 Product1C 跟 Product2C 這些基本的類別外，還要再為它們新增一個 ProductFactoryC 的類別，讓人覺得有一些麻煩。&lt;/p&gt;
&lt;p&gt;這邊要介紹 &lt;a href=&quot;http://findbook.tw/book/9789866761799/basic&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;大話設計模式&lt;/a&gt;一書中作者提供的一個技巧，可以將 工廠模式 再修改的更加完美。&lt;/p&gt;
&lt;p&gt;看到上圖後，可以發現我們將 Factory 的抽象化拿掉了，變回最初的 簡單工廠模式 ，這時候的 Factory 會變成這樣：&lt;/p&gt;
&lt;p&gt;Factory class：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;System&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;namespace&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;SimpleAbstractFactoryPattern&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Factory&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Product1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CreateProduct1&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;switch&lt;/span&gt;&lt;span&gt; (name)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Product1A&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;          &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Product1A&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Product1B&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;          &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Product1B&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;          &lt;/span&gt;&lt;span&gt;throw&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Exception&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Product2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CreateProduct2&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;switch&lt;/span&gt;&lt;span&gt; (name)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Product2A&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;          &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Product2A&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Product2B&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;          &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Product2B&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;          &lt;/span&gt;&lt;span&gt;throw&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Exception&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;這樣寫的話，之前才提到的 開放-封閉原則 的缺點不是又出現了嗎？&lt;/p&gt;
&lt;p&gt;如果增加了一個新的 ProductC 類別的話，就需要在 Factory 裡面增加新的 switch case 分支條件，不容易擴充的問題就出現了。&lt;/p&gt;
&lt;p&gt;這裡要介紹 C# 跟 JAVA 都有提供的一個機制，&lt;strong&gt;Reflection(反射)&lt;/strong&gt;，&lt;strong&gt;它可以直接依照 class 的名稱來實體化類別&lt;/strong&gt;，我們直接來看看用Reflection機制修改後的 Factory 程式碼吧：&lt;/p&gt;
&lt;p&gt;Factory class：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;System&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;System&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Reflection&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;namespace&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;SimpleAbstractFactoryPattern&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Factory&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 專案的namespace&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;readonly&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AssemblyName&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;SimpleAbstractFactoryPattern&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Product1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CreateProduct1&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 如果傳進來的name是&quot;Product1A&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 那className就等於SimpleAbstractFactoryPattern.Product1A&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;className&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; AssemblyName &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;.&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; name;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 這裡就是Reflection，直接依照className實體化具體類別&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;IProduct1&lt;/span&gt;&lt;span&gt;)Assembly.&lt;/span&gt;&lt;span&gt;Load&lt;/span&gt;&lt;span&gt;(AssemblyName).&lt;/span&gt;&lt;span&gt;CreateInstance&lt;/span&gt;&lt;span&gt;(className);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Product2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CreateProduct2&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;className&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; AssemblyName &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;.&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; name;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;IProduct2&lt;/span&gt;&lt;span&gt;)Assembly.&lt;/span&gt;&lt;span&gt;Load&lt;/span&gt;&lt;span&gt;(AssemblyName).&lt;/span&gt;&lt;span&gt;CreateInstance&lt;/span&gt;&lt;span&gt;(className);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Client：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;namespace&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;SimpleAbstractFactoryPattern&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Program&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Main&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;[] &lt;/span&gt;&lt;span&gt;args&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 生產產品A&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;Product1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;product1A&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; Factory.&lt;/span&gt;&lt;span&gt;CreateProduct1&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;Product1A&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;Product2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;product2A&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; Factory.&lt;/span&gt;&lt;span&gt;CreateProduct2&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;Product2A&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;product1A.&lt;/span&gt;&lt;span&gt;Operation&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;product2A.&lt;/span&gt;&lt;span&gt;Operation&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 生產產品B&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;Product1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;product1B&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; Factory.&lt;/span&gt;&lt;span&gt;CreateProduct1&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;Product1B&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;Product2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;product2B&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; Factory.&lt;/span&gt;&lt;span&gt;CreateProduct2&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;Product2B&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;product2B.&lt;/span&gt;&lt;span&gt;Operation&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;product1B.&lt;/span&gt;&lt;span&gt;Operation&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;看到了嗎!?這樣就算加入了其他 Product 子類別後，也不需要修改 Factory 的程式碼，只要在 CreateProduct() 方法內傳入要實體化的類別名稱就可以了，相當方便吧。&lt;/p&gt;
&lt;p&gt;範例檔案：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://sites.google.com/site/amo26site/download/FactoryPatternSample.rar?attredirects=0&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;FactoryPatternSample.rar&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;參考資料：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://zh.wikipedia.org/w/index.php?title=%E8%BD%AF%E4%BB%B6%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F&amp;amp;variant=zh-tw&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;軟體設計模式 — 維基百科&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://caterpillar.onlyfun.net/Gossip/DesignPattern/DesignPattern.htm&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;設計模式筆記&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.cnblogs.com/zhenyulu/category/6930.html?Show=All&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;First we try,then we trust — C#設計模式&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>Tech</category></item><item><title>swfobject — 網頁輕鬆嵌入 Flash</title><link>https://blog.amowu.com/posts/2009-07-23-swfobject-flash/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2009-07-23-swfobject-flash/</guid><description>Flash 在網頁上的應用已經越來越普遍了，未來一樣占有一席之位，但是從以前到現在，對於發佈 Flash 到網頁上常常碰到一些不必要的問題，例如 XP SP2 更新後，IE 瀏覽器上方會出現檔掉的訊息，或是 Flash…</description><pubDate>Thu, 23 Jul 2009 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Flash 在網頁上的應用已經越來越普遍了，未來一樣占有一席之位，但是從以前到現在，對於發佈 Flash 到網頁上常常碰到一些不必要的問題，例如 XP SP2 更新後，IE 瀏覽器上方會出現檔掉的訊息，或是 Flash 外框出現虛線，需要滑鼠點一下才能開始動作等…，而且使用者並未安裝 Flash Player 的情況下或版本太低都會無法順利的瀏覽。&lt;/p&gt;
&lt;p&gt;最近剛好看到一個不錯的東西，就是這次要介紹的 swfobject，一般我們要嵌入 swf 到 HTML 時，通常會寫的語法如下：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;object&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;FlashID&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;classid&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;clsid:D27CDB6E-AE6D-11cf-96B8-444553540000&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;width&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;300&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;height&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;120&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;param&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;movie&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;test.swf&quot;&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;param&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;quality&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;high&quot;&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;param&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;wmode&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;opaque&quot;&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;param&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;swfversion&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;6.0.65.0&quot;&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;object&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;上面的程式碼使用到 &lt;code&gt;&amp;lt;object&amp;gt;&lt;/code&gt; 標籤，早一點的甚至還要再寫 &lt;code&gt;&amp;lt;embed&amp;gt;&lt;/code&gt; 才能動作，因為 IE 跟其他瀏覽器的種種原因，使得這種寫法不但又臭又長，也不符合標準。&lt;/p&gt;
&lt;p&gt;那使用 swfobject 有什麼好處呢？我們繼續看下去吧。&lt;/p&gt;
&lt;p&gt;首先我們得先去 swfobject 的網頁 &lt;a href=&quot;http://code.google.com/p/swfobject/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;http://code.google.com/p/swfobject/&lt;/a&gt;，然後下載目前的最新版本 &lt;a href=&quot;http://swfobject.googlecode.com/files/swfobject_2_2.zip&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;swfobject_2_2.zip&lt;/a&gt; 。&lt;/p&gt;
&lt;p&gt;下載解壓縮後可以看到以下檔案：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;swfobject&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;├ src (原始碼資料夾)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;│  ├ expressInstall.as (flash player版本升級原始碼)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;│  ├ expressInstall.fla (flash player版本升級原始碼)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;│  └ swfobject.js (swfobject javascript)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;│&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;├ expressInstall.swf (flash player版本升級程式)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;├ index.html (HTML範例檔)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;├ index_dynamic.html (HTML範例檔)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;├ swfobject.js (swfobject javascript)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;└ test.swf (測試用swf檔)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;上面灰色的檔案是比較不重要的範例檔，我們只需要 swfobject.js 跟 expressInstall.swf 就可以了。&lt;/p&gt;
&lt;p&gt;在這裡先解釋一下，expressInstall.swf 是用來幫助使用者升級 flash player 用的 swf 檔，如果你發佈了一個版本為 10 的 swf 到網頁，而使用者的瀏覽器版本只到 9 或更低，網頁就會自動載入 expressInstall.swf 協助使用者升級 player 的版本。&lt;/p&gt;
&lt;p&gt;下面我們開始使用 swfobject，先建立一個資料夾，暫時命名為 MySwfobject，然後在資料夾裡在建立一個 js 資料夾，我們把 swfobject.js 複製到 js 資料夾內，然後將 expressInstall.swf 複製到 MySwfobject 資料夾內，再來我們需要一個測試用的 flash，就把剛剛下載的 test.swf 一樣複製到 MySwfobject 資料夾內，最後建立一個空白的 HTML 檔 index.html，程式碼如下：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;!&lt;/span&gt;&lt;span&gt;DOCTYPE&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt; PUBLIC &quot;-//W3C//DTD XHTML 1.0 Transitional//EN&quot; &quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&quot;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;xmlns&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;http://www.w3.org/1999/xhtml&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;head&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;&amp;gt;MySwfobject&amp;lt;/&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;meta&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;http-equiv&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;Content-Type&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;content&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;text/html; charset=utf-8&quot;&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;head&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;這時我們的目錄應該如下的結構：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;MySwfobject&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;├ js&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;│ └swfobject.js&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;│&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;├ expressInstall.swf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;├ index.html&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;└ test.swf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;我們開始把 test.swf 嵌入到 index.html，先在 &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; 標籤內導入 swfobject.js：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;src&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;js/swfobject.js&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;text/javascript&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;然後在底下加上這幾行javascript:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;text/javascript&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;swfobject.embedSWF(&quot;test.swf&quot;, &quot;mySwfobjectDiv&quot;, &quot;300&quot;, &quot;120&quot;, &quot;9.0.0&quot;, &quot;expressInstall.swf&quot;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;最後在 &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; 內加入 &lt;code&gt;id=&quot;mySwfobjectDiv&quot;&lt;/code&gt; 的 &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; 標籤：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;mySwfobjectDiv&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;這時候用你的瀏覽器開啟 index.html 就會發現 test.swf 成功被嵌入了。&lt;/p&gt;
&lt;p&gt;大家可以發現程式碼非常的簡短，而且不需要一堆 &lt;code&gt;&amp;lt;object&amp;gt;&amp;lt;embed&amp;gt;&lt;/code&gt; 標籤，只需要一個 &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; 就可以輕鬆嵌入 Flash，其中的核心就是 &lt;code&gt;swfobject.embedSWF(&quot;test.swf&quot;, &quot;mySwfobjectDiv&quot;, &quot;300&quot;, &quot;120&quot;, &quot;9.0.0&quot;, &quot;expressInstall.swf&quot;);&lt;/code&gt; 這行，這裡我們介紹一下 &lt;code&gt;swfobject.embedSWF&lt;/code&gt; 的幾個參數：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;swfobject.&lt;/span&gt;&lt;span&gt;embedSWF&lt;/span&gt;&lt;span&gt;(swfUrl, id, width, height, version, expressInstallSwfurl, flashvars, params, attributes, callbackFn)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;swfUrl&lt;/code&gt; 要嵌入的 swf 檔的位置（例如在這裡我們是 test.swf）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;id&lt;/code&gt; 要嵌入在哪的 HTML 標籤 id（在這裡我們是嵌入在 &lt;code&gt;&amp;lt;div id=&quot;mySwfobjectDiv&quot;&amp;gt;&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;width&lt;/code&gt; Flash 的寬&lt;/li&gt;
&lt;li&gt;&lt;code&gt;height&lt;/code&gt; Flash 的高&lt;/li&gt;
&lt;li&gt;&lt;code&gt;version&lt;/code&gt; Flash Player 的版本（可直接輸入整數 ex: 9 or 10&lt;/li&gt;
&lt;li&gt;&lt;code&gt;expressInstallSwfurl&lt;/code&gt; expressInstall.swf 的位置&lt;/li&gt;
&lt;li&gt;&lt;code&gt;flashvars&lt;/code&gt; 要傳給 Flash 的變數&lt;/li&gt;
&lt;li&gt;&lt;code&gt;params&lt;/code&gt; Flash 的參數 ex: menu,wmode,quality 等…&lt;/li&gt;
&lt;li&gt;&lt;code&gt;attributes&lt;/code&gt; Flash 本身的一些屬性 ex: id,name…&lt;/li&gt;
&lt;li&gt;&lt;code&gt;callbackFn&lt;/code&gt; 要 callback 給 javascript 的 function&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;最後這裡附上加了 flashvars, params 跟 attributes 的 index.html 程式碼：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;!&lt;/span&gt;&lt;span&gt;DOCTYPE&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt; PUBLIC &quot;-//W3C//DTD XHTML 1.0 Transitional//EN&quot; &quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&quot;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;xmlns&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;http://www.w3.org/1999/xhtml&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;head&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;&amp;gt;MySwfobject&amp;lt;/&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;meta&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;http-equiv&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;Content-Type&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;content&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;text/html; charset=utf-8&quot;&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;src&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;js/swfobject.js&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;text/javascript&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;text/javascript&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;var flashvars = {};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;var params = {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;menu: &quot;false&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;var attributes = {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;id:&quot;MySwfobject&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;swfobject.embedSWF(&quot;test.swf&quot;, &quot;mySwfobjectDiv&quot;, &quot;300&quot;, &quot;120&quot;, &quot;9.0.0&quot;, &quot;expressInstall.swf&quot;, flashvars, params, attributes);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;head&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;mySwfobjectDiv&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;http://www.adobe.com/go/getflashplayer&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;請安裝Flash Player&amp;lt;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;我們新增了關閉 player menu 的參數給 swfobject，然後將 id 命名為 MySwfobject，並且在 mySwfobjectDiv 的 div 標籤內加入一個 Flash Plyer 的安裝連結，如果使用者的瀏覽器沒有安裝 Flash Player，swfobject 就不會將 test.swf 嵌入到 mySwfobjectDiv，而是直接顯示這行連結通知使用者安裝。&lt;/p&gt;
&lt;p&gt;更多進階的使用文件可以參考 &lt;a href=&quot;http://code.google.com/p/swfobject/wiki/documentation&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;swfobject 的 documentation&lt;/a&gt; 。&lt;/p&gt;
&lt;p&gt;範例程式：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://sites.google.com/site/amo26site/download/SwfobjectSample.rar?attredirects=0&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;SwfobjectSample.rar&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;參考資料：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://code.google.com/p/swfobject/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;swfobject&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://fanchie.blogspot.com/2008/04/swfobject-swfobject-20-flash.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;番茄腦袋&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>Tech</category></item><item><title>Visual Studio Code Snippets</title><link>https://blog.amowu.com/posts/2009-07-16-visual-studio-code-snippets/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2009-07-16-visual-studio-code-snippets/</guid><description>最近在看 聖殿祭司寫的 ASP.NET 3.5 專家技術手冊 ，上面提到 Visual Studio 2005 之後，提供一個很不錯的功能叫做 Code Snippets（程式碼片段），這是一個可以快速產生常用程式碼的好東西。</description><pubDate>Thu, 16 Jul 2009 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;最近在看 &lt;a href=&quot;http://blog.sina.com.tw/dotnet/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;聖殿祭司&lt;/a&gt;寫的 &lt;a href=&quot;http://findbook.tw/book/9789861816524/basic&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;ASP.NET 3.5 專家技術手冊&lt;/a&gt; ，上面提到 Visual Studio 2005 之後，提供一個很不錯的功能叫做 Code Snippets（程式碼片段），這是一個可以快速產生常用程式碼的好東西。&lt;/p&gt;
&lt;p&gt;使用 Code Snippets 的好處是當我們每次要打一些常用的程式碼語法時，例如 if、do while、for 等等…就不必自己打一遍了，一方面平常輸入這些老是反覆再寫的程式碼時，效率不佳，而且另一方面有時候還會發生打錯或是想不起來怎麼寫的情況。&lt;/p&gt;
&lt;p&gt;但是 C# 語言內建的程式碼片段很少，而且都是一些無關痛癢的基本語法，所以網路上有很多人將一些常用的 C# 程式碼片段寫成 VS 的擴充套件提供大家下載：&lt;/p&gt;
&lt;p&gt;&lt;em&gt;微軟&lt;/em&gt;
&lt;a href=&quot;http://msdn.microsoft.com/en-us/vs2005/aa718338.aspx&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;em&gt;http://msdn.microsoft.com/en-us/vs2005/aa718338.aspx&lt;/em&gt;&lt;/a&gt;* (英文)*
&lt;a href=&quot;http://msdn.microsoft.com/zh-tw/vs2005/aa718338.aspx&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;em&gt;http://msdn.microsoft.com/zh-tw/vs2005/aa718338.aspx&lt;/em&gt;&lt;/a&gt;* (繁體中文)*&lt;/p&gt;
&lt;p&gt;&lt;em&gt;CodePlex&lt;/em&gt;
&lt;a href=&quot;http://www.codeplex.com/site/search?projectSearchText=Snippet&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;em&gt;http://www.codeplex.com/site/search?projectSearchText=Snippet&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;gotCODESNIPPETS.Net&lt;/em&gt;
&lt;a href=&quot;http://gotcodesnippets.com/default.aspx&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;em&gt;http://gotcodesnippets.com/default.aspx&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;這邊我用微軟提供的程式碼片段作範例，首先要先去 MSDN 網站下載。&lt;/p&gt;
&lt;p&gt;下載完成後點擊 Code_Snippets.msi 進入安裝，安裝路徑為「Microsoft Visual Studio位置\VC#\Snippets\1033\自訂命名」，例如「C:\Program Files\Microsoft Visual Studio 9.0\VC#\Snippets\1033\C# Snippets」。&lt;/p&gt;
&lt;p&gt;接下來開啟 VS2005 或 VS2008，點選工具 -&amp;gt; 程式碼片段管理員。&lt;/p&gt;
&lt;p&gt;開啟程式碼片段管理員後，點擊加入 -&amp;gt; 選擇剛剛安裝的目錄「C:\Program Files\Microsoft Visual Studio 9.0\VC#\Snippets\1033\C# Snippets」-&amp;gt; 確定，完成後就可以看到 C# Snippets 目錄被加入到清單內了。&lt;/p&gt;
&lt;p&gt;成功加入後我們馬上來試試，在要加入程式碼片段的地方右鍵 -&amp;gt; 插入程式碼片段 -&amp;gt; C# Snippets -&amp;gt; database -&amp;gt; Create a local SQL Connection to SQL Server(or Express)，然後在下一行繼續插入程式碼片段 -&amp;gt; C# Snippets -&amp;gt; database -&amp;gt; Create a Parameterized SELECT Command，這樣就完成一個資料庫的連接跟查詢功能，程式碼都幫我們產生好了，只需要改改變數或字串就可以了，非常方便吧!!!&lt;/p&gt;
&lt;p&gt;除了下載安裝別人提供的套件外，當然也可以寫一份屬於個人的程式碼片段擴充套件，畢竟平常在寫哪些程式碼，只有自己是最清楚的，可以去 &lt;a href=&quot;http://www.codeplex.com/SnippetEditor&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;微軟 Snippet Editor&lt;/a&gt;或 &lt;a href=&quot;http://www.codeplex.com/snippy&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Snippy — Visual Studio code Snippet Editor&lt;/a&gt; 下載編輯工具，詳細的介紹各位可以參考 &lt;a href=&quot;http://findbook.tw/book/9789861816524/basic&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;聖殿祭司的ASP.NET 3.5 專家技術手冊I-使用C#&lt;/a&gt; 這本書，內容適合中高階經驗的人，值得一看!!!&lt;/p&gt;</content:encoded><category>Tech</category></item><item><title>AS3 如何複製 DisplayObject</title><link>https://blog.amowu.com/posts/2009-06-30-as3-displayobject/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2009-06-30-as3-displayobject/</guid><description>最近正在寫一個 FLASH 的相片大頭貼截取程式，碰到了一個複製 MovieClip 的問題，AS3 已經沒有 AS2 的 duplicateMovie() 方法可以用，所以我找了一些解決方法分享上來。</description><pubDate>Tue, 30 Jun 2009 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;最近正在寫一個 FLASH 的相片大頭貼截取程式，碰到了一個複製 MovieClip 的問題，AS3 已經沒有 AS2 的 &lt;code&gt;duplicateMovie()&lt;/code&gt; 方法可以用，所以我找了一些解決方法分享上來。&lt;/p&gt;
&lt;p&gt;第一種方式是比較好的解決方法，是 PTT 的 CJCAT 大提供的，可以直接複製一個相同的 Class。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;假如場景上面有一個 clip_mc，它的 class 是 MyClip，在不使用 &lt;em&gt;&lt;code&gt;*new MyClip()*&lt;/code&gt;&lt;/em&gt; 的前提下，以下的 code 可以生出一個新的 MyClip 物件。&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;// 先抓到MyClip的constructor&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; mcClass&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;Class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Object&lt;/span&gt;&lt;span&gt;(clip_mc).&lt;/span&gt;&lt;span&gt;constructor&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;// 這樣就生出一個新的MyClip物件了&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; clip2_mc&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;DisplayObject&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mcClass&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;// 加入到舞台後就成功了&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;addChild&lt;/span&gt;&lt;span&gt;(clip2_mc);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;不過上面這個方法我試不出來，不知道是不是哪裡出錯了，所以又找了第二種方法， &lt;a href=&quot;http://www.kirupa.com/forum/showthread.php?t=283258&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Copying a Sprite using BitmapData&lt;/a&gt; ，這個方法可以將 Sprite 的畫面複製到另一個 DisplayObject 上，因為我只是需要複製一個靜態的圖，所以可以不需要使用第一種複製 Class 的方式。&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;// 先實體一個clip_mc大小的BitmapData&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; myBitmapData&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;BitmapData&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;BitmapData&lt;/span&gt;&lt;span&gt;(clip_mc.width, clip_mc.height);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;// 然後繪製一個相同的clip_mc&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;myBitmapData.&lt;/span&gt;&lt;span&gt;draw&lt;/span&gt;&lt;span&gt;(clip_mc);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;// 實體一個複製的Bitmap&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; clip2_mc&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;Bitmap&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Bitmap&lt;/span&gt;&lt;span&gt;(myBitmapData);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;// 加入到舞台後就成功了&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;addChild&lt;/span&gt;&lt;span&gt;(clip2_mc);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;參考資料：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://www.ptt.cc/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;批踢踢實業坊&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.kirupa.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;KIRUPA.COM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://help.adobe.com/zh_TW/AS3LCR/Flash_10.0/index.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;ActionScript 3.0 語言和組件參考&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>Tech</category></item><item><title>XNA Simple Animation</title><link>https://blog.amowu.com/posts/2009-06-12-xna-simple-animation/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2009-06-12-xna-simple-animation/</guid><description>因為我的大學專題是寫一款四人網路連線的格鬥遊戲，所以對 XNA 這套微軟提供的遊戲開發平台有點研究，之前在痞客邦寫網誌的時候有寫過幾篇這個作品的開發情況，後來因為實在太忙了所以沒有繼續介紹下去，現在這個遊戲已經在去年完成了，也得到不錯的成績，有機會我會 PO…</description><pubDate>Fri, 12 Jun 2009 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;因為我的大學專題是寫一款四人網路連線的格鬥遊戲，所以對 &lt;a href=&quot;http://zh.wikipedia.org/w/index.php?title=XNA&amp;amp;variant=zh-tw&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;XNA&lt;/a&gt; 這套微軟提供的遊戲開發平台有點研究，之前在痞客邦寫網誌的時候有寫過幾篇這個作品的開發情況，後來因為實在太忙了所以沒有繼續介紹下去，現在這個遊戲已經在去年完成了，也得到不錯的成績，有機會我會 PO 上來分享一些製作心得。&lt;/p&gt;
&lt;p&gt;在網誌搬到 Blogger 之後一直沒有機會寫一些跟 XNA 有關的文章，我打算介紹一些 &lt;a href=&quot;http://creators.xna.com/en-US/education/catalog/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;XNA Creators Club&lt;/a&gt; 教學範例中所使用到的開發技術，國內介紹 XNA 的文章不多，有興趣用 XNA 開發遊戲的人，推薦可以到 &lt;a href=&quot;http://www.dotblogs.com.tw/Team.aspx?GroupID=507&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;點部落&lt;/a&gt; 去看一些不錯的文章。&lt;/p&gt;
&lt;p&gt;這一篇我想先介紹 XNA 如何使用 Model、ModelBone 和 ModelMesh 等技術去載入一個 3D 模型，然後控制一些簡單的 3D 動畫。&lt;/p&gt;
&lt;p&gt;首先必須先準備一個 3D 模型，XNA 預設的類型有 &lt;code&gt;.x&lt;/code&gt; 跟 &lt;code&gt;.fbx&lt;/code&gt; 兩種模型檔，這裡我們先用微軟提供的坦克車模型（ &lt;a href=&quot;http://sites.google.com/site/amo26site/download/TankModel.part1.rar?attredirects=0&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;part1&lt;/a&gt;）（ &lt;a href=&quot;http://sites.google.com/site/amo26site/download/TankModel.part2.rar?attredirects=0&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;part2&lt;/a&gt; ）來作示範。&lt;/p&gt;
&lt;p&gt;一開始先建立一個 XNA 的專案。&lt;/p&gt;
&lt;p&gt;將模型檔加入到 Content 資料夾內。&lt;/p&gt;
&lt;p&gt;新增一個 &lt;code&gt;Tank.cs&lt;/code&gt; ：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Microsoft&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Xna&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Framework&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Microsoft&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Xna&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Framework&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Content&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Microsoft&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Xna&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Framework&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Graphics&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;namespace&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;SimpleAnimationSample&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// 能讓零件動作的坦克車模型類別&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Tank&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// 坦克車模型&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Model&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tankModel&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// 坦克所有組件的平移矩陣&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Matrix&lt;/span&gt;&lt;span&gt;[] &lt;/span&gt;&lt;span&gt;boneTransforms&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// 載入坦克車資源&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Load&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;ContentManager&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;content&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 從ContentManager載入坦克車模型.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.tankModel &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; content.&lt;/span&gt;&lt;span&gt;Load&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Model&lt;/span&gt;&lt;span&gt;&amp;gt;(&lt;/span&gt;&lt;span&gt;&quot;tank&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 配置所有組件的平移矩陣&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.boneTransforms &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Matrix&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.tankModel.Bones.Count];&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// 坦克車的繪圖機制&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;param&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;world&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;世界矩陣&amp;lt;/&lt;/span&gt;&lt;span&gt;param&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;param&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;view&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;觀察矩陣&amp;lt;/&lt;/span&gt;&lt;span&gt;param&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;param&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;projection&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;投影矩陣&amp;lt;/&lt;/span&gt;&lt;span&gt;param&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Draw&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Matrix&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;world&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;Matrix&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;view&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;Matrix&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;projection&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 設定坦克車最上層的平移矩陣為世界矩陣&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.tankModel.Root.Transform &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; world;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 更新所有組件的平移矩陣&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.tankModel.&lt;/span&gt;&lt;span&gt;CopyAbsoluteBoneTransformsTo&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.boneTransforms);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 繪製模型&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;foreach&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;ModelMesh&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mesh&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.tankModel.Meshes)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;foreach&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;BasicEffect&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;effect&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; mesh.Effects)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;effect.World &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.boneTransforms[mesh.ParentBone.Index];&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;effect.View &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; view;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;effect.Projection &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; projection;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;effect.&lt;/span&gt;&lt;span&gt;EnableDefaultLighting&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;mesh.&lt;/span&gt;&lt;span&gt;Draw&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;修改遊戲主程式 &lt;code&gt;Game1.cs&lt;/code&gt;（這裡我命名為 &lt;code&gt;SimpleAnimation.cs&lt;/code&gt; ）：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Microsoft&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Xna&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Framework&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Microsoft&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Xna&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Framework&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Content&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Microsoft&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Xna&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Framework&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Graphics&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Microsoft&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Xna&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Framework&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Input&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;namespace&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;SimpleAnimationSample&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// 控制坦克車模型動畫的範例程式&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;SimpleAnimation&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;Microsoft&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Xna&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Framework&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Game&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;GraphicsDeviceManager&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;graphics&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Tank&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tank&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;SimpleAnimation&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.graphics &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;GraphicsDeviceManager&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.graphics.PreferredBackBufferWidth &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;600&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.graphics.PreferredBackBufferHeight &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;450&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.Content.RootDirectory &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Content&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// 初始化遊戲內容&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;protected&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;override&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Initialize&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.tank &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Tank&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;base&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Initialize&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// 載入遊戲資源&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;protected&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;override&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;LoadContent&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.tank.&lt;/span&gt;&lt;span&gt;Load&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.Content);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// 遊戲繪圖更新&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;param&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;gameTime&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;上一次繪圖後經過的時間&amp;lt;/&lt;/span&gt;&lt;span&gt;param&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;protected&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;override&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Draw&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;GameTime&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;gameTime&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.GraphicsDevice.&lt;/span&gt;&lt;span&gt;Clear&lt;/span&gt;&lt;span&gt;(Color.CornflowerBlue);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 視窗畫面&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;Viewport&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;viewport&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.GraphicsDevice.Viewport;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 畫面長寬比&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;float&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;aspectRatio&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;float&lt;/span&gt;&lt;span&gt;)viewport.Width &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;float&lt;/span&gt;&lt;span&gt;)viewport.Height;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 世界矩陣(縮放矩陣, 旋轉矩陣, 平移矩陣)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;Matrix&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;world&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; Matrix.&lt;/span&gt;&lt;span&gt;CreateScale&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1.0f&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; Matrix.&lt;/span&gt;&lt;span&gt;CreateRotationY&lt;/span&gt;&lt;span&gt;(MathHelper.PiOver4) &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; Matrix.&lt;/span&gt;&lt;span&gt;CreateTranslation&lt;/span&gt;&lt;span&gt;(Vector3.Zero);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 觀察矩陣(攝影機座標, 攝影機焦點座標, 攝影機上方的向量)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;Matrix&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;view&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; Matrix.&lt;/span&gt;&lt;span&gt;CreateLookAt&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Vector3&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1000&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;600&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;), &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Vector3&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;), Vector3.Up);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 投影矩陣(畫面視角呈現弧度, 畫面長寬比, 近景值, 遠景值)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;Matrix&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;projection&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; Matrix.&lt;/span&gt;&lt;span&gt;CreatePerspectiveFieldOfView&lt;/span&gt;&lt;span&gt;(MathHelper.PiOver4, aspectRatio, &lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;10000&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.tank.&lt;/span&gt;&lt;span&gt;Draw&lt;/span&gt;&lt;span&gt;(world, view, projection);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;base&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Draw&lt;/span&gt;&lt;span&gt;(gameTime);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;上面我們完成了基本的坦克車模型載入，接下來要讓它的每個組件都能動作。&lt;/p&gt;
&lt;p&gt;如果要讓模型內的組件獨立動作的話，會需要用到 ModelBone 這個類別，這裡我們先示範讓坦克車的&lt;strong&gt;左前輪&lt;/strong&gt;轉動。&lt;/p&gt;
&lt;p&gt;要讓左前輪動就必須知道他在模型內的 Mesh 命名，如果不知道當初建模時的命名，可以照下圖的步驟找出左前輪的名稱。&lt;/p&gt;
&lt;p&gt;找到左前輪的命名 &lt;code&gt;&quot;l_front_wheel_geo&quot;&lt;/code&gt; 後，就可以開始用程式去控制它了，修改 &lt;code&gt;Tank.cs&lt;/code&gt; ：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Microsoft&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Xna&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Framework&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Microsoft&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Xna&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Framework&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Content&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Microsoft&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Xna&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Framework&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Graphics&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;namespace&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;SimpleAnimationSample&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// 能讓零件動作的坦克車模型類別&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Tank&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// 坦克車模型&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Model&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tankModel&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 坦克左前輪組件&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ModelBone&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;leftFrontWheelBone&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 坦克左前輪的平移矩陣&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Matrix&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;leftFrontWheelTransform&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// 坦克所有組件的平移矩陣&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Matrix&lt;/span&gt;&lt;span&gt;[] &lt;/span&gt;&lt;span&gt;boneTransforms&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 輪子的旋轉值&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;float&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;wheelRotationValue&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// Get或Set輪子的旋轉值.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;float&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;WheelRotation&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; wheelRotationValue; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt; { wheelRotationValue &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; value; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// 載入坦克車資源&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Load&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;ContentManager&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;content&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 從ContentManager載入坦克車模型.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.tankModel &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; content.&lt;/span&gt;&lt;span&gt;Load&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Model&lt;/span&gt;&lt;span&gt;&amp;gt;(&lt;/span&gt;&lt;span&gt;&quot;tank&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 參照坦克左前輪組件&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.leftFrontWheelBone &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.tankModel.Bones[&lt;/span&gt;&lt;span&gt;&quot;l_front_wheel_geo&quot;&lt;/span&gt;&lt;span&gt;];&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 取得左前輪平移矩陣&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.leftFrontWheelTransform &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.leftFrontWheelBone.Transform;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 配置所有組件的平移矩陣&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.boneTransforms &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Matrix&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.tankModel.Bones.Count];&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// 坦克車的繪圖機制&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;param&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;world&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;世界矩陣&amp;lt;/&lt;/span&gt;&lt;span&gt;param&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;param&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;view&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;觀察矩陣&amp;lt;/&lt;/span&gt;&lt;span&gt;param&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;param&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;projection&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;投影矩陣&amp;lt;/&lt;/span&gt;&lt;span&gt;param&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Draw&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Matrix&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;world&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;Matrix&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;view&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;Matrix&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;projection&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 設定坦克車最上層的平移矩陣為世界矩陣&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.tankModel.Root.Transform &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; world;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 輪子繞著X軸旋轉&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;Matrix&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;wheelRotation&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; Matrix.&lt;/span&gt;&lt;span&gt;CreateRotationX&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.wheelRotationValue);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 左前輪的平移矩陣 = 旋轉矩陣 * 平移矩陣&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.leftFrontWheelBone.Transform &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; wheelRotation &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.leftFrontWheelTransform;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 更新所有組件的平移矩陣&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.tankModel.&lt;/span&gt;&lt;span&gt;CopyAbsoluteBoneTransformsTo&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.boneTransforms);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 繪製模型&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;foreach&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;ModelMesh&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mesh&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.tankModel.Meshes)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;foreach&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;BasicEffect&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;effect&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; mesh.Effects)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;effect.World &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.boneTransforms[mesh.ParentBone.Index];&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;effect.View &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; view;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;effect.Projection &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; projection;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;effect.&lt;/span&gt;&lt;span&gt;EnableDefaultLighting&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;mesh.&lt;/span&gt;&lt;span&gt;Draw&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;接著馬上更新遊戲主程式，讓坦克的左前輪旋轉，修改 &lt;code&gt;Game1.cs&lt;/code&gt;（ &lt;code&gt;SimpleAnimation.cs&lt;/code&gt; ）：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Microsoft&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Xna&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Framework&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Microsoft&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Xna&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Framework&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Content&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Microsoft&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Xna&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Framework&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Graphics&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Microsoft&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Xna&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Framework&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Input&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;namespace&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;SimpleAnimationSample&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// 控制坦克車模型動畫的範例程式&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;SimpleAnimation&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;Microsoft&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Xna&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Framework&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Game&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;GraphicsDeviceManager&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;graphics&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Tank&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tank&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;SimpleAnimation&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.graphics &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;GraphicsDeviceManager&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.graphics.PreferredBackBufferWidth &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;600&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.graphics.PreferredBackBufferHeight &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;450&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.Content.RootDirectory &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Content&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// 初始化遊戲內容&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;protected&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;override&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Initialize&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.tank &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Tank&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;base&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Initialize&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// 載入遊戲資源&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;protected&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;override&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;LoadContent&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.tank.&lt;/span&gt;&lt;span&gt;Load&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.Content);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// 遊戲邏輯更新&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;param&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;gameTime&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;上一次更新後經過的時間&amp;lt;/&lt;/span&gt;&lt;span&gt;param&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;protected&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;override&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Update&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;GameTime&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;gameTime&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 上次更新後到現在經過幾秒&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;float&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;time&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;float&lt;/span&gt;&lt;span&gt;)gameTime.TotalGameTime.TotalSeconds;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 更新坦克輪子的旋轉值&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.tank.WheelRotation &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; time &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;base&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Update&lt;/span&gt;&lt;span&gt;(gameTime);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// 遊戲繪圖更新&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;param&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;gameTime&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;上一次繪圖後經過的時間&amp;lt;/&lt;/span&gt;&lt;span&gt;param&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;protected&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;override&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Draw&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;GameTime&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;gameTime&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.GraphicsDevice.&lt;/span&gt;&lt;span&gt;Clear&lt;/span&gt;&lt;span&gt;(Color.CornflowerBlue);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 視窗畫面&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;Viewport&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;viewport&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.GraphicsDevice.Viewport;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 畫面長寬比&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;float&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;aspectRatio&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;float&lt;/span&gt;&lt;span&gt;)viewport.Width &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;float&lt;/span&gt;&lt;span&gt;)viewport.Height;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 世界矩陣(縮放矩陣, 旋轉矩陣, 平移矩陣)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;Matrix&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;world&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; Matrix.&lt;/span&gt;&lt;span&gt;CreateScale&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1.0f&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; Matrix.&lt;/span&gt;&lt;span&gt;CreateRotationY&lt;/span&gt;&lt;span&gt;(MathHelper.PiOver4) &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; Matrix.&lt;/span&gt;&lt;span&gt;CreateTranslation&lt;/span&gt;&lt;span&gt;(Vector3.Zero);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 觀察矩陣(攝影機座標, 攝影機焦點座標, 攝影機上方的向量)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;Matrix&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;view&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; Matrix.&lt;/span&gt;&lt;span&gt;CreateLookAt&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Vector3&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1000&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;600&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;), &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Vector3&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;), Vector3.Up);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 投影矩陣(畫面視角呈現弧度, 畫面長寬比, 近景值, 遠景值)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;Matrix&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;projection&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; Matrix.&lt;/span&gt;&lt;span&gt;CreatePerspectiveFieldOfView&lt;/span&gt;&lt;span&gt;(MathHelper.PiOver4, aspectRatio, &lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;10000&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.tank.&lt;/span&gt;&lt;span&gt;Draw&lt;/span&gt;&lt;span&gt;(world, view, projection);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;base&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Draw&lt;/span&gt;&lt;span&gt;(gameTime);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;沒意外的話坦克車的左前輪就會開始旋轉了，接著其他的組件也只要按照上面的步驟加入程式碼，就能完成一部很酷的坦克車動畫了，手邊如果有其他模型檔的話也可以玩玩看~&lt;/p&gt;
&lt;p&gt;完整範例程式：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SimpleAnimationSample.rar（&lt;a href=&quot;http://sites.google.com/site/amo26site/download/SimpleAnimationSample.part1.rar?attredirects=0&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;part1&lt;/a&gt;）（&lt;a href=&quot;http://sites.google.com/site/amo26site/download/SimpleAnimationSample.part2.rar?attredirects=0&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;part2&lt;/a&gt;）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;參考資料：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://creators.xna.com/en-US/sample/simpleanimation&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;XNA Creator Club Online — simple animation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://msdn.microsoft.com/en-us/library/bb203940.aspx&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;MSDN&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.dotblogs.com.tw/help/archive/2009/01/14/6810.aspx&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;點部落 — XNA顯示3D模型&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.dotblogs.com.tw/help/archive/2009/01/09/6711.aspx&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;點部落 — XNA3D座標系統&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.dotblogs.com.tw/sonic10690/archive/2009/01/02/6598.aspx&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;點部落 — XNA3D矩陣運算&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.dotblogs.com.tw/sonic10690/archive/2009/01/03/6602.aspx&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;點部落 — XNA攝影機&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.dotblogs.com.tw/sonic10690/archive/2009/01/04/6617.aspx&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;點部落 — XNA投影轉換&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>Tech</category></item><item><title>SortedDictionary 如何自訂排序</title><link>https://blog.amowu.com/posts/2009-05-22-sorteddictionary/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2009-05-22-sorteddictionary/</guid><description>之前再寫 資料檢索這堂課的作業時，碰到一個 SortedDictionary 排序上的問題，因為我在寫 反轉索引法的過程中有使用到這個集合，其中 Key 的部分我是用來存放 TF-IDF 演算法求出來的 weight score，這個值我是以 double…</description><pubDate>Fri, 22 May 2009 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;之前再寫 &lt;a href=&quot;http://zh.wikipedia.org/w/index.php?title=%E8%B3%87%E8%A8%8A%E6%AA%A2%E7%B4%A2&amp;amp;variant=zh-tw&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;資料檢索&lt;/a&gt;這堂課的作業時，碰到一個 &lt;a href=&quot;http://msdn.microsoft.com/en-us/library/f7fta44c.aspx&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;SortedDictionary&lt;/a&gt; 排序上的問題，因為我在寫 &lt;a href=&quot;http://en.wikipedia.org/wiki/Inverted_index&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;反轉索引法&lt;/a&gt;的過程中有使用到這個集合，其中 Key 的部分我是用來存放 &lt;a href=&quot;http://en.wikipedia.org/wiki/Tf%E2%80%93idf&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;TF-IDF&lt;/a&gt; 演算法求出來的 weight score，這個值我是以 double 的型態來存放，不過因為 SortedDictionary 的排序方式是由小到大，也就是以升冪排序的方式幫你排序好了，但是我希望它能夠由大到小排序，因為作業的顯示結果要求 weight score 越高的結果要排越前面，所以我上 MSDN 查了一下 SortedDictionary 的資料後，本來看到它有一個 &lt;a href=&quot;http://msdn.microsoft.com/en-us/library/bb358497.aspx&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Reverse&lt;/a&gt; 的方法，不過後來研究了好久還是不會用，最後就上 PTT 的 C_Sharp 板去求助，經過 Cloud 大大的指導後終於解決這個問題了。&lt;/p&gt;
&lt;p&gt;利用 SortedDictionary 建構式的一個多載，IComparer 介面就可以實現自訂排序方式，來看看實作程式碼吧。&lt;/p&gt;
&lt;p&gt;我們先看一下一開始 SortedDictionary 為我們排序的結果：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;System&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;System&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Collections&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Generic&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;namespace&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;SortedDictionarySample&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Program&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Main&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;[] &lt;/span&gt;&lt;span&gt;args&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;SortedDictionary&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;&amp;gt; &lt;/span&gt;&lt;span&gt;sortedDictionary&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;SortedDictionary&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;&amp;gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;sortedDictionary.&lt;/span&gt;&lt;span&gt;Add&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;五&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;sortedDictionary.&lt;/span&gt;&lt;span&gt;Add&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;4&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;四&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;sortedDictionary.&lt;/span&gt;&lt;span&gt;Add&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;6&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;六&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;sortedDictionary.&lt;/span&gt;&lt;span&gt;Add&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;二&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;sortedDictionary.&lt;/span&gt;&lt;span&gt;Add&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;8&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;八&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;foreach&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;KeyValuePair&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;&amp;gt; &lt;/span&gt;&lt;span&gt;kvp&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; sortedDictionary)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Console.&lt;/span&gt;&lt;span&gt;WriteLine&lt;/span&gt;&lt;span&gt;(kvp.Value);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;上面是一開始用 SortedDictionary 排序的結果，可以看出他是由小到大的升冪排序。
接下來就是本文的重點，自己定義 SortedDictionary 排序方式的實作方法：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;System&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;System&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Collections&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Generic&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;namespace&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;SortedDictionarySample&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;///&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// 遞減整數用的比較器，實作IComparer介面&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;///&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;IntegerDecreaseComparer&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;IComparer&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;///&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// Compares two integer and returns a value indicating whether one is less than, equal to, or greater than the other.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;///&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// The first integer to compare.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// The second integer to compare.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;///&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// return Less than zero if x is less than y,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// return Zero if x equals y,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// return Greater than zero if x is greater than y.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;///&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Compare&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;x&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;y&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (x &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt; y)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Program&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Main&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;[] &lt;/span&gt;&lt;span&gt;args&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;SortedDictionary&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;&amp;gt; &lt;/span&gt;&lt;span&gt;sortedDictionary&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;SortedDictionary&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;&amp;gt;(&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;IntegerDecreaseComparer&lt;/span&gt;&lt;span&gt;());&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;sortedDictionary.&lt;/span&gt;&lt;span&gt;Add&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;五&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;sortedDictionary.&lt;/span&gt;&lt;span&gt;Add&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;4&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;四&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;sortedDictionary.&lt;/span&gt;&lt;span&gt;Add&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;6&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;六&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;sortedDictionary.&lt;/span&gt;&lt;span&gt;Add&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;二&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;sortedDictionary.&lt;/span&gt;&lt;span&gt;Add&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;8&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;八&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;foreach&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;KeyValuePair&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;&amp;gt; &lt;/span&gt;&lt;span&gt;kvp&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; sortedDictionary)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Console.&lt;/span&gt;&lt;span&gt;WriteLine&lt;/span&gt;&lt;span&gt;(kvp.Value);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;範例程式：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://sites.google.com/site/amo26site/download/SortedDictionarySample.rar?attredirects=0&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;SortedDictionarySample.rar&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;參考資料：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://msdn.microsoft.com/en-us/library/default.aspx&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;MSDN&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>Tech</category></item><item><title>Strategy pattern 策略模式</title><link>https://blog.amowu.com/posts/2009-05-20-strategy-pattern-20/</link><guid isPermaLink="true">https://blog.amowu.com/posts/2009-05-20-strategy-pattern-20/</guid><description>這篇文章是我打算要寫的 Design pattern 系列中的第一篇，在我大學三年級的時候，修了系上開的視窗程式設計課程，開課教授也是我的專題指導教授，這堂課老師教了我們很多關於 programming design pattern 的觀念。</description><pubDate>Wed, 20 May 2009 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;這篇文章是我打算要寫的 Design pattern 系列中的第一篇，在我大學三年級的時候，修了系上開的視窗程式設計課程，開課教授也是我的專題指導教授，這堂課老師教了我們很多關於 programming design pattern 的觀念。&lt;/p&gt;
&lt;p&gt;最近剛好也讀完了 &lt;a href=&quot;http://findbook.tw/book/9789867794529/basic&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;O’REILLY 出版的 Head First Design Patterns（深入淺出設計模式）&lt;/a&gt; ，對於一些較常用的 pattern 也有了更加的了解，所以我希望能整理幾個比較入門的 pattern 來當作學習文章,如果有哪裡寫錯或是有疑問的，非常歡迎留言指教，大家一起學習。&lt;/p&gt;
&lt;p&gt;什麼是 design pattern（設計模式）呢?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;design pattern 是對軟體設計中普遍存在（反覆出現）的各種問題，所提出的解決方案。&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;design pattern 並不是直接用來完成程式碼的編寫，而是描述在各種不同情況下，要怎麼解決問題的一種方案。&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;演算法不能算是一種 design pattern，因為演算法主要是用來解決計算上的問題，而非設計上的問題。 design pattern 主要目的是避免會引起麻煩的程式緊密耦合，以增強軟體設計面對並適應變化的能力。&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;以上是引用 &lt;a href=&quot;http://zh.wikipedia.org/w/index.php?title=%E8%BD%AF%E4%BB%B6%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F&amp;amp;variant=zh-tw&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;軟體設計模式-維基百科&lt;/a&gt; 的其中一段介紹，第一次接觸的人可以去看看相關內容。&lt;/p&gt;
&lt;p&gt;接下來我們進入主題，第一篇要來介紹的 pattern 是 &lt;strong&gt;Strategy pattern&lt;/strong&gt; （策略模式）。&lt;/p&gt;
&lt;p&gt;Strategy pattern 是指物件本身有某些行爲（method），但是在不同的情況中，該行爲有不同的實現方式（演算法），找出程式中可能需要更動的地方，把它們個別封裝起來，就算演算法有變動，也不會影響到使用演算法的程式，舉個 &lt;a href=&quot;https://www.blogger.com/blog/post/edit/696354394590316778/6797536677321234615&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;維基百科&lt;/a&gt; 提供的例子，比如每個人都要「繳交個人所得稅」，但是「在美國繳交的個人所得稅」和「在台灣繳交的個人所得稅」就有不同的計算稅的演算法。&lt;/p&gt;
&lt;p&gt;我們用 &lt;a href=&quot;http://findbook.tw/book/9789867794529/basic&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Head First Design Patterns&lt;/a&gt; 書上的鴨子範例來一步步解釋。&lt;/p&gt;
&lt;p&gt;假設我們打算完成一個模擬鴨子的小程式，程式中可以模擬出各種不同類型的鴨子行為，例如游泳，呱呱叫等等的…。&lt;/p&gt;
&lt;p&gt;這個時候我們會設計一個擁有這些行為的鴨子抽象類別（abstract class），讓各種鴨子繼承此類別。&lt;/p&gt;
&lt;p&gt;紅頭鴨跟綠頭鴨都直接繼承 Duck 的 swim（游泳）跟 quack（呱呱叫）方法，因為兩隻鴨子的外觀不同，所以各自實作 display 抽象方法。&lt;/p&gt;
&lt;p&gt;如果這個時候突然想為鴨子加上飛行的行為呢?&lt;/p&gt;
&lt;p&gt;很直覺的，我們會直接在 Duck 中加入 &lt;code&gt;fly()&lt;/code&gt; 讓每種鴨子都能順利繼承。&lt;/p&gt;
&lt;p&gt;但是這個時候我們碰到問題了。&lt;/p&gt;
&lt;p&gt;如果我們打算加入一隻橡皮玩具鴨怎麼辦?&lt;/p&gt;
&lt;p&gt;橡皮鴨繼承了上面 Duck 的 fly 行為，可是像皮鴨實際上並不會飛行的……。&lt;/p&gt;
&lt;p&gt;你可能想到一個方法，就是用 override 覆寫 &lt;code&gt;fly()&lt;/code&gt; 方法，讓它不實作任何程式。&lt;/p&gt;
&lt;p&gt;解決了飛行的問題之後，我們發現其實像皮鴨的叫聲也跟其他鴨子不同，所以把 &lt;code&gt;quack()&lt;/code&gt; 方法也 override 成吱吱叫，取代原本的呱呱叫。&lt;/p&gt;
&lt;p&gt;但是問題解決後，馬上又產生另一個問題，如果我們又打算加入一隻誘餌鴨呢? 
誘餌鴨不會飛也不會叫，我們又要把 &lt;code&gt;fly()&lt;/code&gt; 跟 &lt;code&gt;quack()&lt;/code&gt; 兩個方法又 override 一次。&lt;/p&gt;
&lt;p&gt;這裡我們就會發現，利用繼承的方式提供鴨子行為會產生一些問題：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;程式碼在多個類別中不斷重複&lt;/li&gt;
&lt;li&gt;runtime 期間，鴨子的行為不容易改變&lt;/li&gt;
&lt;li&gt;難以得知鴨子的全部行為&lt;/li&gt;
&lt;li&gt;改變會牽一髮動全身，造成其他鴨子不想要的影響&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;上面我們看見最主要的問題就是不容易維護，若是以後還有其他鴨子要加進來，我們又要因應它的特性來繼續 override 這些行為，而這些行為其實之前明明就已經實作過了，最後造成一堆不必要的程式碼重複。&lt;/p&gt;
&lt;p&gt;不能用繼承的話，用介面來實作如何?&lt;/p&gt;
&lt;p&gt;把上面需要不同實作方式的 fly 跟 quack 抽出來分別寫成 Flyable 跟 Quackable 介面，讓所有鴨子實作牠們自己需要的介面。&lt;/p&gt;
&lt;p&gt;完成了上面的實作後，雖然完成了不用一直 override 的難題，並且也可以實現出不會飛的鴨子，但是最主要的問題還是沒解決，程式碼還是有到處重複無法再利用的情形，例如上圖中 MallarDuck 跟 RedheadDuck 的 &lt;code&gt;fly()&lt;/code&gt; 跟 &lt;code&gt;quack()&lt;/code&gt; 內容都是一樣的，綠頭鴨是呱呱叫跟會飛，紅頭鴨也是呱呱叫會飛，但是牠們卻都要自己再寫一次程式碼去實作，根據 &lt;a href=&quot;http://en.wikipedia.org/wiki/Anas&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;維基百科&lt;/a&gt;提供的資料，鴨子的種類有 40 到 50 種，這些都是會飛且呱呱叫的鴨子，難道要我們每增加一次鴨子類別，就又要去實作已經寫過的 &lt;code&gt;fly()&lt;/code&gt; 跟 &lt;code&gt;quack()&lt;/code&gt; 嗎！？可見這並不是一個良好的 OO 軟體設計方式。&lt;/p&gt;
&lt;p&gt;那到底該怎麼寫才適合這些可惡的鴨子呢…?&lt;/p&gt;
&lt;p&gt;接下來會進入我們這篇文章的重點，策略模式的精神。&lt;/p&gt;
&lt;p&gt;我們知道目前 Duck 類別內的 &lt;code&gt;fly()&lt;/code&gt; 跟 &lt;code&gt;quack()&lt;/code&gt; 會隨著有些鴨子的不同而改變，為了要讓他們可以隨著鴨子改變，且達到可重覆利用的特性，我們一樣將這兩個行為從 Duck 抽出來，獨立寫成個別的類別，讓我們來看下面這張圖。&lt;/p&gt;
&lt;p&gt;上圖就是為了讓鴨子行為可以再三利用，獨立分出來的兩個策略類別（Strategy class），所有不同的飛行類別都要實作（或繼承）FlyBehavior 介面（或抽象類別），叫聲行為也是，這樣做有什麼好處呢？我們直接寫程式來實作給大家看。&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;System&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;namespace&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;StrategySample&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// 鴨子抽象類別&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;abstract&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Duck&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// 每隻鴨子都會參考到一個實踐FlyBehavior介面的飛行物件&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;protected&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;FlyBehavior&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;flyBehavior&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// 每隻鴨子都會參考到一個實踐QuackBehavior介面的叫聲物件&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;protected&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;QuackBehavior&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;quackBehavior&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// 鴨子的外觀&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;remarks&lt;/span&gt;&lt;span&gt;&amp;gt;因為每隻鴨子的外觀不同，所以display()方法是抽象的&amp;lt;/&lt;/span&gt;&lt;span&gt;remarks&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;abstract&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;display&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// 鴨子的游泳行為&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;remarks&lt;/span&gt;&lt;span&gt;&amp;gt;所有的鴨子都用相同的方式游泳，所以由超類別負責處理這部份的程式&amp;lt;/&lt;/span&gt;&lt;span&gt;remarks&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;swim&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Console.&lt;/span&gt;&lt;span&gt;WriteLine&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;All duck s float, even decoys&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// 鴨子的飛行行為&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;remarks&lt;/span&gt;&lt;span&gt;&amp;gt;鴨子不親自處理飛行行為，而是委由flyBehavior物件幫Duck進行飛行&amp;lt;/&lt;/span&gt;&lt;span&gt;remarks&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;performFly&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.flyBehavior.&lt;/span&gt;&lt;span&gt;fly&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// 鴨子的叫聲行為&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;remarks&lt;/span&gt;&lt;span&gt;&amp;gt;鴨子不親自處理叫聲行為，而是委由quackBehavior物件幫Duck進行呱呱叫&amp;lt;/&lt;/span&gt;&lt;span&gt;remarks&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;performQuack&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.quackBehavior.&lt;/span&gt;&lt;span&gt;quack&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// 呼叫setFlyBehavior()方法可以改變鴨子的飛行方式&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;param&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;newFlyBehavior&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;想要改變的飛行方式&amp;lt;/&lt;/span&gt;&lt;span&gt;param&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;setFlyBehavior&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;FlyBehavior&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;newFlyBehavior&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.flyBehavior &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; newFlyBehavior;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// 呼叫setQuackBehavior()方法可以改變鴨子的叫聲方式&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;param&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;newQuackBehavior&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;想要改變的叫聲方式&amp;lt;/&lt;/span&gt;&lt;span&gt;param&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;setQuackBehavior&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;QuackBehavior&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;newQuackBehavior&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.quackBehavior &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; newQuackBehavior;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;上面我們把 FlyBehavior 跟 QuackBehavior 以介面的型態加入到 Duck 的成員變數，這樣就能在 Runtime 期間動態改變鴨子的行為。&lt;/p&gt;
&lt;p&gt;再來我們繼續把上面的 FlyBehavior 跟 QuackBehavior 程式碼給寫出來。&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;namespace&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;StrategySample&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// 這是一個飛行行為的介面，所有飛行行為的類別都應該實作它&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;interface&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;FlyBehavior&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// 飛行的方式&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;remarks&lt;/span&gt;&lt;span&gt;&amp;gt;所有繼承FlyBehavior的類別都應該實作此方法&amp;lt;/&lt;/span&gt;&lt;span&gt;remarks&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fly&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;namespace&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;StrategySample&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// 這是一個叫聲行為的介面，所有叫聲行為的類別都應該實作它&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;interface&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;QuackBehavior&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// 叫聲的方式&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;remarks&lt;/span&gt;&lt;span&gt;&amp;gt;所有繼承QuackBehavior的類別都應該實作此方法&amp;lt;/&lt;/span&gt;&lt;span&gt;remarks&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;quack&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;然後我們把所有的行為實作出來&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;System&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;namespace&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;StrategySample&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// 用翅膀飛行的行為&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;FlyWithWings&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;FlyBehavior&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fly&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Console.&lt;/span&gt;&lt;span&gt;WriteLine&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;I&apos;m flying with my wings&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;System&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;namespace&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;StrategySample&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// 無法飛行的行為&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;FlyNoWay&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;FlyBehavior&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fly&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Console.&lt;/span&gt;&lt;span&gt;WriteLine&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;I can&apos;t fly&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;System&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;namespace&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;StrategySample&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// 呱呱叫的行為&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Quack&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;QuackBehavior&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;quack&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Console.&lt;/span&gt;&lt;span&gt;WriteLine&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;I say Quack&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;System&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;namespace&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;StrategySample&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// 吱吱叫的行為&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Squeak&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;QuackBehavior&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;quack&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Console.&lt;/span&gt;&lt;span&gt;WriteLine&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;I say Squeak&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;System&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;namespace&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;StrategySample&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// 沒有叫聲的行為&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;MuteQuack&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;QuackBehavior&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;quack&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Console.&lt;/span&gt;&lt;span&gt;WriteLine&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;I say ...(Silence)&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;接著我們來寫一隻綠頭鴨子的模擬程式測試看看。&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;System&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;namespace&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;StrategySample&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// 綠頭鴨&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;MallardDuck&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;Duck&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// 綠頭鴨&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;MallardDuck&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.flyBehavior &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;FlyWithWings&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.quackBehavior &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Quack&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;override&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;display&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Console.&lt;/span&gt;&lt;span&gt;WriteLine&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;I&apos;m a real mallard duck&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;System&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;namespace&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;StrategySample&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;MiniDuckSimulator&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Main&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;[] &lt;/span&gt;&lt;span&gt;args&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 新增一隻綠頭鴨&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;Duck&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mallardDuck&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;MallardDuck&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;mallardDuck.&lt;/span&gt;&lt;span&gt;display&lt;/span&gt;&lt;span&gt;();      &lt;/span&gt;&lt;span&gt;// 綠頭鴨外觀&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;mallardDuck.&lt;/span&gt;&lt;span&gt;performFly&lt;/span&gt;&lt;span&gt;();   &lt;/span&gt;&lt;span&gt;// 用翅膀飛行&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;mallardDuck.&lt;/span&gt;&lt;span&gt;performQuack&lt;/span&gt;&lt;span&gt;(); &lt;/span&gt;&lt;span&gt;// 呱呱叫&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;完成了綠頭鴨子之後我們再加入一隻紅頭鴨子模擬一遍。&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;System&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;namespace&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;StrategySample&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// 紅頭鴨&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;RedheadDuck&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;Duck&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// 紅頭鴨&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;RedheadDuck&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.flyBehavior &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;FlyWithWings&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.quackBehavior &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Quack&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;override&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;display&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Console.&lt;/span&gt;&lt;span&gt;WriteLine&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;I&apos;m a real redhead duck&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;System&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;namespace&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;StrategySample&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;MiniDuckSimulator&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Main&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;[] &lt;/span&gt;&lt;span&gt;args&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 新增一隻綠頭鴨&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;Duck&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mallardDuck&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;MallardDuck&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;mallardDuck.&lt;/span&gt;&lt;span&gt;display&lt;/span&gt;&lt;span&gt;();      &lt;/span&gt;&lt;span&gt;// 綠頭鴨外觀&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;mallardDuck.&lt;/span&gt;&lt;span&gt;performFly&lt;/span&gt;&lt;span&gt;();   &lt;/span&gt;&lt;span&gt;// 用翅膀飛行&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;mallardDuck.&lt;/span&gt;&lt;span&gt;performQuack&lt;/span&gt;&lt;span&gt;(); &lt;/span&gt;&lt;span&gt;// 呱呱叫&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Console.&lt;/span&gt;&lt;span&gt;WriteLine&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;-------------------------&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 新增一隻紅頭鴨&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;Duck&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;redheadDuck&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;RedheadDuck&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;redheadDuck.&lt;/span&gt;&lt;span&gt;display&lt;/span&gt;&lt;span&gt;();      &lt;/span&gt;&lt;span&gt;// 紅頭鴨外觀&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;redheadDuck.&lt;/span&gt;&lt;span&gt;performFly&lt;/span&gt;&lt;span&gt;();   &lt;/span&gt;&lt;span&gt;// 用翅膀飛行&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;redheadDuck.&lt;/span&gt;&lt;span&gt;performQuack&lt;/span&gt;&lt;span&gt;(); &lt;/span&gt;&lt;span&gt;// 呱呱叫&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;看到上面的程式就可以發現雖然紅頭鴨跟綠頭鴨的行為一樣，但是不會產生像之前那樣程式碼重覆的情形，因為我們將 fly 跟 quack 的實作方式交給 FlyBehavior 跟 QuackBehavior，鴨子們只管呼叫它，不必知道它們的實現方法，這是 OO 中一個很重要的概念。&lt;/p&gt;
&lt;p&gt;繼續完成剩下的兩隻鴨子&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;System&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;namespace&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;StrategySample&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// 橡皮鴨&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;RubberDuck&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;Duck&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// 橡皮鴨&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;RubberDuck&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.flyBehavior &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;FlyNoWay&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.quackBehavior &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Squeak&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;override&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;display&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Console.&lt;/span&gt;&lt;span&gt;WriteLine&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;I&apos;m a real rubber duck&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;System&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;namespace&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;StrategySample&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// 誘餌鴨&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;DecoyDuck&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;Duck&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// 誘餌鴨&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;DecoyDuck&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.flyBehavior &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;FlyNoWay&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.quackBehavior &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;MuteQuack&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;override&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;display&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Console.&lt;/span&gt;&lt;span&gt;WriteLine&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;I&apos;m a real decoy duck&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;System&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;namespace&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;StrategySample&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;MiniDuckSimulator&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Main&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;[] &lt;/span&gt;&lt;span&gt;args&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 新增一隻綠頭鴨&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;Duck&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mallardDuck&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;MallardDuck&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;mallardDuck.&lt;/span&gt;&lt;span&gt;display&lt;/span&gt;&lt;span&gt;();      &lt;/span&gt;&lt;span&gt;// 綠頭鴨外觀&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;mallardDuck.&lt;/span&gt;&lt;span&gt;performFly&lt;/span&gt;&lt;span&gt;();   &lt;/span&gt;&lt;span&gt;// 用翅膀飛行&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;mallardDuck.&lt;/span&gt;&lt;span&gt;performQuack&lt;/span&gt;&lt;span&gt;(); &lt;/span&gt;&lt;span&gt;// 呱呱叫&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Console.&lt;/span&gt;&lt;span&gt;WriteLine&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;-------------------------&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 新增一隻紅頭鴨&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;Duck&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;redheadDuck&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;RedheadDuck&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;redheadDuck.&lt;/span&gt;&lt;span&gt;display&lt;/span&gt;&lt;span&gt;();      &lt;/span&gt;&lt;span&gt;// 紅頭鴨外觀&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;redheadDuck.&lt;/span&gt;&lt;span&gt;performFly&lt;/span&gt;&lt;span&gt;();   &lt;/span&gt;&lt;span&gt;// 用翅膀飛行&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;redheadDuck.&lt;/span&gt;&lt;span&gt;performQuack&lt;/span&gt;&lt;span&gt;(); &lt;/span&gt;&lt;span&gt;// 呱呱叫&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Console.&lt;/span&gt;&lt;span&gt;WriteLine&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;-------------------------&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 新增一隻橡皮鴨&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;Duck&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rubberDuck&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;RubberDuck&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;rubberDuck.&lt;/span&gt;&lt;span&gt;display&lt;/span&gt;&lt;span&gt;();       &lt;/span&gt;&lt;span&gt;// 橡皮鴨外觀&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;rubberDuck.&lt;/span&gt;&lt;span&gt;performFly&lt;/span&gt;&lt;span&gt;();    &lt;/span&gt;&lt;span&gt;// 不會飛行&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;rubberDuck.&lt;/span&gt;&lt;span&gt;performQuack&lt;/span&gt;&lt;span&gt;();  &lt;/span&gt;&lt;span&gt;// 吱吱叫&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Console.&lt;/span&gt;&lt;span&gt;WriteLine&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;-------------------------&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 新增一隻誘餌鴨&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;Duck&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;decoyDuck&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;DecoyDuck&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;decoyDuck.&lt;/span&gt;&lt;span&gt;display&lt;/span&gt;&lt;span&gt;();        &lt;/span&gt;&lt;span&gt;// 誘餌鴨外觀&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;decoyDuck.&lt;/span&gt;&lt;span&gt;performFly&lt;/span&gt;&lt;span&gt;();     &lt;/span&gt;&lt;span&gt;// 不會飛行&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;decoyDuck.&lt;/span&gt;&lt;span&gt;performQuack&lt;/span&gt;&lt;span&gt;();   &lt;/span&gt;&lt;span&gt;// 不會叫&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;到這裡我們已經把鴨子模擬程式完成的差不多了，最後我們再來加一隻鴨子，我們加入一隻可以隨時改造的模型鴨子，這裡可以看出 runtime 期間動態改變行為的威力。&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;System&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;namespace&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;StrategySample&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// 模型鴨&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ModelDuck&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;Duck&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// 模型鴨&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ModelDuck&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.flyBehavior &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;FlyNoWay&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.quackBehavior &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;MuteQuack&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;override&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;display&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Console.&lt;/span&gt;&lt;span&gt;WriteLine&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;I&apos;m a real model duck&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;接著我們為 ModelDuck 寫一個飛行行為，我們把它取名為火箭飛行 FlyRocketPowered。&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;namespace&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;StrategySample&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// 利用火箭動力飛行的行為&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/// &amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;FlyRocketPowered&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;FlyBehavior&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fly&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Console.&lt;/span&gt;&lt;span&gt;WriteLine&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;I&apos;m flying with rocket&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;好了，我們開始模擬這隻模型鴨，一開始我們的模型鴨不會飛也不會叫，經過了 Duck 的 &lt;code&gt;setFlyBehavior()&lt;/code&gt; 我們可以在程式執行的期間把它裝上火箭動力，使它飛起來。&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;System&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;namespace&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;StrategySample&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;MiniDuckSimulator&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Main&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;[] &lt;/span&gt;&lt;span&gt;args&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 新增一隻綠頭鴨&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;Duck&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mallardDuck&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;MallardDuck&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;mallardDuck.&lt;/span&gt;&lt;span&gt;display&lt;/span&gt;&lt;span&gt;();      &lt;/span&gt;&lt;span&gt;// 綠頭鴨外觀&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;mallardDuck.&lt;/span&gt;&lt;span&gt;performFly&lt;/span&gt;&lt;span&gt;();   &lt;/span&gt;&lt;span&gt;// 用翅膀飛行&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;mallardDuck.&lt;/span&gt;&lt;span&gt;performQuack&lt;/span&gt;&lt;span&gt;(); &lt;/span&gt;&lt;span&gt;// 呱呱叫&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Console.&lt;/span&gt;&lt;span&gt;WriteLine&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;-------------------------&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 新增一隻紅頭鴨&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;Duck&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;redheadDuck&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;RedheadDuck&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;redheadDuck.&lt;/span&gt;&lt;span&gt;display&lt;/span&gt;&lt;span&gt;();      &lt;/span&gt;&lt;span&gt;// 紅頭鴨外觀&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;redheadDuck.&lt;/span&gt;&lt;span&gt;performFly&lt;/span&gt;&lt;span&gt;();   &lt;/span&gt;&lt;span&gt;// 用翅膀飛行&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;redheadDuck.&lt;/span&gt;&lt;span&gt;performQuack&lt;/span&gt;&lt;span&gt;(); &lt;/span&gt;&lt;span&gt;// 呱呱叫&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Console.&lt;/span&gt;&lt;span&gt;WriteLine&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;-------------------------&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 新增一隻橡皮鴨&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;Duck&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rubberDuck&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;RubberDuck&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;rubberDuck.&lt;/span&gt;&lt;span&gt;display&lt;/span&gt;&lt;span&gt;();       &lt;/span&gt;&lt;span&gt;// 橡皮鴨外觀&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;rubberDuck.&lt;/span&gt;&lt;span&gt;performFly&lt;/span&gt;&lt;span&gt;();    &lt;/span&gt;&lt;span&gt;// 不會飛行&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;rubberDuck.&lt;/span&gt;&lt;span&gt;performQuack&lt;/span&gt;&lt;span&gt;();  &lt;/span&gt;&lt;span&gt;// 吱吱叫&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Console.&lt;/span&gt;&lt;span&gt;WriteLine&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;-------------------------&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 新增一隻誘餌鴨&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;Duck&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;decoyDuck&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;DecoyDuck&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;decoyDuck.&lt;/span&gt;&lt;span&gt;display&lt;/span&gt;&lt;span&gt;();        &lt;/span&gt;&lt;span&gt;// 誘餌鴨外觀&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;decoyDuck.&lt;/span&gt;&lt;span&gt;performFly&lt;/span&gt;&lt;span&gt;();     &lt;/span&gt;&lt;span&gt;// 不會飛行&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;decoyDuck.&lt;/span&gt;&lt;span&gt;performQuack&lt;/span&gt;&lt;span&gt;();   &lt;/span&gt;&lt;span&gt;// 不會叫&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Console.&lt;/span&gt;&lt;span&gt;WriteLine&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;-------------------------&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 新增一隻模型鴨&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;Duck&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;modelDuck&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ModelDuck&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;modelDuck.&lt;/span&gt;&lt;span&gt;display&lt;/span&gt;&lt;span&gt;();        &lt;/span&gt;&lt;span&gt;// 模型鴨外觀&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;modelDuck.&lt;/span&gt;&lt;span&gt;performFly&lt;/span&gt;&lt;span&gt;();     &lt;/span&gt;&lt;span&gt;// 不會飛行&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;modelDuck.&lt;/span&gt;&lt;span&gt;performQuack&lt;/span&gt;&lt;span&gt;();   &lt;/span&gt;&lt;span&gt;// 不會叫&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Console.&lt;/span&gt;&lt;span&gt;WriteLine&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;After the installation of rocket...&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;modelDuck.&lt;/span&gt;&lt;span&gt;setFlyBehavior&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;FlyRocketPowered&lt;/span&gt;&lt;span&gt;());   &lt;/span&gt;&lt;span&gt;// 裝上火箭動力之後...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;modelDuck.&lt;/span&gt;&lt;span&gt;performFly&lt;/span&gt;&lt;span&gt;();     &lt;/span&gt;&lt;span&gt;// 火箭動力飛行!!!&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;OK!!! 鴨子模擬程式完成了，我們來看看整個類別架構圖。&lt;/p&gt;
&lt;p&gt;到這裡我們已經將策略模式給學起來了，讓我們來看看策略模式的 UML，並且跟上圖比較看看。&lt;/p&gt;
&lt;p&gt;看完了上面介紹的鴨子範例後我想大家應該已經可以了解策略模式的主要精神了，將抽象類別中可能會需要隨著繼承類別的不同改變的方法獨立抽出來封裝成另外一個介面，然後個別為這些介面去實作需要的類別方法，不但增加可利用性，也不會出現程式碼不斷重複的情況。&lt;/p&gt;
&lt;p&gt;呼，寫到這裡終於到一個段落了，因為這是我的第一篇心得分享文章，所以希望把它寫的很仔細，雖然可能有很多地方會有問題，或是交待的不清楚，所以大家如果有哪些疑問也請不要客氣，儘管提出你的看法，讓我有更大的動力繼續衝下一篇文章，謝謝。&lt;/p&gt;
&lt;p&gt;下一篇 design pattern 的文章我打算介紹同樣屬於比較容易了解的初學模式，Factory pattern（工廠模式），Factory pattern 也是一個非常常用的設計方式，其中還衍生出許多不同的 pattern，例如 abstract factory pattern（抽象工廠模式），希望到時能如期把它完成並分享上來，謝謝大家不厭其煩的把這篇文章看到這裡，感謝。&lt;/p&gt;
&lt;p&gt;範例程式:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://sites.google.com/site/amo26site/download/StrategySample.rar?attredirects=0&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;StrategySample.rar&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;參考資料:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://findbook.tw/book/9789867794529/basic&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Head First Design Patterns — 深入淺出設計模式&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://zh.wikipedia.org/w/index.php?title=%E7%AD%96%E7%95%A5%E6%A8%A1%E5%BC%8F&amp;amp;variant=zh-tw&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;策略模式 — 維基百科&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>Tech</category></item></channel></rss>