👆

[TouchDesigner] DI(依存性の注入)をする方法を考える

2020/10/16に公開

TouchDesignerで同一プロジェクトを複数人で分担して制作していく場合など、変更に柔軟な設計にしておくのが望ましい。
そこで、ソフトウェア設計における「DI(依存性の注入)」というパターンをTouchDesignerに転用できないか検討する。

DI(依存性の注入)とは

オブジェクトA がオブジェクトB を参照する場合、AがBを直接参照するよう実装してしまうとプログラムが変更に弱くなってしまう。
例えば参照先をオブジェクトB からオブジェクトB' に変更したい場合、オブジェクトAの実装までも変更しなくてはならなくなる。

そこで、オブジェクトA がオブジェクトB を直接参照するのではなく、オブジェクトB のインターフェースを参照するよう実装し、主要な処理の外でインターフェースへオブジェクトB を注入する。
こうすることにより、B → B' へ変更する場合もオブジェクトAの実装自体には手を入れる必要がなく、注入元を変更するだけでよくなる。

このような方法を DI(Dependency Injection / 依存性の注入) と呼ぶ。

DIを行う上で重要なポイントは2つある。

  1. 主要な処理の外側にて実装を注入する
  2. インターフェースとして操作対象(関数やパラメータ)の定義をする

この2つの処理を実装することによりDIを実現することができる。

一般的なオブジェクト指向プログラミング環境では、①はプログラム起動時等に一括で注入することにより、②はインターフェースクラスを用いることにより、それぞれ実現している。

TouchDesignerにおけるDI

ところが、TouchDesignerではオブジェクト指向プログラミングでいうところのインターフェースクラスに当たる概念がない。
そのため、それを実装注入の手段に用いることもインターフェースとして用いることもできない。

したがって、TouchDesignerで扱うことが可能な機能を代替え手段として用いる必要がある。
具体例として

  1. Custom Parameter の OPタイプパラメータを用いて実装を注入
  2. Custom Parameter とダックタイピングを駆使してインターフェースとして用いる

といった方法が考えられる。
サンプルプロジェクトを参考に順を追って説明していく。

サンプルプロジェクトの説明

TouchDesigner的DIを説明するために簡単なサンプルプロジェクトを作成した。
サンプルの内容は、UIに配置されたボタンを押すとViewerの画像が切り替わるという単純なものである。
https://github.com/poipoi/sample_di

参照の方向としては、uiがviewerを参照する形で、以下の順序で処理が進む。

  1. ボタンが押される
  2. uiオペレータ内部でviewerオペレータのImageNoパラメータをセットする
  3. ImageNoパラメータに基づいてviewerオペレータ内で画像を切り替える。

このサンプルはDI的に実装しているため、uiの内容に手を加えることなくviewerを同様のインターフェースをもった別のオペレータに差し替える(例えば指定されたImageNoの画像を表示するのではなくNDIで送信するなど)ことが容易に可能である。

1. Custom ParameterのOPパラメータを用いて実装を注入する

Custom Parameterでは様々なタイプから選択してパラメータを作成することができるが、そのなかに「OP」というタイプがある。

このパラメータに他のオペレータのパスを設定することで、そのオペレータを参照することができる。
そのため、これを利用して参照先を切り替えることが可能となる。

また、一度設定したパラメータはtoeファイルに保存され、次回起動時には各コード等が実行される前に反映される。そのため、実装注入の処理自体はTouchDesigner起動時に勝手に行ってくれるのである。

サンプルを例に具体的に説明すると、uiオペレータにCustom Parameterを用いて「Viewerop」というパラメータを作成する。
そして、そこにviewerオペレータを指定するだけである。

これにより、後述するダックタイピンで参照される参照先を指定することができた。

2. Custom Parameterとダックタイピングでインターフェースを実現する

TouchDesignerでオペレータに値を入出力する方法は、in/outオペレータを用いて直接オペレータ同士を接続する方法と、Custom Parameterを用いる方法がある。
ここでは、このあと説明するダックタイピングを比較的やりやすい Custom Parameter の方をを採用する。

先述の通り、TouchDesignerにはインターフェースクラスのように定義のみを参照できる方法がないため、代替えとしてダックタイピングを用いる。
ダックタイピングでは同様のパラメータさえ存在すれば実装が切り替わっていても実行することが可能であるため、その性質を利用して実装の変更に対応する。

サンプルを例に具体的に説明すると、uiオペレータの内部では、先述のVieweropパラメータを介して以下のようにviewerオペレータへの参照を得る。

viewer = op(parent().par.Viewerop)

viewerオペレータは Imageno というパラメータを所持しているものとして、そのパラメータにボタン入力によって得た画像No.を設定する。

viewer.par.Imageno = val

参照される側のviewerオペレータでは、Imageno パラメータの変更に伴って表示するMovieFileInTOPを切り替えるよう、SelectTOP の top パラメータに以下を展開して指定する。

'moviefilein{}'.format(parent().par.Imageno + 1)

仮に Viewerop パラメータに注入されるオペレータが変更されたとしても、そのオペレータが Imageno というパラメータを持ってさえすれば、正しく実行することが可能である。

Discussion