🔖

Golangでいい設計を実践するための6つのツール

2022/05/25に公開

概要

Golangを書くにあたり、いい設計のコードを書くための手助けとなるツールを調べたのでまとめます。

想定読者

  • Golangの使い方をある程度わかっている(チュートリアルはやった)
  • いい設計をするための具体的なノウハウに興味がある

記事を書いたきっかけ

引用: https://www.amazon.co.jp/dp/B09Y1MWK9N

最近設計に関して勉強するために「良いコード/悪いコードで学ぶ設計入門」を読みました。
本の中では

  • マジックナンバーを使うな
  • 一つのメソッドの中で多くのことをやりすぎるな

などの言われてみると基本的な注意点が書いてありました。

一方で以下のように、確かにそうなんだけど実際は守れていない注意点にも書かれていました。

  • 単一責任の原則を守ってクラス設計しよう
  • 高凝集なクラスを作ろう

本を読んでわかった気になって今までと同じように悪い設計のコードを書くままではいけないと思い、自分でサンプルプロジェクトを作成し手を動かしながら、どうやれば実際に実践できるのかを考えました。

いいコードを書くためにはどうすればいいか

実際に手を動かして考えてみて結論以下のことができると便利でした

  • 不注意で書いてしまう悪いコードを機械的に検知する
  • クラス設計を俯瞰的に図示してみれる

不注意で書いてしまう悪いコード

  • マジックナンバー
  • 無駄なスコープを持った変数
  • 不必要にpublicになってるフィールド

などは本に書いてあると「そんなこと自分がするわけないじゃん」と思ってしまうのですが、ロジックを考えることに必死になっていると意外に大量にやってしまいます。

しかしこういったコードは機械的に簡単に検知できるのでlinterなどを導入して防ぐことができます

クラス設計を俯瞰的に見る

単一責任の原則を満たしていないことや拡張性の乏しさに気付くためには、図で直感的に把握することが大事だと思います。
反対に、ソースコードを見るだけでは詳細に目がいってしまい、悪いクラス設計に気付きづらいと感じました。

自分でクラス図を書くことは億劫ですが、今はいろんなツールで自動生成できるのでそれらを活用することができます。

不注意で書く悪いコードを機械的に検知する方法

Golandのwarnigns

不注意で書いてしまう悪いコードを検知するための最も手軽な方法はエディタの警告を見ることです。

僕が普段使用しているGolandでは画像のように良くないコードを警告して教えてくれる機能があります。

また自動で修正できる内容ならoption + enterで修正できるのも便利でした。

他にもGolandには便利な機能があるのでお金はかかりますがGolangを書くならおすすめです

https://zenn.dev/uma002/articles/559dce29a809b4

golangcilint

golangcilintを使うとGolandよりも高度な検出をすることができます。

実際に使ってみると以下のような「本で言われるとそれくらいは気をつけてる」と思いがちな悪いコードをうっかり書いていて検出することができました。

https://github.com/physphys/go-sns-login/compare/a13ef53d6296c8afb5ce7aa3337a7e939b639e1b...1698f374e5e588b984dd4866f08a907a4e583a9b

  • マジックナンバー結構使ってた
  • 不要なスコープを持った変数
  • 外部パッケージのerrorをwrapせずに出力
  • HTTPリクエストのタイムアウト設定忘れ
  • 複雑で長い関数(循環的複雑度)

中でも循環的複雑度はcyclopを使うことで計測できたので、多くのことをやりすぎている一つのメソッドを分割して良い設計のコードを書くことに活かせました。

このようにうっかりミスを防ぐだけでなく、「linterに指摘される → いい設計を考案する」の流れが自動的に作れるのでlinterの導入は必須だと感じました。

設計全体を俯瞰的に見る方法

不注意で書くコードはlinterを使うことで検出できましたが、package内のクラスが良い設計になっているのか考えることができませんでした。

「良いコード/悪いコードで学ぶ設計入門」の中でもクラス設計を簡単に図に書きながら実装しようと書かれていましたが、期限に追われながらコードを書いている中ではサボってしまう自分が想像できました。

普段はRuby on Railsを書いており、RubyMineの機能でクラス図を自動生成できますが、Golandにはそのような機能がなさそうだったのでOSSを調べてみました。

  • クラス図を自動生成するgoplantuml
  • パッケージの依存関係を自動生成するgodegraph

が便利でした。

goplantuml

goplantumlを使うことで画像のような図を自動で生成することができます。

  1. structとそのメソッドやinterfaceをガワだけ作ってみる
  2. platnumlを出力して再度設計し直す
  3. コメントだけでどのような処理をするのか書いてみて、再度設計し直す
  4. ある程度自信が持てたら実際に実装を開始する

このような手順を踏むことで、手戻り少なく良いクラス設計をすることができるようになりました。

最初から図を作らずに実装してみると、実装し始めてから無理な実装になっていることやおかしなメソッドの持たせ方になっていることに気づいて手戻りが多くなってしまったので、ガワだけ作って図と睨めっこしながら設計していくのが重要だと感じました。

godegraph

サンプルプロジェクトは勉強用のシンプルなアプリケーションだったのでpackageは少数だけでしたが、実際のプロジェクトになるとパッケージの依存関係も気になるかなと想像しました。

godegraphを使えば簡単にパッケージの依存関係を把握できるので、コードリーディングにも役立ちそうだと思いました。

その他

syntax highlighting

「良いコード/悪いコードで学ぶ設計入門」の中では注意するべき箇所に赤色系のsyntax higlightingを設定して注意を促す手法が紹介されていました。

著者のTweetのように、Golangでも気をつける箇所に赤色系を設定したのですが、個人的にはガチャガチャしていて見辛いと感じました。

なので今は1色だけ決めて以下の箇所にピンク色で設定しています。

箇所 理由
struct exported member 見落としがちな低凝集の対策
package exported variables 見落としがちな低凝集の対策
local values 一つのメソッドでいろんなことをやっていることの対策

go vet

Golangではシンプルな言語仕様と静的解析のツールが標準パッケージで提供されているおかげで、様々な静的解析のツールがOSSで公開されています。

このメルカリさんの記事で解説されているように利用した静的解析のコマンドをインストールして

go vet -vetool=`which 利用したいコマンド名` ./...`

することで簡単に静的解析を行うことができます。

僕は試しに凝集度をLCOMという指標で測るツールを使ってみました。

golangcilintで補えない範囲は自作の静的解析ツールを作ったりしてみても良いのかなと思います。

± % go vet -vettool=`which lcom4` ./...
# go-app-service-test/domain/model
domain/model/user.go:13:2: 'User' has low cohesion, LCOM4 is 2, pairs of methods: [[Name() .name ChangeName()] [ID() .id]]

まとめ

実際にやってみた結果

  • 不注意で書く悪いコードはlinterで検出して良い設計を考える
  • 俯瞰的にクラス設計を見れるように自動生成ツールを使う

のが実際に本の内容を実践していくには大事だと感じました。

他にも良いツールを知っていたらコメントで教えていただけると嬉しいです。

Discussion