深夜テンションで書いてます。
記事はポエムのような内容です。
プログラミングの上で、データの構造が複雑なことになることは多々あります。
その一つが相互参照が必要なデータ構造です。
きれいな設計にすることは難しいですが、少しの工夫で問題を簡単にできます。
#相互参照のデータ構造とは
ここでは相互参照のあるデータ構造とは以下のように定義します。
- DictionaryまたはMapに格納されるデータがKey/Valueの2つの組であり、Key=>Value,Value=>Keyのどちらでも検索する可能性がある。
- Key/Valueの双方がもう一方を引数として使うメソッドを持つ。
この状況では2つのクラスの結合は密です。
いかにして切り離すのが良いのでしょうか。
トップダウン設計で書く
最上位の利用層から考えて、トップダウンでモックを作りながら書く方法です。
2つのデータを管理するクラスを作り、そのクラスに全てを任せます。
この状況では、Key/Valueのそれぞれのクラスは互いを知らずに済みます。
やり取りが必要な場合でも、プリミティブ型や双方に関係がある任意のクラスXと関連だけで書きます。
2つのクラスを管理するクラスは複雑になりますが、どこかで複雑度が発生するのは避けられません。
細かなオブジェクト同士の相互作用で記述するボトムアップ型の設計は、2つのクラスが包含の関係であるならうまくいきます。
(上記の例であれば、Value.Keyの形でデータが含まれるような状況)
#解決手段
任意のインターフェースにまとめる
上位クラスが必要な情報をまとめたインターフェースクラスを作っておくと、複雑度が抑えられます。
これはKey/Valueを管理する何らかのManager的クラスが参照するデータ範囲を限定するためです。
後で追加データが必要になれば、インターフェースを少し拡張しましょう。
ただし、インターフェース同士が相互参照するのは複雑な問題を起こします。
こういう設計をやると、あとでドメイン欠乏症が起こるリスクがあります。
やり取りを関数オブジェクトの奥に押し込む
関数オブジェクトをやり取りする形にすれば、複雑な処理も引数と戻り値だけの問題に落ちます。
関数オブジェクトは1つのメソッドだけを持つインターフェースだと言えます。
関数オブジェクトを使う場合は副作用のない関数で引数・戻り値だけで処理することを徹底しましょう。
副作用を使うようになると、問題が起きた場所を探すのに大変苦労します。
この苦労のため、2回ほど試作品のプログラムを破棄しており、痛い目を見ています。
Templateの力を使う
VScode+JavaScriptの環境では、JSdocのアノテーションでテンプレートを利用したジェネリックプログラミングが可能です。
今回、描画関連の処理はtemplateを多用することで、きれいに解決できました。
JSdocではクラスや関数の上に@templateでアノテーションを付けることで、テンプレート化できます。
詳細の説明は省きますが、使ってみると便利です。
#終わりに
今回のケースはRPGツクールMV用にモンスター図鑑プラグインを作る際に生じた問題の備忘録として記載しています。
関数オブジェクトのやり取りだけで解決できたのは、描画・検索関連の処理を別のプログラムに押し込んでライブラリ化したためです。
リレーショナルデータベースの再発明のようなものを作ったため、描画関連の処理を意識せずに書くことができました。
このプラグインは後日公開予定です。