プラグイン機能の開発から学びなおした依存性逆転の原則
システムのプラグイン機能開発から依存性逆転の原則について再認識できたのでメモ。
依存性の逆転といえば「抽象 (インターフェース) に依存せよ」なんて言われるが、その本質がようやく理解できた気がする。
依存性逆転の原則とは
システム開発の設計指針で有名な SOLID の原則の一つとして広く知られる原則。 依存性逆転の原則は簡単に言えば以下の2つを守ること。
- 上位モジュールは下位モジュールの実装に依存しない
- 双方ともに抽象(インターフェース)に依存する
ここで言う上位モジュールは使う側、下位モジュールは使われる側という感じで捉えておく。
よく紹介される例として、ClassA
が ClassB
に依存しているときに、
ClassB
のインターフェース IClassB
を定義して、そこに依存させるようにしましょう、というものがある。
もっと詳しく知りたい方は書籍や以下のリンクを参考に。
プラグイン機能を組み込んだシステムの開発
この話の前提となるシステムについてさらっと解説。 ある GUI つきのシステムに第三者がプラグインを組み込めるような仕組みを追加したい。 プラグインのイメージは VS Code や Chrome などの拡張機能を想像してほしい。
リッチなプラグインを作ろうと思えば、それ専用の GUI が欲しくなるだろう。 プラグイン用の GUI をオリジナルのシステムに表示する仕組みはどう実装すべきだろうか?
View のインターフェースを公開する(ダメな例)
例えば GUI 側に View 変更用の IF を作ってプラグインから制御するような実装も可能である。 ただし、設計の原則的によくないことは直感的に理解できるだろう
単純な MVC モデルで考えると、システムの GUI は View でプラグインは Model に当たる View が Model を参照することはあっても Model から View を制御するのはありえない。
なぜダメなのか。 Model から見れば、View の仕様に関する詳しい理解が必要となる。 View から見れば、Model でどのような使われ方をされるか想定して View を実装・変更しなければならなくなる。
しかしプラグイン用の GUI を開発できないとなると、自由度が低くなってしまう。 なんとかして、プラグイン用の GUI を制御できないだろうか?
プラグインの設定ファイルをシステムが読み込む(依存性逆転)
そこで View の IF を公開するのではなく、プラグインに所定のフォーマットに従うことを強制する。 例えば設定ファイルのようなものを使うのがいいだろう。 システムはプラグインの設定ファイルを読み込んで、その内容に基づいてプラグインの GUI を表示するようにする。
ちなみにChrome 拡張機能の開発などもこの形を採用している。
一見すると、やっていることはあまり変わっていない。 プラグイン側から見ると、 View の IF を呼ぶ実装をするか設定ファイルを作るかどうかの違いである。 ただし依存関係を見ると、View とプラグインが設定ファイルに依存するようになっている。 設定という抽象に依存することで、View とプラグインが互いに影響せずに実装することが可能となった。
これにより、View の制御周りをプラグイン側に開放せずに閉じ込めておくことが可能になる。 そして、プラグイン側の View を設定ファイルというフォーマットで強制することが可能となった。
またプラグインの設計者からすると、設定ファイルと言う形で View の知識が必要になる点は変わらないが、 プラグイン内部から View の関心事を排除することが可能になる。
できることを変えずに、システムの安定性や変更容易性が大きく向上するだろう。
依存性逆転の本質は下位モジュールの振る舞いを約束すること
今回の内容は、業務で扱うシステムにプラグインを追加できる仕組みを用意するために、Chrome や VSCode の拡張機能の仕組みを輸入しようとしたことがきっかけで考え始めた。
拡張機能には設定ファイルが必要なのか、なんでこういう実装にしたんだろう。。。 あ、これってもしかして依存性逆転の原則の本質なのでは? そんな感じで、長年の疑問が腹落ちした感覚を得られた。
今までは、依存性逆転の原則とは「上位モジュールのインターフェースに下位モジュールが依存する」という言葉だけを覚えていた。 極端なことを言ってしまえば、インターフェースさえ作っていればそれでいいんでしょ、というレベルの理解。
今回の知見から、上位モジュールが下位モジュールの振る舞いを約束することが依存性逆転の原則の本質であると気づけた。
知識を蓄えるのも大事だが、やはり実践こそが最高の学びの場だ。
それでは。