musicLineアプリ開発日記

作曲を誰でも楽しく簡単に♪

インスタンスの共有方法(依存性の注入1)

今回はmusicLineの内部実装の話。
Transform(スクロール量等の画面状態)の情報を末端のクラスへどのように共有するか(依存性の注入)について考えてみました。

Transformの共有方法は?



はじめに

musicLineでは、画面上に音符を表示して、タップやスワイプにより音符を編集します。そのため、 音符位置をスクリーン座標系へ座標変換するTransform(スクロール量等の画面状態)の情報が必要になります。

Transformの情報

座標変換の詳細はこちら


Transformは作曲の際に必要となる情報で、ピアノやスクロールエリアをスクロールすることで状態を変更します。そのため、音符や曲の情報というよりは作曲全体の情報なので、ルートのモデルでTransformを保持することが自然だと考えられます。
そうした時に、musicLineの作曲モデルを大まかに表すとこうなります。

作曲モデル

Modelで編集中のSongTransformを保持しています。SongからTrackPhraseNoteと枝分かれしてインスタンス数が多くなり広がっていきます。
ここでTransformNoteに注目するとルートと末端の関係になっています。ルートのModelではTransformを保持しているため、取得・編集することができますが、画面に音符を表示するためには末端のNoteでもTransformが必要です。
ルートで保持しているTransformを末端のNoteへどのように共有するべきでしょう。




インスタンスの共有方法

ルートで保持しているインスタンスを末端でも参照したいような状況で、インスタンスを共有する方法は次の3パターンくらいあると思います。


インスタンスを下層へ伝搬する

下層へ伝搬

利点依存方向が分かりやすい
欠点不要なプロパティが増える


作成時にコンストラクタでインスタンスTransformを渡す方法です。
単純に親から子へTransformを伝搬するだけなので、依存方向がわかりやすいです。依存方向がわかりやすいと、階層的なデータ構造では影響範囲がわかりやすくなります。
しかし、末端までTransformを伝搬していくためには、その間のクラスを跨ぐ必要があります。そのため、階層が深いほど不要にTransformを保持しなければいけない状況が増えます。



インスタンス使用をルートに集約する

ルートに集約

利点参照場所がまとまる
欠点末端だけで処理できない


インスタンスTransformを保持しているクラスを介す方法です。
例えば、音符を表示するときは座標をNoteから直接取得するのではなく、Transformを保持しているModelで座標変換してから取得します。
つまり、末端でTransformを保持するのではなく、関数を使う時にTransformを渡します。これにより、Transformが散らばることを防ぎ、インスタンスの管理が容易になります。
しかし、末端だけではTransformを利用した処理ができず、必ず上層でハンドリングする必要があります。



シングルトンでインスタンスを1つにする

シングルトン利用

利点どこからでも参照可能
欠点複数の状態を持てない


インスタンスTransformをシングルトンとして独立させる方法です。
エンティティに保持させようとはせずに、絶対的な存在として1つのTransformをどこからでもアクセスできるようにします。
しかし、必ず1つの状態しか持てないため、状態が複数になる可能性があるときは使用できません。
また、シングルトンはグローバル変数のようなものなので不必要にシングルトンを利用すると、影響範囲が絞り込めず、依存関係が分かりずらくなります。



まとめ

利点欠点
下層へ伝搬依存方向が分かりやすい不要なプロパティが増える
ルートに集約参照場所がまとまる末端だけで処理できない
シングルトンどこからでも参照可能複数の状態を持てない


基本は単純に下層へ伝搬することで良いですが、階層が深くなるとルートに集約することやシングルトンを検討した方がスッキリすると思います。

今回のパターンのようにTransformで表示するだけの場合は、ルートに関数を作って座標変換後のNote座標を取得することでも良いような気がします。しかしNoteは表示するだけではなく、タップや矩形選択に必要な当たり判定の処理があったり、他にも音符に関する役割を色々と与えたくなると考えられます。その際に、Transformが必要な処理をルートに集約させるとルートクラスが肥大化する問題があります。(サービスクラスに分散させる等の工夫もできますが。。) また、オブジェクト指向的にもNoteに関する処理はNote自身でTransformにアクセスして処理した方がわかりやすくなります(関心の分離)。

シングルトンパターンを利用することで上記の問題は解決できますが、シングルトンを多用すると依存関係が複雑になり、網の目参照や循環参照になってしまう危険性があります。依存関係が複雑にならないようにできる限り階層的なデータ構造にモデリングし、最低限のシングルトンに抑えた方が望ましいです。




終わりに

今回はTransformを末端のNoteへ共有する方法について考えてみました。
シングルトンパターンを使うと綺麗なコードになりますが、依存関係が複雑になるため注意です。そのため、シングルトンを使えばいいというわけではなく、もう少し深く考える必要がありそうです。
次回はシングルトンの上手な活用法について考えてみたいと思います。