00:00
Dependency Inversion Principleは、Solid原則の一つですね。日本語で言うと、依存性逆転の原則ですかね。
Solid原則の中でも、結構私好きな概念で、なんか結構これが一番好きなのかな、という気持ちがするんですけど、
Solid原則の中でも、結構私好きな概念で、なんか結構これが一番好きなのかな、という気持ちがするんですけど、
結構私好きな概念で、なんか結構これが一番DDDで、DDD、ドメイン駆動設計ですね、の思想に近い部分な気がするんですよね。
Dependency Inversion Principleというのはどういうことかというと、その抽象化レイヤー、抽象化層、なんて言ったらいいんでしょうね、抽象化クラスか、
抽象化レイヤーは具体の実装に依存してはならない。
なので、もしそういう実装が必要になったら、具体が抽象に依存するように逆転させましょう、という方針ですよね。
例えばよく思いつく例で言うと、ユーザーというクラスがあって、ユーザーのインスタンスというのがあるわけですね。
実はそのユーザーというクラスは外部サービスに依存していると。
外部サービス、そうですね、例えばGitHubにしましょうか。外部サービスがGitHubですと。
GitHubのユーザー情報に基づいて何かするみたいな処理があるとしますと。
そういった時に、都度都度GitHubにアクセスをする必要がありましょう。
そういった時にGitHubにアクセスするというのが、ユーザーというのから見ると具体的な実装になっちゃうんですよね。
ユーザーというのがGitHubへのアクセスに対して、本当は抽象的なレイヤーのはずなのに、
ユーザーのインスタンスであるメソッドがあって、それがGitHubにアクセスをしてしまうというと、
ソリッド原則のD、つまりDependency Inversion Principleからは外れてしまう。
じゃあどうやってその依存性逆転、つまりユーザーからGitHubアクセスじゃなくて、
逆にGitHubアクセスからユーザーというふうに依存性を逆転させるにはどうすればいいかというと、
DI、つまりDependency Injectionをよく使うのがよくある手法ですよね。
例えばそのユーザーインスタンス作成時に必ずそのGitHubアクセス層みたいな、
GitHubじゃない、外部サービスアクセス層みたいなアクセスをするインスタンスを受け取るようにすると。
そういった時に外部サービスにアクセスするインスタンスってどういうAPIなんですかっていうのをインターフェースを定義しちゃうんですよね。
03:00
それはもうユーザー側で定義しちゃう。
ユーザーとしてはこういうインターフェースを持ったインスタンスがくれば内部のこと知らんけど、とにかくそれでOKですよ。
ユーザーというインスタンスのメソッドからはそのインターフェースのメソッドを呼ぶだけだから、
あとはその実装者なんとかしてねっていう感じにすると。
こうした時にさっきのGitHubアクセス層っていうのが、
そのユーザーのためのインターフェースを定義して、
ユーザーインスタンス、ユーザーのインスタンスを使いたいアプリケーション層とかですか、ちょっとわかんないですけど、
がGitHubのアクセス層をインスタンス化して、そのユーザーをインスタンス化する時にそれをオブジェクトを渡すという風にして、
Dependency Inversion原則が適用されるということになります。
これは好きな理由は、ユーザーっていうのはめちゃくちゃ雑なクラス名だからあんまり好きじゃないんですけど、
ユーザーって本当にビジネスロジックでめちゃくちゃ大事なはずなんですよね。
それがGitHubにアクセス必要ってしてしまうと、本当にビジネスロジックでGitHubがめちゃくちゃ大事ってなってしまうと。
多分、どう転んでもGitHubへの依存ってもう避けられないよね。ビジネス上絶対GitHubへの依存って必要だよねっていうのもわかるっちゃわかるんですけど、
テストのしやすさという点ではやっぱりちょっと残念だなという気がしますね。
テストっていうのは2つの意味があって、ユニットテストのテストと、
例えばIRBとか、Rails使ってるならRailsCとかですよね。
コンソール上で試したいって言った時に、
もしGitHubとユーザーが密切につながっていると、すどすどGitHubにアクセスしてしまうと。
でも、例えばコンソール上で試したいことってGitHubへのアクセスじゃなくて、
GitHubのアクセスというか、外部サービスから返ってきた情報を元にしたロジックの確からしさみたいなのを検証したりとかしたいと思うんですよね。
テストもしっかりですというのがあって、ちょっとやりすぎなんですよね。
単体テストに関して言えば、例えばRスペックを使っているんだったら、
GitHubへのアクセスをMockとかStubすればいいので、問題はほとんどないですよね。
ないんですけど、やっぱりそのMockとかStubって使いたくなる時点で、なんか疑わしさを感じますよね。
まあそんな感じで、Dependency Inversion Principle、私結構好きなわけですね。
ここからが本題でして、Dependency Inversion Principle、DIPとかよく略されたりしますけど、
06:02
これを実現するためには、さっきインターフェースって言いましたけど、
インターフェースがあるとめちゃくちゃやりやすいんですよね。
ただですね、RubyにはインターフェースっていうのがRubyの言語仕様としてはないと思ってます。ないですよね。
インターフェースに似たようなことをもちろん実現することができて、
例えば、抽象クラスがあるので、その抽象クラスで定義されているメソッドを
定義しないとダメですよみたいにして宣言しておくとか、
いずれにしろ結構取り決めベースって言ったらいいんでしょうね。
ダックタイピングみたいな感じですよね。
RBI、ディペンデンシーインジェクションの段階で渡されたオブジェクトで
何とかっていうメソッドを呼ぶよっていう風にしても書いてるわけですよね。
これは強制力がないんですよね。実行するまでわかんない。
本当にそれが定義されているかわかんない。だからテスト必要っていうのはあるんですけど、
書いてる最中にそのメソッドが必要だよねっていう情報がわかると非常に開発効率は良くなりますよね。
そういったわけでRBSとかの型ですよね。型っていうのは便利になりそうだなっていう予感がします。
RBS、ここ数年の話をRuby会議とかで聞いていると、
最近はプロダクティビティにフォーカスしていて、
VSコードとの連携だとか、LSPとの連携だとか、
かなり便利さっていうのを追求していると思うんですよね。
それはそれ自体で非常に大事だと思っています。
これでちょっと私が危惧しているのは、RBSってもう本当にただ便利にするだけ?みたいな。
要はそのメソッドとかのタイプシグネチャーがあるから、
エディターフレンドリーで、このメソッドはこういう定義なのから、例えばストリングは渡せないよねとか、
そういった情報をコードを書いている最中に理解したい。
それは非常にわかるし有意義なことなんですけど、もうちょっと発展させたいんですよね。
というのも、例えば型を使って設計をするみたいなのってあってもいいと思っていて、
それがDependency Inversion Principleって結構型を最初に定義するアプローチだと思うんですよね。
つまりユーザーっていうクラスはこういうオブジェクトが必要です。
そのオブジェクトはこのインターフェースを実装していなければいけません。
このインターフェースっていうのはこういうタイプシグネチャーですよっていう宣言をするんですよね。
これはRBSだと実現できそうな気がするんですよね。
09:02
RBSって確かインターフェースを定義することができたので、非常にそこが相性いいなと思っていると。
もちろんそのRBSの他の利点とか書き方とか、
あとそもそも別ファイルに書くのがどうなのとか、いろいろなご意見はあるとは思うんですけど、
ディペンデンシーインバージョンプリンシップをやりたいって別にRubyでも全然あると思っていて、
そういった時にRBSでこのメソッドクラス、例えばさっきのユーザーですね。
ユーザーはこのインターフェースを定義したオブジェクトを必要としてますっていう宣言を書ける。
最初に書いちゃうというのができると、開発が非常にはかどるんじゃないかなって思ってますし、
さっきのユーザーとGitHubへのアクセス、つまり抽象化層が具体の実装に依存するということに対する意識づけができそうな気がするんですよね。
なのでRBSとディペンデンシーインバージョンプリンシップを組み合わせると、
もうちょっと開発がいい感じになりそうだなっていう予感があったので、
今年Ruby会議のLTのCFPをちょっと出してみました。
これが採用されるかわかんないんですけど、私にとって結構大きな挑戦になります。
ということで今回はRubyのRBSを使ってディペンデンシーインバージョンプリンシップをもうちょっといい感じに実現するでした。
それではまた。