第 5 章: パッケージ管理と静的解析¶
5.1 はじめに¶
この章では Clojure プロジェクトのパッケージ管理と静的コード解析ツールについて学びます。
5.2 Leiningen によるパッケージ管理¶
project.clj¶
Leiningen のプロジェクト設定ファイル project.clj で依存関係やプラグインを管理します。
(defproject fizzbuzz "0.1.0-SNAPSHOT"
:description "FizzBuzz - TDD practice in Clojure"
:dependencies [[org.clojure/clojure "1.11.1"]]
:plugins [[lein-cloverage "1.2.4"]
[lein-kibit "0.1.8"]
[jonase/eastwood "1.4.3"]]
:main ^:skip-aot fizzbuzz.core
:target-path "target/%s"
:profiles {:uberjar {:aot :all}})
依存関係の管理¶
# 依存関係の取得
$ lein deps
# 依存関係のツリー表示
$ lein deps :tree
Leiningen は Maven リポジトリと Clojars から依存関係を自動的にダウンロードします。
5.3 静的コード解析¶
Eastwood — 静的コード解析¶
Eastwood はコードの潜在的な問題を検出する Linter です。
$ lein eastwood
検出する主な問題:
- 未使用の変数や名前空間
- 間違った関数呼び出し
- 非推奨な構文の使用
- リフレクション警告
Kibit — イディオム検査¶
Kibit は Clojure らしい書き方を提案するツールです。
$ lein kibit
提案の例:
;; Before(Kibit が改善を提案)
(if (= x true) :yes :no)
;; After(Clojure イディオム)
(if x :yes :no)
;; Before
(not (empty? coll))
;; After
(seq coll)
cljfmt — コードフォーマッター¶
cljfmt は Clojure コードのフォーマットを統一するツールです。
# フォーマットチェック
$ lein cljfmt check
# 自動フォーマット
$ lein cljfmt fix
5.4 コードカバレッジ¶
静的コード解析による品質の確認ができました。動的なテストに関しては コードカバレッジ を確認する必要があります。
コード網羅率(Code coverage)は、ソフトウェアテストで用いられる尺度の 1 つである。プログラムのソースコードがテストされた割合を意味する。
— ウィキペディア
Cloverage を使ってテストカバレッジを計測します。
$ lein cloverage
project.clj にプラグインを追加済みです。
:plugins [[lein-cloverage "1.2.4"]]
テスト実行後に target/coverage フォルダが作成されます。その中の index.html を開くとカバレッジ状況を確認できます。
5.5 コード複雑度のチェック¶
静的コード解析では、コーディングスタイルやバグパターンだけでなく、コードの複雑度 もチェックできます。Clojure プロジェクトでは、Bikeshed による品質チェックと独自の循環複雑度チェッカーを組み合わせて、関数の複雑度を制限します。
循環的複雑度(Cyclomatic Complexity)¶
循環的複雑度(サイクロマティック複雑度)とは、ソフトウェア測定法の一つであり、コードがどれぐらい複雑であるかを関数単位で数値にして表す指標。
Clojure では以下のフォームが分岐を生み出し、複雑度を上げます。
| フォーム | 説明 |
|---|---|
cond |
各分岐(:else 除く)で +1 |
if / if-let / if-not |
条件分岐で +1 |
when / when-let / when-not |
条件付き実行で +1 |
case |
各分岐(デフォルト除く)で +1 |
and / or |
短絡評価による分岐で +1 |
loop |
再帰ループで +1 |
try |
例外処理で +1 |
本プロジェクトでは、循環的複雑度を 7 以下 に制限しています。
| 複雑度の範囲 | 意味 |
|---|---|
| 1〜7 | 低複雑度:管理しやすく、問題なし |
| 8〜15 | 中程度の複雑度:リファクタリングを検討 |
| 16 以上 | 高複雑度:関数を分割する必要がある |
;; 循環複雑度が低い例(複雑度: 1)
(defn simple-function [x]
(+ x 1))
;; 循環複雑度が中程度の例(複雑度: 5)
(defn fizzbuzz [n]
(cond
(and (zero? (mod n 3)) (zero? (mod n 5))) "FizzBuzz" ; cond +1, and +1
(zero? (mod n 3)) "Fizz" ; cond +1
(zero? (mod n 5)) "Buzz" ; cond +1
:else (str n))) ; :else は加算なし
;; 基本 1 + cond 3 分岐 + and 1 = 5
Bikeshed — コード品質チェック¶
Bikeshed は Clojure コードの品質を総合的にチェックするツールです。
$ lein bikeshed
チェック項目:
- 行の長さ(100 文字制限)
- 末尾の空白
- ファイル末尾の空行
- clojure.core 関数との名前衝突
- docstring の記述率
project.clj での設定:
:plugins [[lein-bikeshed "0.5.2"]]
:bikeshed {:verbose true
:max-line-length 100}
循環複雑度チェッカー¶
Clojure には PMD のような循環複雑度チェッカーが標準で提供されていないため、プロジェクト固有のチェッカーを dev/complexity_checker.clj に作成しています。
;; dev/complexity_checker.clj(抜粋)
(def ^:private branching-forms
#{'if 'if-let 'if-not 'if-some
'when 'when-let 'when-not 'when-some 'when-first
'and 'or 'loop 'try})
(defn- complexity-of-form
"S 式を再帰的に走査して循環複雑度を計算する"
[form]
(cond
(not (sequential? form)) 0
(empty? form) 0
:else
(let [head (first form)
children-complexity (reduce + (map complexity-of-form (rest form)))]
(+ children-complexity
(cond
(= head 'cond) (count-cond-branches form)
(= head 'case) (count-case-branches form)
(contains? branching-forms head) 1
:else 0)))))
このチェッカーは S 式を再帰的に走査し、分岐フォームの数を数えて循環複雑度を計算します。Clojure のホモイコニシティ(コードがデータ構造である性質)を活かしたアプローチです。
複雑度チェックの実行¶
project.clj にエイリアスを定義して簡単に実行できるようにします。
:aliases {"complexity" ["run" "-m" "complexity-checker"]}
:profiles {:dev {:source-paths ["dev"]}}
$ lein complexity
=== Clojure 循環複雑度チェック (閾値: 7) ===
[ok] fizzbuzz (core.clj) 複雑度: 5
[ok] create-type (type.clj) 複雑度: 3
[ok] fizz-buzz? (model.clj) 複雑度: 2
[ok] fizzbuzz-list (core.clj) 複雑度: 1
[ok] print-fizzbuzz (core.clj) 複雑度: 1
...
関数数: 10, 違反: 0
複雑度チェック: 成功
Makefile でも実行できます。
$ make complexity
複雑度チェックの効果¶
コード複雑度の制限により、以下の効果が得られます。
- 可読性向上 — 小さな関数は理解しやすい
- 保守性向上 — 変更の影響範囲が限定される
- テスト容易性 — 個別機能のテストが簡単
- 自動品質管理 — 複雑なコードの混入を自動防止
現在の FizzBuzz の fizzbuzz 関数は循環複雑度が 5 で、制限値 7 以内に収まっています。第 3 部でプロトコルとマルチメソッドによる設計を進める際も、この制限を意識してコードを書いていきます。
5.6 名前空間と依存関理¶
Clojure では ns マクロで名前空間を定義し、:require で依存関係を宣言します。
(ns fizzbuzz.core
(:require [fizzbuzz.domain.model :as model]
[fizzbuzz.domain.type :as type])
(:gen-class))
名前空間の命名規則:
| 名前空間 | ファイルパス | 役割 |
|---|---|---|
fizzbuzz.core |
src/fizzbuzz/core.clj |
公開 API |
fizzbuzz.domain.model |
src/fizzbuzz/domain/model.clj |
値オブジェクト |
fizzbuzz.domain.type |
src/fizzbuzz/domain/type.clj |
タイプ定義 |
fizzbuzz.application.command |
src/fizzbuzz/application/command.clj |
コマンド |
5.7 まとめ¶
この章では、パッケージ管理と静的コード解析を導入しました。
| ツール | 用途 | コマンド |
|---|---|---|
| Leiningen | 依存関係管理・ビルド | lein deps |
| Eastwood | 静的コード解析 | lein eastwood |
| Kibit | イディオム検査 | lein kibit |
| Bikeshed | コード品質チェック | lein bikeshed |
| 循環複雑度チェッカー | 関数の複雑度計測 | lein complexity |
| cljfmt | コードフォーマット | lein cljfmt check |
| Cloverage | テストカバレッジ | lein cloverage |
この章では以下のことを学びました。
- Leiningen による依存関係管理と
project.cljの構成 - Eastwood による静的コード解析
- Kibit による Clojure イディオムの検査
- cljfmt によるコードフォーマット
- Cloverage によるテストカバレッジ計測
- Bikeshed によるコード品質チェック
- 循環複雑度チェッカー による関数複雑度の自動検出
nsマクロによる名前空間と依存関係の管理
次の章では、これらのタスクをまとめて実行できるタスクランナーと、CI/CD パイプラインの構築について解説します。