🧜‍♀️

【Mermaid記法】グロい図の治療法【結論、割とあたりまえのことをやっていく】

に公開
  • Mermaid 使ってるけど図が煩雑になりがちな方の助けになるといいな~

背景

  • 開発中ライブラリのプログラム関係図を作って、構成をわかりやすくしよう!
  • Codex に頼んじゃお~😋

結果↓

実際のコード
graph TD

%% API
classDef highlight stroke-width:8px
SoundSystem:::highlight
SoundPresetProperty
SerializedBGMPresetDictionary
SerializedSEPresetDictionary
SerializedListenerPresetDictionary

%% Player
BGMManager
SEManager
ListenerEffector

%% Loader & Cache
ISoundLoader
SoundLoader_Addressables
SoundLoader_Resources
SoundLoader_Streaming
SoundLoaderFactory
ISoundCache
SoundCache
IEvictionStrategy
EvictionStrategy_LRU
EvictionStrategy_TTL
EvictionStrategy_Random
SoundCacheFactory

%% Pool
IAudioSourcePool
AudioSourcePool_Base
AudioSourcePool_FIFO
AudioSourcePool_Strict
AudioSourcePoolFactory

%% Relations
SoundSystem -->|利用| BGMManager
SoundSystem -->|利用| SEManager
SoundSystem -->|利用| ListenerEffector
SoundSystem -->|プリセット読込| SoundPresetProperty
SoundSystem -->|生成| SoundLoaderFactory
SoundLoaderFactory -->|生成| SoundLoader_Addressables
SoundLoaderFactory -->|生成| SoundLoader_Resources
SoundLoaderFactory -->|生成| SoundLoader_Streaming
SoundLoader_Addressables -->|依存| ISoundCache
SoundLoader_Resources -->|依存| ISoundCache
SoundLoader_Streaming -->|依存| ISoundCache
SoundLoader_Addressables -->|間接依存| SoundCacheFactory
BGMManager -->|利用| ISoundLoader
SEManager -->|利用| ISoundLoader
SEManager -->|利用| IAudioSourcePool

SoundCacheFactory -->|生成| SoundCache
SoundCacheFactory -->|生成| EvictionStrategy_LRU
SoundCacheFactory -->|生成| EvictionStrategy_TTL
SoundCacheFactory -->|生成| EvictionStrategy_Random
SoundCache -->|利用| IEvictionStrategy
EvictionStrategy_LRU -->|実装| IEvictionStrategy
EvictionStrategy_TTL -->|実装| IEvictionStrategy
EvictionStrategy_Random -->|実装| IEvictionStrategy

AudioSourcePoolFactory -->|生成| AudioSourcePool_FIFO
AudioSourcePoolFactory -->|生成| AudioSourcePool_Strict
AudioSourcePool_FIFO -->|継承| AudioSourcePool_Base
AudioSourcePool_Strict -->|継承| AudioSourcePool_Base

SoundPresetProperty -->|BGMプリセット| SerializedBGMPresetDictionary
SoundPresetProperty -->|SEプリセット| SerializedSEPresetDictionary
SoundPresetProperty -->|Listenerエフェクトプリセット| SerializedListenerPresetDictionary
  • 🤔🤔🤔…
  • プロンプトをこねこねして Codex 君により良い図を作ってもらうのもいい
  • しかし、その前に「どんな図にすれば見やすいか」を整理したいので本記事を書く

グロ図のチェックポイントと治療法

レイヤー, グループで視覚的に分けられていない

✅解決策 subgraph で囲む
Before

実際のコード
graph TD
  SoundPlayer -->|利用| SoundCache_A
  SoundPlayer -->|利用| SoundCache_B
  SoundPlayer -->|生成| SoundLoader_A
  SoundPlayer -->|生成| SoundLoader_B

After

実際のコード
graph TD
  SoundPlayer
  subgraph Cache
    Cache_A
    Cache_B
  end
  subgraph Loader
    Loader_A
    Loader_B
  end
  SoundPlayer -->|利用| Cache
  SoundPlayer -->|生成| Loader

過剰な情報量

  • 例えば、全体を俯瞰するための図で Strategy の派生を全て表示する必要はないかも

✅解決策 まとめる, 抽象化する
Before

実際のコード
graph TD
  SomeFactory -->|生成| Some_A
  SomeFactory -->|生成| Some_B
  SomeFactory -->|生成| Some_C
  SomeFactory -->|生成| Some_D

After

実際のコード
graph TD
  SomeFactory -->|生成| Some群

矢印の向きがバラバラ

  • 「矢印をたどるだけで視線をめっちゃジグザグ動かす必要があるな…」となったら注意
  • Mermaid では細かく矢印の位置を指定できないので、矢印の交差自体はよくある

✅解決策 グラフ方向を LR, TD など適宜変更する…

  • といったことを考えたが、矢印の発生, 向きはプログラムの関係を表現した結果なので
    あまりに酷い場合、図をどうこうするよりもプログラム設計から見直す必要がある

ラベルに使う単語がバラバラ

✅解決策 用いる語彙を統一する
Before

実際のコード
graph TD
  A -->|利用| B
  A -->|使う| C
  A -->|呼び出す| D
  A -->|Use| E

After

実際のコード
graph TD
  A -->|利用| B
  A -->|利用| C
  A -->|利用| D
  A -->|利用| E

語彙統一の例(私がよく使う C# で考えています)

  • 利用:他クラスのメソッドを呼び出す
  • 生成:Factory クラス, メソッドによるインスタンス生成
  • 依存:コンパイル時に型や設定を参照する
  • 継承:クラス継承
  • 実装:インターフェース実装
  • 必要に応じて「委譲」とかもありカモ

生成AIに頼む場合

  • 「このリポジトリのプログラム関係図作って~」とだけ頼むと、
    記事冒頭のような図が出力されやすい
  • 「このリポジトリのプログラム関係図を機能ごとに作ってください。
    機能同士の関係を考慮して、メイン以外は適宜「Loader群」のようにまとめてください
    1:メイン:CoreSystem
    2:メイン:Loader
    3:メイン:Cache
    4:…」
    と書くとクオリティがマシになる

余談

  • 「治療法」なんて気取ったタイトルにしましたが、
    書いてみると割とあたりまえの内容ばかりでした
  • こういう図って何て言えばいいんですかね。クラス図とは違いますし。

超余談

  • 「マーメイド」ぐらいしか共通点ないんですけど、追憶のマーメイドって曲が好きです

Discussion