オブジェクト指向と手続き型、あるいは宣言型プログラミングとの比較
手続き型と宣言型の違い
手続き型は How、どうやって行うのかを書いたプログラミング 宣言型は What、何を成し遂げたいのかを書いたプログラミング
手続き型は副作用の制御 宣言型は副作用のない純粋関数
手続き型は意図がわからない 宣言型は意図を明確にする
宣言型は状態と副作用がない?
状態がないプログラミングなんてあり得るのか? 実際は状態が無いのではなく、データベースなどに隠蔽している ドメインサービス側から見れば、データを保持せずに処理だけするので、ステートレスにすることができる
実際の実装はどうする?
局所的に宣言型らしく、ステートレスで副作用のない実装を心がける 特に Model 層 Value Object で値を Mutable にするのはまさにこの思想に近い Mutable にすることで、Value Object 内の処理による副作用をなくしてしまう。 値に対する処理や条件を ValueObject に閉じ込めるのも宣言的
大局的にはどこかで副作用を起こしてシステム全体を制御しなければならない そのためには、サービスレイヤーで副作用を起こす部分を閉じ込めて、モデル層で副作用を起こさずに宣言的に処理することが大切 結局単一責任の原則に従って、責務を分離するのが大事というわけだ
参考
オブジェクト指向には、カメラがやっとついたころのガラケーのイメージがある - きしだのHatena 現代のオブジェクト指向の class の割れ窓化と宣言的プログラミング JavaScriptで覗き見る関数型プログラミングの世界|F Lab|Fixel株式会社 宣言型プログラミングとは何かをJavaとElmで考えてみる(前編) - Qiita 宣言型、命令型、手続き型の違いを考える (zenn.dev)
プラグイン機能の開発から学びなおした依存性逆転の原則
システムのプラグイン機能開発から依存性逆転の原則について再認識できたのでメモ。
依存性の逆転といえば「抽象 (インターフェース) に依存せよ」なんて言われるが、その本質がようやく理解できた気がする。
依存性逆転の原則とは
システム開発の設計指針で有名な 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 の拡張機能の仕組みを輸入しようとしたことがきっかけで考え始めた。
拡張機能には設定ファイルが必要なのか、なんでこういう実装にしたんだろう。。。 あ、これってもしかして依存性逆転の原則の本質なのでは? そんな感じで、長年の疑問が腹落ちした感覚を得られた。
今までは、依存性逆転の原則とは「上位モジュールのインターフェースに下位モジュールが依存する」という言葉だけを覚えていた。 極端なことを言ってしまえば、インターフェースさえ作っていればそれでいいんでしょ、というレベルの理解。
今回の知見から、上位モジュールが下位モジュールの振る舞いを約束することが依存性逆転の原則の本質であると気づけた。
知識を蓄えるのも大事だが、やはり実践こそが最高の学びの場だ。
それでは。