List of Facade - text.Baldanders.info
tag:text.Baldanders.info,2016-11-06:/tags
2016-11-06T17:57:37+09:00
帰ってきた「しっぽのさきっちょ」
https://text.baldanders.info/images/avatar.jpg
https://text.baldanders.info/images/avatar.jpg
モンテカルロ法による円周率の推定(その2 CLI)
tag:text.Baldanders.info,2016-11-06:/golang/estimate-of-pi-2-cli/
2016-11-06T08:57:37+00:00
2021-08-12T21:22:05+00:00
さっそく推定結果について評価を行いたいところだが,その前に CLI (command-line interface) を整備する。今回は spf13/cobra パッケージを使うことにする。
Spiegel
https://baldanders.info/profile/
<ol>
<li><a href="https://text.baldanders.info/golang/estimate-of-pi/">モンテカルロ法による円周率の推定(その1)</a></li>
<li><a href="https://text.baldanders.info/golang/estimate-of-pi-2-cli/">モンテカルロ法による円周率の推定(その2 CLI)</a> ← イマココ</li>
<li><a href="https://text.baldanders.info/golang/estimate-of-pi-3-gaussian/">モンテカルロ法による円周率の推定(その3 Gaussian)</a></li>
<li><a href="https://text.baldanders.info/golang/estimate-of-pi-4-prng/">モンテカルロ法による円周率の推定(その4 PRNG)</a></li>
</ol>
<h2>コマンドライン・インタフェース</h2>
<p>さっそく推定結果について評価を行いたいところだが,その前に CLI (command-line interface) を整備する。
どういうことかというと,<a href="https://text.baldanders.info/golang/estimate-of-pi/" title="モンテカルロ法による円周率の推定(その1)">前回</a>作った2つの <code>main()</code> 関数の処理</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"math/rand"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"time"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/spiegel-im-spiegel/pi/gencmplx"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">c</span> <span class="o">:=</span> <span class="nx">gencmplx</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="nx">rand</span><span class="p">.</span><span class="nf">NewSource</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nf">Now</span><span class="p">().</span><span class="nf">UnixNano</span><span class="p">()),</span> <span class="nb">int64</span><span class="p">(</span><span class="mi">10000</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">p</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">c</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">"%v\t%v\n"</span><span class="p">,</span> <span class="nb">real</span><span class="p">(</span><span class="nx">p</span><span class="p">),</span> <span class="nb">imag</span><span class="p">(</span><span class="nx">p</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"math/cmplx"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"math/rand"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"time"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/spiegel-im-spiegel/pi/gencmplx"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">c</span> <span class="o">:=</span> <span class="nx">gencmplx</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="nx">rand</span><span class="p">.</span><span class="nf">NewSource</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nf">Now</span><span class="p">().</span><span class="nf">UnixNano</span><span class="p">()),</span> <span class="nb">int64</span><span class="p">(</span><span class="mi">100000</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"> <span class="nx">n</span> <span class="o">:=</span> <span class="nb">int64</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="c1">// total
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">m</span> <span class="o">:=</span> <span class="nb">int64</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="c1">// plot in circle
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">for</span> <span class="nx">p</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">c</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">n</span><span class="o">++</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">cmplx</span><span class="p">.</span><span class="nf">Abs</span><span class="p">(</span><span class="nx">p</span><span class="p">)</span> <span class="o"><=</span> <span class="nb">float64</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">m</span><span class="o">++</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">"n = %v, m = %v, 4m/n = %v\n"</span><span class="p">,</span> <span class="nx">n</span><span class="p">,</span> <span class="nx">m</span><span class="p">,</span> <span class="nb">float64</span><span class="p">(</span><span class="mi">4</span><span class="o">*</span><span class="nx">m</span><span class="p">)</span><span class="o">/</span><span class="nb">float64</span><span class="p">(</span><span class="nx">n</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>これをひとつの CLI で呼び出せるよう統合してしまおうというわけ。</p>
<p>CLI については以前に解説した。</p>
<ul>
<li><a href="https://text.baldanders.info/golang/cli-and-facade-pattern/">コマンドライン・インタフェースとファサード・パターン</a></li>
</ul>
<p>このときは <a href="https://github.com/mitchellh/cli" title="mitchellh/cli"><code>mitchellh/cli</code></a> を紹介したが,今回は <a href="https://github.com/spf13/cobra" title="spf13/cobra: A Commander for modern Go CLI interactions"><code>spf13/cobra</code></a> を使うことにする。</p>
<h3>spf13/cobra パッケージ</h3>
<p><a href="https://github.com/spf13/cobra" title="spf13/cobra: A Commander for modern Go CLI interactions"><code>spf13/cobra</code></a> パッケージの作者 <a href="https://github.com/spf13">spf13 (Steve Francia)</a> さんは <a href="https://www.linkedin.com/in/stevefrancia" title="Steven Francia | LinkedIn">Docker の中の人</a>で <a href="https://gohugo.io/" title="The world’s fastest framework for building websites | Hugo">Hugo</a> の作者としても有名な方。
もちろん <a href="https://gohugo.io/" title="The world’s fastest framework for building websites | Hugo">Hugo</a> の CLI にも <a href="https://github.com/spf13/cobra" title="spf13/cobra: A Commander for modern Go CLI interactions"><code>spf13/cobra</code></a> が使われている。</p>
<p>さらにありがたいことに <a href="https://github.com/spf13/cobra" title="spf13/cobra: A Commander for modern Go CLI interactions"><code>spf13/cobra</code></a> にはテンプレートコードを出力する CLI も用意されている。
インストールは <code>go get</code> コマンドで行う。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ go get -v github.com/spf13/cobra/cobra
</span></span></code></pre></div><p>これで <a href="https://github.com/spf13/cobra" title="spf13/cobra: A Commander for modern Go CLI interactions"><code>spf13/cobra</code></a> パッケージ本体と CLI がインストールされる。
テンプレートコードの生成は以下のコマンドを叩く。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ cobra init github.com/spiegel-im-spiegel/pi
</span></span><span class="line"><span class="cl">Your Cobra application is ready at
</span></span><span class="line"><span class="cl">C:\workspace\pi\src\github.com\spiegel-im-spiegel\pi
</span></span><span class="line"><span class="cl">Give it a try by going there and running `go run main.go`
</span></span><span class="line"><span class="cl">Add commands to it by running `cobra add [cmdname]`
</span></span></code></pre></div><p>既にパッケージ用のフォルダが作られている場合は,そのフォルダまで降りて</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ cobra init .
</span></span><span class="line"><span class="cl">Your Cobra application is ready at
</span></span><span class="line"><span class="cl">C:\workspace\pi\src\github.com\spiegel-im-spiegel\pi
</span></span><span class="line"><span class="cl">Give it a try by going there and running `go run main.go`
</span></span><span class="line"><span class="cl">Add commands to it by running `cobra add [cmdname]`
</span></span></code></pre></div><p>でもよい。</p>
<p>この時点で <code>main.go</code> と <code>cmd/root.go</code> のふたつが生成される。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// Copyright © 2016 NAME HERE <EMAIL ADDRESS>
</span></span></span><span class="line"><span class="cl"><span class="c1">//
</span></span></span><span class="line"><span class="cl"><span class="c1">// Licensed under the Apache License, Version 2.0 (the "License");
</span></span></span><span class="line"><span class="cl"><span class="c1">// you may not use this file except in compliance with the License.
</span></span></span><span class="line"><span class="cl"><span class="c1">// You may obtain a copy of the License at
</span></span></span><span class="line"><span class="cl"><span class="c1">//
</span></span></span><span class="line"><span class="cl"><span class="c1">// http://www.apache.org/licenses/LICENSE-2.0
</span></span></span><span class="line"><span class="cl"><span class="c1">//
</span></span></span><span class="line"><span class="cl"><span class="c1">// Unless required by applicable law or agreed to in writing, software
</span></span></span><span class="line"><span class="cl"><span class="c1">// distributed under the License is distributed on an "AS IS" BASIS,
</span></span></span><span class="line"><span class="cl"><span class="c1">// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
</span></span></span><span class="line"><span class="cl"><span class="c1">// See the License for the specific language governing permissions and
</span></span></span><span class="line"><span class="cl"><span class="c1">// limitations under the License.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="s">"github.com/spiegel-im-spiegel/pi/cmd"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">cmd</span><span class="p">.</span><span class="nf">Execute</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// Copyright © 2016 NAME HERE <EMAIL ADDRESS>
</span></span></span><span class="line"><span class="cl"><span class="c1">//
</span></span></span><span class="line"><span class="cl"><span class="c1">// Licensed under the Apache License, Version 2.0 (the "License");
</span></span></span><span class="line"><span class="cl"><span class="c1">// you may not use this file except in compliance with the License.
</span></span></span><span class="line"><span class="cl"><span class="c1">// You may obtain a copy of the License at
</span></span></span><span class="line"><span class="cl"><span class="c1">//
</span></span></span><span class="line"><span class="cl"><span class="c1">// http://www.apache.org/licenses/LICENSE-2.0
</span></span></span><span class="line"><span class="cl"><span class="c1">//
</span></span></span><span class="line"><span class="cl"><span class="c1">// Unless required by applicable law or agreed to in writing, software
</span></span></span><span class="line"><span class="cl"><span class="c1">// distributed under the License is distributed on an "AS IS" BASIS,
</span></span></span><span class="line"><span class="cl"><span class="c1">// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
</span></span></span><span class="line"><span class="cl"><span class="c1">// See the License for the specific language governing permissions and
</span></span></span><span class="line"><span class="cl"><span class="c1">// limitations under the License.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">cmd</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"os"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/spf13/cobra"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/spf13/viper"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">cfgFile</span> <span class="kt">string</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// RootCmd represents the base command when called without any subcommands
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">var</span> <span class="nx">RootCmd</span> <span class="p">=</span> <span class="o">&</span><span class="nx">cobra</span><span class="p">.</span><span class="nx">Command</span><span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">Use</span><span class="p">:</span> <span class="s">"pi"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">Short</span><span class="p">:</span> <span class="s">"A brief description of your application"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">Long</span><span class="p">:</span> <span class="s">`A longer description that spans multiple lines and likely contains
</span></span></span><span class="line"><span class="cl"><span class="s">examples and usage of using your application. For example:
</span></span></span><span class="line"><span class="cl"><span class="s">
</span></span></span><span class="line"><span class="cl"><span class="s">Cobra is a CLI library for Go that empowers applications.
</span></span></span><span class="line"><span class="cl"><span class="s">This application is a tool to generate the needed files
</span></span></span><span class="line"><span class="cl"><span class="s">to quickly create a Cobra application.`</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="c1">// Uncomment the following line if your bare application
</span></span></span><span class="line"><span class="cl"><span class="c1">// has an action associated with it:
</span></span></span><span class="line"><span class="cl"><span class="c1">// Run: func(cmd *cobra.Command, args []string) { },
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Execute adds all child commands to the root command sets flags appropriately.
</span></span></span><span class="line"><span class="cl"><span class="c1">// This is called by main.main(). It only needs to happen once to the rootCmd.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">Execute</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">RootCmd</span><span class="p">.</span><span class="nf">Execute</span><span class="p">();</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">os</span><span class="p">.</span><span class="nf">Exit</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">init</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">cobra</span><span class="p">.</span><span class="nf">OnInitialize</span><span class="p">(</span><span class="nx">initConfig</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// Here you will define your flags and configuration settings.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// Cobra supports Persistent Flags, which, if defined here,
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// will be global for your application.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"> <span class="nx">RootCmd</span><span class="p">.</span><span class="nf">PersistentFlags</span><span class="p">().</span><span class="nf">StringVar</span><span class="p">(</span><span class="o">&</span><span class="nx">cfgFile</span><span class="p">,</span> <span class="s">"config"</span><span class="p">,</span> <span class="s">""</span><span class="p">,</span> <span class="s">"config file (default is $HOME/.pi.yaml)"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="c1">// Cobra also supports local flags, which will only run
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// when this action is called directly.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">RootCmd</span><span class="p">.</span><span class="nf">Flags</span><span class="p">().</span><span class="nf">BoolP</span><span class="p">(</span><span class="s">"toggle"</span><span class="p">,</span> <span class="s">"t"</span><span class="p">,</span> <span class="kc">false</span><span class="p">,</span> <span class="s">"Help message for toggle"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// initConfig reads in config file and ENV variables if set.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">initConfig</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">cfgFile</span> <span class="o">!=</span> <span class="s">""</span> <span class="p">{</span> <span class="c1">// enable ability to specify config file via flag
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">viper</span><span class="p">.</span><span class="nf">SetConfigFile</span><span class="p">(</span><span class="nx">cfgFile</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="nx">viper</span><span class="p">.</span><span class="nf">SetConfigName</span><span class="p">(</span><span class="s">".pi"</span><span class="p">)</span> <span class="c1">// name of config file (without extension)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">viper</span><span class="p">.</span><span class="nf">AddConfigPath</span><span class="p">(</span><span class="s">"$HOME"</span><span class="p">)</span> <span class="c1">// adding home directory as first search path
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">viper</span><span class="p">.</span><span class="nf">AutomaticEnv</span><span class="p">()</span> <span class="c1">// read in environment variables that match
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"> <span class="c1">// If a config file is found, read it in.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">viper</span><span class="p">.</span><span class="nf">ReadInConfig</span><span class="p">();</span> <span class="nx">err</span> <span class="o">==</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">"Using config file:"</span><span class="p">,</span> <span class="nx">viper</span><span class="p">.</span><span class="nf">ConfigFileUsed</span><span class="p">())</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>この状態でいきなり動かしてみる。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ go run main.go
</span></span><span class="line"><span class="cl">A longer description that spans multiple lines and likely contains
</span></span><span class="line"><span class="cl">examples and usage of using your application. For example:
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Cobra is a CLI library for Go that empowers applications.
</span></span><span class="line"><span class="cl">This application is a tool to generate the needed files
</span></span><span class="line"><span class="cl">to quickly create a Cobra application.
</span></span></code></pre></div><p><code>RootCmd</code> で定義した説明が表示されているのがわかると思う。</p>
<p>次にサブコマンドを定義する。
名前は <code>plot</code> と <code>estmt</code> としようか。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ cobra add plot
</span></span><span class="line"><span class="cl">plot created at C:\workspace\pi\src\github.com\spiegel-im-spiegel\pi\cmd\plot.go
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">$ cobra add estmt
</span></span><span class="line"><span class="cl">estmt created at C:\workspace\pi\src\github.com\spiegel-im-spiegel\pi\cmd\estmt.go
</span></span></code></pre></div><p>これで <code>cmd/plot.go</code> と <code>cmd/estmt.go</code> のふたつが生成された。
<code>cmd/plot.go</code> の中身を見てみよう。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// Copyright © 2016 NAME HERE <EMAIL ADDRESS>
</span></span></span><span class="line"><span class="cl"><span class="c1">//
</span></span></span><span class="line"><span class="cl"><span class="c1">// Licensed under the Apache License, Version 2.0 (the "License");
</span></span></span><span class="line"><span class="cl"><span class="c1">// you may not use this file except in compliance with the License.
</span></span></span><span class="line"><span class="cl"><span class="c1">// You may obtain a copy of the License at
</span></span></span><span class="line"><span class="cl"><span class="c1">//
</span></span></span><span class="line"><span class="cl"><span class="c1">// http://www.apache.org/licenses/LICENSE-2.0
</span></span></span><span class="line"><span class="cl"><span class="c1">//
</span></span></span><span class="line"><span class="cl"><span class="c1">// Unless required by applicable law or agreed to in writing, software
</span></span></span><span class="line"><span class="cl"><span class="c1">// distributed under the License is distributed on an "AS IS" BASIS,
</span></span></span><span class="line"><span class="cl"><span class="c1">// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
</span></span></span><span class="line"><span class="cl"><span class="c1">// See the License for the specific language governing permissions and
</span></span></span><span class="line"><span class="cl"><span class="c1">// limitations under the License.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">cmd</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/spf13/cobra"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// plotCmd represents the plot command
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">var</span> <span class="nx">plotCmd</span> <span class="p">=</span> <span class="o">&</span><span class="nx">cobra</span><span class="p">.</span><span class="nx">Command</span><span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">Use</span><span class="p">:</span> <span class="s">"plot"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">Short</span><span class="p">:</span> <span class="s">"A brief description of your command"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">Long</span><span class="p">:</span> <span class="s">`A longer description that spans multiple lines and likely contains examples
</span></span></span><span class="line"><span class="cl"><span class="s">and usage of using your command. For example:
</span></span></span><span class="line"><span class="cl"><span class="s">
</span></span></span><span class="line"><span class="cl"><span class="s">Cobra is a CLI library for Go that empowers applications.
</span></span></span><span class="line"><span class="cl"><span class="s">This application is a tool to generate the needed files
</span></span></span><span class="line"><span class="cl"><span class="s">to quickly create a Cobra application.`</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">Run</span><span class="p">:</span> <span class="kd">func</span><span class="p">(</span><span class="nx">cmd</span> <span class="o">*</span><span class="nx">cobra</span><span class="p">.</span><span class="nx">Command</span><span class="p">,</span> <span class="nx">args</span> <span class="p">[]</span><span class="kt">string</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="c1">// TODO: Work your own magic here
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">"plot called"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">init</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">RootCmd</span><span class="p">.</span><span class="nf">AddCommand</span><span class="p">(</span><span class="nx">plotCmd</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// Here you will define your flags and configuration settings.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"> <span class="c1">// Cobra supports Persistent Flags which will work for this command
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// and all subcommands, e.g.:
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// plotCmd.PersistentFlags().String("foo", "", "A help for foo")
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"> <span class="c1">// Cobra supports local flags which will only run when this command
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// is called directly, e.g.:
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// plotCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><a href="https://github.com/spf13/cobra" title="spf13/cobra: A Commander for modern Go CLI interactions"><code>spf13/cobra</code></a> パッケージで特徴的なのは,サブコマンドを追加する際に <code>cmd/root.go</code> を変更する必要が無いことである。
サブコマンドの組み込みは <code>cmd</code> パッケージ内の各ファイルに定義されている <code>init()</code> 関数によって <code>main()</code> 起動前に行われる。</p>
<p>この状態で動かしてみよう。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ go run main.go
</span></span><span class="line"><span class="cl">A longer description that spans multiple lines and likely contains
</span></span><span class="line"><span class="cl">examples and usage of using your application. For example:
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Cobra is a CLI library for Go that empowers applications.
</span></span><span class="line"><span class="cl">This application is a tool to generate the needed files
</span></span><span class="line"><span class="cl">to quickly create a Cobra application.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Usage:
</span></span><span class="line"><span class="cl"> pi [command]
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Available Commands:
</span></span><span class="line"><span class="cl"> estmt A brief description of your command
</span></span><span class="line"><span class="cl"> plot A brief description of your command
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Flags:
</span></span><span class="line"><span class="cl"> --config string config file (default is $HOME/.pi.yaml)
</span></span><span class="line"><span class="cl"> -t, --toggle Help message for toggle
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Use "pi [command] --help" for more information about a command.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">$ go run main.go plot --help
</span></span><span class="line"><span class="cl">A longer description that spans multiple lines and likely contains examples
</span></span><span class="line"><span class="cl">and usage of using your command. For example:
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Cobra is a CLI library for Go that empowers applications.
</span></span><span class="line"><span class="cl">This application is a tool to generate the needed files
</span></span><span class="line"><span class="cl">to quickly create a Cobra application.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Usage:
</span></span><span class="line"><span class="cl"> pi plot [flags]
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Global Flags:
</span></span><span class="line"><span class="cl"> --config string config file (default is $HOME/.pi.yaml)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">$ go run main.go plot
</span></span><span class="line"><span class="cl">plot called
</span></span></code></pre></div><p>問題なくサブコマンドが組み込まれていることがわかる。</p>
<h2>CLI の作成</h2>
<p>では,生成されたテンプレートをベースに機能を組み込んでいく。 …というわけで,出来上がりが以下の repository にある。</p>
<ul>
<li><a href="https://github.com/spiegel-im-spiegel/pi">spiegel-im-spiegel/pi: Estimate of Pi with Monte Carlo method.</a></li>
</ul>
<p>フォルダ構成は以下の通り。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">github.com/spiegel-im-spiegel/pi
</span></span><span class="line"><span class="cl">| .editorconfig
</span></span><span class="line"><span class="cl">| .gitignore
</span></span><span class="line"><span class="cl">| glide.lock
</span></span><span class="line"><span class="cl">| glide.yaml
</span></span><span class="line"><span class="cl">| LICENSE
</span></span><span class="line"><span class="cl">| main.go
</span></span><span class="line"><span class="cl">| README.md
</span></span><span class="line"><span class="cl">|
</span></span><span class="line"><span class="cl">+---cmd
</span></span><span class="line"><span class="cl">| estmt.go
</span></span><span class="line"><span class="cl">| plot.go
</span></span><span class="line"><span class="cl">| root.go
</span></span><span class="line"><span class="cl">|
</span></span><span class="line"><span class="cl">+---estmt
</span></span><span class="line"><span class="cl">| estmt.go
</span></span><span class="line"><span class="cl">|
</span></span><span class="line"><span class="cl">+---gencmplx
</span></span><span class="line"><span class="cl">| gencmplx.go
</span></span><span class="line"><span class="cl">|
</span></span><span class="line"><span class="cl">+---genpi
</span></span><span class="line"><span class="cl">| genpi.go
</span></span><span class="line"><span class="cl">|
</span></span><span class="line"><span class="cl">\---plot
</span></span><span class="line"><span class="cl"> plot.go
</span></span></code></pre></div><p>そして各パッケージの構成は以下のようになっている。</p>
<figure style='margin:0 auto;text-align:center;'><a href="./estimate-of-pi.svg"><img src="./estimate-of-pi.svg" srcset="./estimate-of-pi.svg 640w" sizes="(min-width:600px) 500px, 80vw" alt="パッケージ構成" loading="lazy"></a><figcaption><div><a href="./estimate-of-pi.svg">パッケージ構成</a></div></figcaption>
</figure>
<p><a href="https://text.baldanders.info/golang/estimate-of-pi/" title="モンテカルロ法による円周率の推定(その1)">前回</a>と変わったところは <code>genpi</code> パッケージを追加したことだろうか。
こんな感じ。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">genpi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"math/cmplx"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"math/rand"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"time"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/spiegel-im-spiegel/pi/gencmplx"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">//New returns generator of Pi
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">New</span><span class="p">(</span><span class="nx">pc</span><span class="p">,</span> <span class="nx">ec</span> <span class="kt">int64</span><span class="p">)</span> <span class="o"><-</span><span class="kd">chan</span> <span class="kt">float64</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">ch</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">chan</span> <span class="kt">float64</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">pcf</span> <span class="o">:=</span> <span class="nb">float64</span><span class="p">(</span><span class="nx">pc</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">go</span> <span class="kd">func</span><span class="p">(</span><span class="nx">pc</span><span class="p">,</span> <span class="nx">ec</span> <span class="kt">int64</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="nb">int64</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span> <span class="nx">i</span> <span class="p"><</span> <span class="nx">ec</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">c</span> <span class="o">:=</span> <span class="nx">gencmplx</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="nx">rand</span><span class="p">.</span><span class="nf">NewSource</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nf">Now</span><span class="p">().</span><span class="nf">UnixNano</span><span class="p">()),</span> <span class="nx">pc</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">m</span> <span class="o">:=</span> <span class="nb">int64</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="c1">// plot in circle
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">for</span> <span class="nx">p</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">c</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">cmplx</span><span class="p">.</span><span class="nf">Abs</span><span class="p">(</span><span class="nx">p</span><span class="p">)</span> <span class="o"><=</span> <span class="nb">float64</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">m</span><span class="o">++</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">ch</span> <span class="o"><-</span> <span class="nb">float64</span><span class="p">(</span><span class="mi">4</span><span class="o">*</span><span class="nx">m</span><span class="p">)</span> <span class="o">/</span> <span class="nx">pcf</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nb">close</span><span class="p">(</span><span class="nx">ch</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}(</span><span class="nx">pc</span><span class="p">,</span> <span class="nx">ec</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">ch</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>指定した回数だけ推定処理を行い,結果を <a href="http://golang.org/ref/spec#Channel_types">channel</a> <code>ch</code> に渡している。
たとえば100,000個の点から円周率を推定する処理を10回したければ</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ go run main.go estmt -e 10 -p 100000
</span></span><span class="line"><span class="cl">3.14576
</span></span><span class="line"><span class="cl">3.1422
</span></span><span class="line"><span class="cl">3.13716
</span></span><span class="line"><span class="cl">3.14648
</span></span><span class="line"><span class="cl">3.14852
</span></span><span class="line"><span class="cl">3.13952
</span></span><span class="line"><span class="cl">3.14824
</span></span><span class="line"><span class="cl">3.13828
</span></span><span class="line"><span class="cl">3.14532
</span></span><span class="line"><span class="cl">3.14312
</span></span></code></pre></div><p>とすればよい。</p>
<p>これでようやく評価のための準備が整った。</p>
<h2>ブックマーク</h2>
<ul>
<li><a href="http://lab.flama.co.jp/archives/1536/">GolangでCLIの場合にcobraを使うことにした件 | FLAMA技術Blog</a></li>
<li><a href="http://qiita.com/astronoka/items/aa2f271d280863cedf5e">packageに複数のinitがあるときの挙動 - Qiita</a></li>
</ul>
<p><a href="https://text.baldanders.info/golang/bookmark/">Go 言語に関するブックマーク集はこちら</a>。</p>
<h2>参考図書</h2>
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/416Stewy0NS._SL160_.jpg" width="123" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">プログラミング言語Go</a></dt>
<dd>アラン・ドノバン (著), ブライアン・カーニハン (著), 柴田芳樹 (著)</dd>
<dd>丸善出版 2016-06-20 (Release 2021-07-13)</dd>
<dd>Kindle版</dd>
<dd>B099928SJD (ASIN)</dd>
<dd>評価<abbr class="rating fa-sm" title="5"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i></abbr></dd>
</dl>
<p class="description">Kindle 版出た! 一部内容が古びてしまったが,この本は Go 言語の教科書と言ってもいいだろう。感想は<a href="https://text.baldanders.info/remark/2016/07/go-programming-language/" >こちら</a>。</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2021-05-22">2021-05-22</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- プログラミング言語Go -->
コマンドライン・インタフェースとファサード・パターン
tag:text.Baldanders.info,2016-01-05:/golang/cli-and-facade-pattern/
2016-01-05T13:06:41+00:00
2021-11-28T02:58:44+00:00
ファサード・パターンは DDD (Domain-Driven Design) と相性がよい。普通は Web アプリケーションのような多様なサブシステムを持つシステムを設計する際に導入する考え方だが, CLI の場合でもサブコマンドを構成するのであればファサード・パターンがよいだろう。
Spiegel
https://baldanders.info/profile/
<p><a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>コンパイラには <a href="https://golang.org/pkg/flag/" title="flag - The Go Programming Language"><code>flag</code></a> パッケージが標準で提供されており,いわゆるコマンドライン・インタフェース(Command line interface; CLI)の操作はこれでまかなうことができる。
ただし <a href="https://golang.org/pkg/flag/" title="flag - The Go Programming Language"><code>flag</code></a> パッケージではサブコマンドをサポートしていないためサブコマンドを構成したい場合は少し工夫が必要となる。
ちなみにサブコマンドとは,以下のようなコマンドラインの構成になっている CLI である。</p>
<pre tabindex="0"><code>$ command [golabal options] <sub-command> [sub-options] [arguments]
</code></pre><p>たとえば <a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>コンパイラの <code>go run</code> もサブコマンドだし, <a href="https://git-scm.com/" title="Git">git</a> の <code>git commit</code> とかもサブコマンドである。</p>
<h2>コマンドライン・インタフェースと UNIX Philosophy</h2>
<p>ところで CLI でよく引き合いに出されるのが “<a href="http://www.ru.j-npcs.org/usoft/WWW/LJ/Articles/unixtenets.html" title="Tenets of the UNIX Philosophy">UNIX Philosophy</a>” と呼ばれるアプリケーションを作る際の哲学というか指針のようなものである。
曰く</p>
<ol>
<li>Small is beautiful. (小さいものは美しい)</li>
<li>Make each program do one thing well. (各プログラムが一つのことをうまくやるようにせよ)</li>
<li>Build a prototype as soon as possible. (できる限り早くプロトタイプを作れ)</li>
<li>Choose portability over efficiency. (効率よりも移植しやすさを選べ)</li>
<li>Store data in flat text files. (単純なテキストファイルにデータを格納せよ)</li>
<li>Use software leverage to your advantage. (ソフトウェアの効率を優位さとして利用せよ)</li>
<li>Use shell scripts to increase leverage and portability. (効率と移植性を高めるためにシェルスクリプトを利用せよ)</li>
<li>Avoid captive user interfaces. (拘束的なユーザーインターフェースは作るな)</li>
<li>Make every program a Filter. (全てのプログラムはフィルタとして振る舞うようにせよ)</li>
</ol>
<p>の9項目<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>。
昨今は UNIX 互換環境でも GUI が普通になってきたので対話型のインタフェースも増えてきたが,それでも従来の CUI shell 上で動作するアプリケーションの需要が減ったわけではなく,サーバサイドではむしろ需要は大きくなっていると言ってもいい。</p>
<p><a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>で CLI アプリケーションを作る際に気をつける点としては</p>
<ul>
<li>他のツールと shell を介して連携できるよう標準入出力を使ったフィルタプログラムとする</li>
<li>外部データの入出力は JSON, YAML, TOML といったテキストを用い UTF-8 文字エンコーディングに統一する</li>
<li>コードの可搬性(または移植性)を考慮し,プラットフォーム依存を避けるようにする</li>
</ul>
<p>といったところだろうか。
もともと <a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>はクロスプラットフォーム開発に強いため,それほど難しい要件ではないはずである。</p>
<h2>サブコマンドとファサード・パターン</h2>
<p>サブコマンド方式は一見 “<a href="http://www.ru.j-npcs.org/usoft/WWW/LJ/Articles/unixtenets.html" title="Tenets of the UNIX Philosophy">UNIX Philosophy</a>” に反しているように見えるが, <a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>の場合は全てのパッケージをひとつの実行モジュールに結合してしまうため,関連する機能をサブコマンドとして組み込むのは悪くないやりかたである。</p>
<p>サブコマンドを構成する場合は「ファサード・パターン(facade pattern)」で考えるとよい。
「ファサード」は「建物の正面」という意味だそうで,システム内の各サブシステムの窓口のように機能する<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>。</p>
<figure style='margin:0 auto;text-align:center;'><div class="lightmode"><a href="./facade-pattern.svg"><img src="./facade-pattern.svg" srcset="./facade-pattern.svg 640w" sizes="(min-width:600px) 500px, 80vw" alt="Facade Pattern" loading="lazy"></a></div><figcaption><div><a href="./facade-pattern.svg">Facade Pattern</a></div></figcaption>
</figure>
<p>この図のようにファサード・パターンは DDD (Domain-Driven Design) と相性がよい。
普通は Web アプリケーションのような多様なサブシステムを含むシステムを設計する際に導入する考え方だが, CLI の場合でもサブコマンドを構成するのであればファサード・パターンがよいだろう。</p>
<h2>mitchellh/cli パッケージ</h2>
<p>CLI をサポートするパッケージはいくつか公開されているのだが<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>,この中で今回は <a href="https://github.com/mitchellh/cli" title="mitchellh/cli">mitchellh/cli</a> パッケージを紹介する。
<a href="https://github.com/mitchellh/cli" title="mitchellh/cli">mitchellh/cli</a> はサブコマンドをファサード・パターンで実装するのに便利な機能を実装している。</p>
<h3>Command インタフェース</h3>
<p>まずは <code>Command</code> インタフェース。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// A command is a runnable sub-command of a CLI.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">Command</span> <span class="kd">interface</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="c1">// Help should return long-form help text that includes the command-line
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// usage, a brief few sentences explaining the function of the command,
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// and the complete list of flags the command accepts.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nf">Help</span><span class="p">()</span> <span class="kt">string</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// Run should run the actual command with the given CLI instance and
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// command-line arguments. It should return the exit status when it is
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// finished.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nf">Run</span><span class="p">(</span><span class="nx">args</span> <span class="p">[]</span><span class="kt">string</span><span class="p">)</span> <span class="kt">int</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// Synopsis should return a one-line, short synopsis of the command.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// This should be less than 50 characters ideally.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nf">Synopsis</span><span class="p">()</span> <span class="kt">string</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><code>Command</code> インタフェースはサブコマンドの context 情報を構成するのに使う。
<a href="https://github.com/mitchellh/cli" title="mitchellh/cli">mitchellh/cli</a> は <code>Command</code> インタフェースに適合する型(<a href="https://go.dev/ref/spec#Properties_of_types_and_values" title="Properties of types and values">type</a>)のインスタンスを受け取ってサブコマンドの制御を行う<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup>。
さらに以下の関数値(function value)を示す型 <code>CommandFactory</code> も用意されている。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// CommandFactory is a type of function that is a factory for commands.
</span></span></span><span class="line"><span class="cl"><span class="c1">// We need a factory because we may need to setup some state on the
</span></span></span><span class="line"><span class="cl"><span class="c1">// struct that implements the command itself.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">CommandFactory</span> <span class="kd">func</span><span class="p">()</span> <span class="p">(</span><span class="nx">Command</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span>
</span></span></code></pre></div><p>このように <code>Command</code> 型のインスタンスを返す関数を型として定義し,この型のリストを作成するのである。</p>
<h3>CLI 構造体</h3>
<p><a href="https://github.com/mitchellh/cli" title="mitchellh/cli">mitchellh/cli</a> に渡す context 情報は <code>CLI</code> 構造体にまとめられている。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// CLI contains the state necessary to run subcommands and parse the
</span></span></span><span class="line"><span class="cl"><span class="c1">// command line arguments.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">CLI</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="c1">// Args is the list of command-line arguments received excluding
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// the name of the app. For example, if the command "./cli foo bar"
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// was invoked, then Args should be []string{"foo", "bar"}.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">Args</span> <span class="p">[]</span><span class="kt">string</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// Commands is a mapping of subcommand names to a factory function
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// for creating that Command implementation. If there is a command
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// with a blank string "", then it will be used as the default command
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// if no subcommand is specified.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">Commands</span> <span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="nx">CommandFactory</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// Name defines the name of the CLI.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">Name</span> <span class="kt">string</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// Version of the CLI.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">Version</span> <span class="kt">string</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// HelpFunc and HelpWriter are used to output help information, if
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// requested.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">//
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// HelpFunc is the function called to generate the generic help
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// text that is shown if help must be shown for the CLI that doesn't
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// pertain to a specific command.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">//
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// HelpWriter is the Writer where the help text is outputted to. If
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// not specified, it will default to Stderr.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">HelpFunc</span> <span class="nx">HelpFunc</span>
</span></span><span class="line"><span class="cl"> <span class="nx">HelpWriter</span> <span class="nx">io</span><span class="p">.</span><span class="nx">Writer</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="nx">once</span> <span class="nx">sync</span><span class="p">.</span><span class="nx">Once</span>
</span></span><span class="line"><span class="cl"> <span class="nx">isHelp</span> <span class="kt">bool</span>
</span></span><span class="line"><span class="cl"> <span class="nx">subcommand</span> <span class="kt">string</span>
</span></span><span class="line"><span class="cl"> <span class="nx">subcommandArgs</span> <span class="p">[]</span><span class="kt">string</span>
</span></span><span class="line"><span class="cl"> <span class="nx">topFlags</span> <span class="p">[]</span><span class="kt">string</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="nx">isVersion</span> <span class="kt">bool</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>構造体の中に <code>CommandFactory</code> のリストが含まれていることがお分かりだろうか。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">Commands</span> <span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="nx">CommandFactory</span>
</span></span></code></pre></div><p>これによってサブコマンド名と対応する処理を関連付けている。</p>
<h3>Ui インタフェース</h3>
<p>入出力関数群を持つ <code>Ui</code> インタフェースは以下のように定義されている。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// Ui is an interface for interacting with the terminal, or "interface"
</span></span></span><span class="line"><span class="cl"><span class="c1">// of a CLI. This abstraction doesn't have to be used, but helps provide
</span></span></span><span class="line"><span class="cl"><span class="c1">// a simple, layerable way to manage user interactions.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">Ui</span> <span class="kd">interface</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="c1">// Ask asks the user for input using the given query. The response is
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// returned as the given string, or an error.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nf">Ask</span><span class="p">(</span><span class="kt">string</span><span class="p">)</span> <span class="p">(</span><span class="kt">string</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// AskSecret asks the user for input using the given query, but does not echo
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// the keystrokes to the terminal.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nf">AskSecret</span><span class="p">(</span><span class="kt">string</span><span class="p">)</span> <span class="p">(</span><span class="kt">string</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// Output is called for normal standard output.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nf">Output</span><span class="p">(</span><span class="kt">string</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// Info is called for information related to the previous output.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// In general this may be the exact same as Output, but this gives
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// Ui implementors some flexibility with output formats.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nf">Info</span><span class="p">(</span><span class="kt">string</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// Error is used for any error messages that might appear on standard
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// error.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nf">Error</span><span class="p">(</span><span class="kt">string</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// Warn is used for any warning messages that might appear on standard
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// error.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nf">Warn</span><span class="p">(</span><span class="kt">string</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>更に <code>Ui</code> の特化クラスとして <code>BasicUi</code> や <code>PrefixedUi</code> や <code>ColoredUi</code> が定義されている。
<code>ColoredUi</code> は出力をカラーにできるが,残念ながら Windows のコマンドプロンプトには対応していないようだ。</p>
<p><code>Ui</code> インタフェースは <code>Command</code> インタフェースと組み合わせてサブコマンド側の context 情報を構成するのに使う。</p>
<h3>mitchellh/cli パッケージのメリット</h3>
<p>上述したように <a href="https://github.com/mitchellh/cli" title="mitchellh/cli">mitchellh/cli</a> はサブコマンドをファサード・パターンで実装するのに便利な機能を実装している。
なおかつ <a href="https://github.com/mitchellh/cli" title="mitchellh/cli">mitchellh/cli</a> ではファサード・パターンを入れ子にすることができる。
たとえばサブコマンドのサブコマンドを構成することもできるのだ。</p>
<h2>mitchellh/cli を使ってファサード・パターンを組んでみる</h2>
<p><a href="https://github.com/mitchellh/cli" title="mitchellh/cli">mitchellh/cli</a> をファサード・パターンとして組みやすくするためのラッパーとして <a href="https://github.com/spiegel-im-spiegel/gofacade" title="spiegel-im-spiegel/gofacade">spiegel-im-spiegel/gofacade</a> パッケージを作ってみた<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup>。</p>
<p>まず,入出力の Context を定義するためのクラスとして <code>Context</code> 構造体を作った。
中身は <a href="https://github.com/mitchellh/cli" title="mitchellh/cli"><code>cli</code></a><code>.BasicUi</code> 構造体を埋め込んでいるだけである<sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">//Context inheritance cli.BasicUi
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">Context</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="c1">//Embedded BasicUi
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="o">*</span><span class="nx">cli</span><span class="p">.</span><span class="nx">BasicUi</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>更に <code>Context</code> 構造体を包含する <code>Facade</code> 構造体を定義する。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// Facade is context of facade
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">Facade</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="c1">//UI defines user interface of the Cli
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">Cxt</span> <span class="o">*</span><span class="nx">Context</span>
</span></span><span class="line"><span class="cl"> <span class="c1">// commands is a mapping of subcommand names to a factory function
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">commands</span> <span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="nx">cli</span><span class="p">.</span><span class="nx">CommandFactory</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><code>Facade</code> 構造体には <a href="https://github.com/mitchellh/cli" title="mitchellh/cli"><code>cli</code></a><code>.CommandFactory</code> のリストを含んでいる。
このリストに <a href="https://github.com/mitchellh/cli" title="mitchellh/cli"><code>cli</code></a><code>.Command</code> インタフェースに適合するインスタンスを追加するための関数がこれ<sup id="fnref:7"><a href="#fn:7" class="footnote-ref" role="doc-noteref">7</a></sup>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// AddCommand add command
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">f</span> <span class="o">*</span><span class="nx">Facade</span><span class="p">)</span> <span class="nf">AddCommand</span><span class="p">(</span><span class="nx">name</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">command</span> <span class="nx">cli</span><span class="p">.</span><span class="nx">Command</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">f</span><span class="p">.</span><span class="nx">commands</span><span class="p">[</span><span class="nx">name</span><span class="p">]</span> <span class="p">=</span> <span class="kd">func</span><span class="p">()</span> <span class="p">(</span><span class="nx">cli</span><span class="p">.</span><span class="nx">Command</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">command</span><span class="p">,</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>実際にファサードを実行するには以下の関数を起動する。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// Run facade
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">f</span> <span class="o">*</span><span class="nx">Facade</span><span class="p">)</span> <span class="nf">Run</span><span class="p">(</span><span class="nx">appName</span><span class="p">,</span> <span class="nx">version</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">args</span> <span class="p">[]</span><span class="kt">string</span><span class="p">)</span> <span class="p">(</span><span class="kt">int</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">c</span> <span class="o">:=</span> <span class="nx">cli</span><span class="p">.</span><span class="nf">NewCLI</span><span class="p">(</span><span class="nx">appName</span><span class="p">,</span> <span class="nx">version</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nx">Args</span> <span class="p">=</span> <span class="nx">args</span>
</span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nx">Commands</span> <span class="p">=</span> <span class="nx">f</span><span class="p">.</span><span class="nx">commands</span>
</span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nx">HelpWriter</span> <span class="p">=</span> <span class="nx">f</span><span class="p">.</span><span class="nx">Cxt</span><span class="p">.</span><span class="nx">Writer</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">c</span><span class="p">.</span><span class="nf">Run</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>他に細かい道具はあるが,まぁこんなもんだろう。</p>
<h3>spiegel-im-spiegel/gofacade の実装例</h3>
<p><a href="https://github.com/spiegel-im-spiegel/gofacade" title="spiegel-im-spiegel/gofacade">spiegel-im-spiegel/gofacade</a> パッケージの実装例として <a href="https://github.com/spiegel-im-spiegel/astrocalc" title="spiegel-im-spiegel/astrocalc">spiegel-im-spiegel/astrocalc</a> パッケージに CLI ツールを追加してみた。
こんな感じのコマンドラインを構成してみる。</p>
<pre tabindex="0"><code>$ astrocalc [-v | -h] mjdn <year> <month> <day>
</code></pre><p>まず <code>astrocalc mjdn</code> サブコマンドを以下のように定義する。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">mjdnCmd</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"flag"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"strconv"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"strings"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"time"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/spiegel-im-spiegel/astrocalc/mjdn"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/spiegel-im-spiegel/gofacade"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Name は mjdn コマンド名を定義する
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">const</span> <span class="nx">Name</span> <span class="kt">string</span> <span class="p">=</span> <span class="s">"mjdn"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Context は mjdn コマンドのコンテキストを定義する
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">Context</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="c1">//Embedded gofacade.Context
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="o">*</span><span class="nx">gofacade</span><span class="p">.</span><span class="nx">Context</span>
</span></span><span class="line"><span class="cl"> <span class="c1">//AppName にはアプリケーション名を格納する
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">AppName</span> <span class="kt">string</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Command は Context のインスタンスを返す
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">Command</span><span class="p">(</span><span class="nx">cxt</span> <span class="o">*</span><span class="nx">gofacade</span><span class="p">.</span><span class="nx">Context</span><span class="p">,</span> <span class="nx">appName</span> <span class="kt">string</span><span class="p">)</span> <span class="o">*</span><span class="nx">Context</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="o">&</span><span class="nx">Context</span><span class="p">{</span><span class="nx">Context</span><span class="p">:</span> <span class="nx">cxt</span><span class="p">,</span> <span class="nx">AppName</span><span class="p">:</span> <span class="nx">appName</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Synopsis は mjdn コマンドの概要を返す
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">c</span> <span class="nx">Context</span><span class="p">)</span> <span class="nf">Synopsis</span><span class="p">()</span> <span class="kt">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="s">"Calculation of Modified Julian Day"</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Help は mjdn コマンドのヘルプを返す
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">c</span> <span class="nx">Context</span><span class="p">)</span> <span class="nf">Help</span><span class="p">()</span> <span class="kt">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">helpText</span> <span class="o">:=</span> <span class="s">`
</span></span></span><span class="line"><span class="cl"><span class="s">Usage: astrocalc mjdn <year> <month> <day>
</span></span></span><span class="line"><span class="cl"><span class="s">`</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprintln</span><span class="p">(</span><span class="nx">strings</span><span class="p">.</span><span class="nf">TrimSpace</span><span class="p">(</span><span class="nx">helpText</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Run は mjdn コマンドを実行する
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">c</span> <span class="nx">Context</span><span class="p">)</span> <span class="nf">Run</span><span class="p">(</span><span class="nx">args</span> <span class="p">[]</span><span class="kt">string</span><span class="p">)</span> <span class="kt">int</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">flags</span> <span class="o">:=</span> <span class="nx">flag</span><span class="p">.</span><span class="nf">NewFlagSet</span><span class="p">(</span><span class="nx">Name</span><span class="p">,</span> <span class="nx">flag</span><span class="p">.</span><span class="nx">ContinueOnError</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">flags</span><span class="p">.</span><span class="nx">Usage</span> <span class="p">=</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nf">Error</span><span class="p">(</span><span class="nx">c</span><span class="p">.</span><span class="nf">Help</span><span class="p">())</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="c1">// Parse commandline flag
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">flags</span><span class="p">.</span><span class="nf">Parse</span><span class="p">(</span><span class="nx">args</span><span class="p">);</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">gofacade</span><span class="p">.</span><span class="nx">ExitCodeError</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">flags</span><span class="p">.</span><span class="nf">NArg</span><span class="p">()</span> <span class="o">!=</span> <span class="mi">3</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nf">Error</span><span class="p">(</span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprintf</span><span class="p">(</span><span class="s">"年月日を指定してください\n\n%s"</span><span class="p">,</span> <span class="nx">c</span><span class="p">.</span><span class="nf">Help</span><span class="p">()))</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">gofacade</span><span class="p">.</span><span class="nx">ExitCodeError</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">argsStr</span> <span class="o">:=</span> <span class="nx">flags</span><span class="p">.</span><span class="nf">Args</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">ymd</span> <span class="p">=</span> <span class="nb">make</span><span class="p">([]</span><span class="kt">int</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span><span class="p">,</span> <span class="nx">arg</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">argsStr</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">num</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">strconv</span><span class="p">.</span><span class="nf">Atoi</span><span class="p">(</span><span class="nx">arg</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nf">Error</span><span class="p">(</span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprintln</span><span class="p">(</span><span class="nx">err</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">gofacade</span><span class="p">.</span><span class="nx">ExitCodeError</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">ymd</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="p">=</span> <span class="nx">num</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">tm</span> <span class="o">:=</span> <span class="nx">time</span><span class="p">.</span><span class="nf">Date</span><span class="p">(</span><span class="nx">ymd</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="nx">time</span><span class="p">.</span><span class="nf">Month</span><span class="p">(</span><span class="nx">ymd</span><span class="p">[</span><span class="mi">1</span><span class="p">]),</span> <span class="nx">ymd</span><span class="p">[</span><span class="mi">2</span><span class="p">],</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">time</span><span class="p">.</span><span class="nx">UTC</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nf">Output</span><span class="p">(</span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprint</span><span class="p">(</span><span class="nx">mjdn</span><span class="p">.</span><span class="nf">DayNumber</span><span class="p">(</span><span class="nx">tm</span><span class="p">)))</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">gofacade</span><span class="p">.</span><span class="nx">ExitCodeOK</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>ポイントは <code>astrocalc mjdn</code> サブコマンド用の context 情報として <code>Context</code> 構造体を定義しているところ。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// Context は mjdn コマンドのコンテキストを定義する
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">Context</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="c1">//Embedded gofacade.Context
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="o">*</span><span class="nx">gofacade</span><span class="p">.</span><span class="nx">Context</span>
</span></span><span class="line"><span class="cl"> <span class="c1">//AppName にはアプリケーション名を格納する
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">AppName</span> <span class="kt">string</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><a href="https://github.com/spiegel-im-spiegel/gofacade" title="spiegel-im-spiegel/gofacade"><code>gofacade</code></a><code>.Context</code> 構造体を埋め込みフィールドで定義しているのがお分かりだろうか。
<a href="https://github.com/spiegel-im-spiegel/gofacade" title="spiegel-im-spiegel/gofacade"><code>gofacade</code></a><code>.Context</code> はさらに <a href="https://github.com/mitchellh/cli" title="mitchellh/cli"><code>cli</code></a><code>.BasicUi</code> 構造体を埋め込んでいる。
また <code>Context</code> 構造体は <a href="https://github.com/mitchellh/cli" title="mitchellh/cli"><code>cli</code></a><code>.Command</code> インタフェースの特化クラスとして実装している。</p>
<p>では,この <code>Context</code> 構造体を使ってアプリケーションの起動部分を書いてみよう。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"os"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/spiegel-im-spiegel/astrocalc/internal/mjdnCmd"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/spiegel-im-spiegel/gofacade"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">const</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="c1">// Name はアプリケーション名を定義する
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">Name</span> <span class="kt">string</span> <span class="p">=</span> <span class="s">"astrocalc"</span>
</span></span><span class="line"><span class="cl"> <span class="c1">// Version はアプリケーションのバージョン番号を定義する
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">Version</span> <span class="kt">string</span> <span class="p">=</span> <span class="s">"0.1.0"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">setupFacade</span><span class="p">(</span><span class="nx">cxt</span> <span class="o">*</span><span class="nx">gofacade</span><span class="p">.</span><span class="nx">Context</span><span class="p">)</span> <span class="o">*</span><span class="nx">gofacade</span><span class="p">.</span><span class="nx">Facade</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fcd</span> <span class="o">:=</span> <span class="nx">gofacade</span><span class="p">.</span><span class="nf">NewFacade</span><span class="p">(</span><span class="nx">cxt</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fcd</span><span class="p">.</span><span class="nf">AddCommand</span><span class="p">(</span><span class="nx">mjdnCmd</span><span class="p">.</span><span class="nx">Name</span><span class="p">,</span> <span class="nx">mjdnCmd</span><span class="p">.</span><span class="nf">Command</span><span class="p">(</span><span class="nx">cxt</span><span class="p">,</span> <span class="nx">Name</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">fcd</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">cxt</span> <span class="o">:=</span> <span class="nx">gofacade</span><span class="p">.</span><span class="nf">NewContext</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stdin</span><span class="p">,</span> <span class="nx">os</span><span class="p">.</span><span class="nx">Stdout</span><span class="p">,</span> <span class="nx">os</span><span class="p">.</span><span class="nx">Stderr</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fcd</span> <span class="o">:=</span> <span class="nf">setupFacade</span><span class="p">(</span><span class="nx">cxt</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rtn</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">fcd</span><span class="p">.</span><span class="nf">Run</span><span class="p">(</span><span class="nx">Name</span><span class="p">,</span> <span class="nx">Version</span><span class="p">,</span> <span class="nx">os</span><span class="p">.</span><span class="nx">Args</span><span class="p">[</span><span class="mi">1</span><span class="p">:])</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">cxt</span><span class="p">.</span><span class="nf">Error</span><span class="p">(</span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprintln</span><span class="p">(</span><span class="nx">err</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">os</span><span class="p">.</span><span class="nf">Exit</span><span class="p">(</span><span class="nx">rtn</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><code>setupFacade()</code> 関数でファサードを作成し, <code>main()</code> 関数で実行しているのが分かると思う。
では実際に compile & run してみよう。</p>
<pre tabindex="0"><code>C:\workspace\astrocalc> pushd C:\workspace\astrocalc\src\github.com\spiegel-im-spiegel\astrocalc
C:\workspace\astrocalc\src\github.com\spiegel-im-spiegel\astrocalc> glide up
[INFO] Fetching updates for github.com/spiegel-im-spiegel/gofacade.
[INFO] Found glide.yaml in C:\workspace\astrocalc\src\github.com\spiegel-im-spiegel\astrocalc\vendor\github.com\spiegel-im-spiegel\gofacade/glide.yaml
[INFO] Fetching updates for github.com/mitchellh/cli.
[INFO] Scanning github.com/mitchellh/cli for dependencies.
[INFO] ==> Unknown github.com/bgentry/speakeasy (github.com/bgentry/speakeasy)
[INFO] ==> Unknown github.com/mattn/go-isatty (github.com/mattn/go-isatty)
[INFO] Fetching updates for github.com/bgentry/speakeasy.
[INFO] Fetching updates for github.com/mattn/go-isatty.
[INFO] Scanning github.com/bgentry/speakeasy for dependencies.
[INFO] Scanning github.com/mattn/go-isatty for dependencies.
[INFO] Project relies on 4 dependencies.
[INFO] Writing glide.lock file
C:\workspace\astrocalc\src\github.com\spiegel-im-spiegel\astrocalc> popd
C:\workspace\astrocalc> go install -v github.com/spiegel-im-spiegel/astrocalc
github.com/spiegel-im-spiegel/astrocalc/mjdn
github.com/spiegel-im-spiegel/astrocalc/vendor/github.com/bgentry/speakeasy
github.com/spiegel-im-spiegel/astrocalc/vendor/github.com/mattn/go-isatty
github.com/spiegel-im-spiegel/astrocalc/vendor/github.com/mitchellh/cli
github.com/spiegel-im-spiegel/astrocalc/vendor/github.com/spiegel-im-spiegel/gofacade
github.com/spiegel-im-spiegel/astrocalc/internal/mjdnCmd
github.com/spiegel-im-spiegel/astrocalc
C:\workspace\astrocalc> bin\astrocalc.exe -h
usage: astrocalc [--version] [--help] <command> [<args>]
Available commands are:
mjdn Calculation of Modified Julian Day
C:\workspace\astrocalc> bin\astrocalc.exe -h mjdn
Usage: astrocalc mjdn <year> <month> <day>
C:\workspace\astrocalc> bin\astrocalc.exe mjdn 2015 1 1
57023 (2015-01-01)
</code></pre><p>よしよし。
うまくいった。
なお <a href="https://github.com/Masterminds/glide" title="Masterminds/glide">glide</a> については「<a href="https://text.baldanders.info/golang/vendoring-with-glide/">Glide で Vendoring</a>」を参考にどうぞ。</p>
<h2>ブックマーク</h2>
<ul>
<li><a href="http://yuuki.hatenablog.com/entry/go-cli-unix">Go言語によるCLIツール開発とUNIX哲学について - ゆううきブログ</a></li>
<li><a href="http://tellme.tokyo/post/2015/06/23/unix_cli_tool_go/">開発者から見た UNIX 哲学とコマンドラインツールと Go言語 - TELLME.TOKYO</a></li>
<li><a href="http://ryochack.hatenablog.com/entry/2013/04/17/232753">Go言語のflagパッケージを使う - uragami note</a></li>
<li><a href="http://deeeet.com/writing/2015/04/17/panicwrap/">Go言語のCLIツールのpanicをラップしてクラッシュレポートをつくる | SOTA</a></li>
<li><a href="http://qiita.com/kumatch/items/258d7984c0270f6dd73a">flag 並にシンプルでより強力な CLI パーサ kingpin の紹介 - Qiita</a></li>
<li><a href="https://github.com/spf13/cobra">spf13/cobra: A Commander for modern Go CLI interactions</a> : <a href="https://github.com/docker">Docker</a> や <a href="https://github.com/gohugoio/hugo">Hugo</a> の <a href="https://github.com/spf13">Steve Francia (spf13)</a> さんが公開しているパッケージ。そのうち試したい → <a href="https://text.baldanders.info/golang/estimate-of-pi-2-cli/" title="モンテカルロ法による円周率の推定(その2 CLI)">試してみた</a></li>
</ul>
<p><a href="https://text.baldanders.info/golang/bookmark/">Go 言語に関するブックマーク集はこちら</a>。</p>
<h2>参考図書</h2>
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/B00I8ATHGW?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/41mh5r0NwLL._SL160_.jpg" width="126" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/B00I8ATHGW?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">増補改訂版 Java言語で学ぶデザインパターン入門</a></dt>
<dd>結城 浩 (著)</dd>
<dd>SBクリエイティブ 2004-06-18 (Release 2014-03-12)</dd>
<dd>Kindle版</dd>
<dd>B00I8ATHGW (ASIN)</dd>
<dd>評価<abbr class="rating fa-sm" title="5"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i></abbr></dd>
</dl>
<p class="description">結城浩さんによる通称「デザパタ本」。 Java 以外でも使える優れもの。</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2016-01-05">2016-01-05</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- 増補改訂版 Java言語で学ぶデザインパターン入門 -->
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>翻訳は <a href="https://ja.wikipedia.org/wiki/UNIX%E5%93%B2%E5%AD%A6">Wikipedia の記事</a>から拝借させてもらった。ちなみに <a href="https://ja.wikipedia.org/wiki/Wikipedia:Text_of_Creative_Commons_Attribution-ShareAlike_3.0_Unported_License">Wikipedia のコンテンツは基本的には by-sa ライセンスで公開</a>されている。 <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:2">
<p>ファサード自身はサブシステムの詳細を知らず context 情報を渡して処理をキックするのみなのが特徴。サブシステム側はファサードに依存せず, context 情報さえあれば処理可能にするのがコツである。 <a href="#fnref:2" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:3">
<p>「<a href="https://text.baldanders.info/golang/estimate-of-pi-2-cli/">モンテカルロ法による円周率の推定(その2 CLI)</a>」では <a href="https://github.com/spf13/cobra" title="spf13/cobra: A Commander for modern Go CLI interactions">spf13/cobra</a> パッケージを紹介している。 <a href="#fnref:3" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:4">
<p>型(<a href="https://go.dev/ref/spec#Properties_of_types_and_values" title="Properties of types and values">type</a>)については「<a href="https://text.baldanders.info/golang/object-oriented-programming/">Go 言語における「オブジェクト」</a>」を参照のこと。 <a href="#fnref:4" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:5">
<p><a href="https://github.com/spiegel-im-spiegel/gofacade" title="spiegel-im-spiegel/gofacade">spiegel-im-spiegel/gofacade</a> は <a href="https://creativecommons.org/publicdomain/zero/1.0/">CC0</a> で公開している。個人的には実証コードの扱いなので,(著作権情報の書き換えも含めて)自由に利用して 構わない。 <a href="#fnref:5" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:6">
<p>なんでこんな回りくどいことをしているかというと, <a href="https://github.com/mitchellh/cli" title="mitchellh/cli">mitchellh/cli</a> パッケージをカプセル化したかったから。 <a href="#fnref:6" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:7">
<p><a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>では関数は全て関数閉包(closure)として機能する。 <a href="#fnref:7" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>