https://qiita.com/yyu/items/f1f0fa21ddfc23c4bf18 に対する回答です。
twitter 上でも返信しましたが、元記事の文脈から外れてこの記事だけをみた方に誤解を与えかねないので、こちらにも会話を掲示しておきます(Qiita で Vanilla DI を検索するとこの記事が引っかかるので)。
(追記: 2020/12/29)
Minimal Cake Pattern 再考 の記事を受けて、私の理解が間違っていた部分を訂正しました。
TL;DR
Vanilla DI は、「DIコンテナの濫用」に対するカウンターを目指した宣言です。この宣言の目的は、以下の 2 つに集約されます:
- 目的1: DI の仕組みを単純かつ簡潔に保つこと
- 目的2: 設計破綻への感度を上げることで「設計のカナリア」とすること
- 言い換えると、やりづらさを感じたら設計を疑うこと
そして、Vanilla DI ではその手段として Constructor Injection を選んでいます。これは他の手段を比較検討した上での結論です:
- DI コンテナ
- ❌ 目的1と2の両方を満たさない
- デフォルト引数
- ❌ 目的2を満たさない(責務過多/抽象の不足を見逃しやすいうえに、暗黙の前提を埋め込むので保守性が悪い)
- Cake Pattern
-
❌ 目的2を満たさない(デメテルの法則違反や責務過多/抽象の不足を見逃しやすい)(訂正: 2020/12/29) - ⭕️ どちらも満たしうるが、目的1が言語間ポータビリティが目当てなら適さない
-
- Setter Injection
- ❌ 目的2を満たさない(デメテルの法則違反や責務過多/抽象の不足を見逃しやすい)
- Constructor Injection
-
⭕️ どちらも満たす(訂正: 2020/12/29) - ⭕️ どちらも満たすが、言語間ポータビリティが必要なければ各言語にさらに最適な方法が存在しうる
-
まとめると、Vanilla DI は以下の要望をもつ方に向けたものです:
- DI でテスト容易に保ちたい
- 設計をよくしたい
- 言語間ポータビリティを保ちたい (追記: 2020/12/29)
このような要望のもとならば、Vanilla DI はうまく機能することでしょう。
詳細
いま返信を準備してますが、一点誤解されてる点があると思います。あの記事の趣旨は、コンストラクタ注入はデメテルの法則違反を引き起こしにくい(DIコンテナに比べて)ということです。(続く
— Kuniwak@A man using Vanilla DI/Mock (@orga_chem) 2018年9月9日
続き) なので、一つ目に対してはおっしゃる通りですが、これは記事の趣旨と同じことを言っています。そして二つ目はこの記事の趣旨と直交する話です。デメテルの法則は間接的な依存自体を否定するものではありません。あくまで、間接的な依存を前提にしてはいけないということを言いたいのです
— Kuniwak@A man using Vanilla DI/Mock (@orga_chem) 2018年9月9日
ちょっと僕が述べた「依存の依存が書きにくい」という点にやや不明瞭な点があったかもしれません。僕はあるクラスのインスタンシエイトにおいて、その依存をインスタンシエイトする際に依存の依存もインスタンシエイトしなければならない事を指して書きにくいと述べました。
— 吉村 優 / Yuu YOSHIMURA (@_yyu_) 2018年9月10日
ただ、たしかに記事をもう一度読むと、コンストラクタ引数が多いことは問題としてはいるものの、依存の依存が問題とは述べていませんね……。
— 吉村 優 / Yuu YOSHIMURA (@_yyu_) 2018年9月10日
実は僕もどちらかというと言語機能でDIをする方がいい派ですが、コンストラクタ注入よりはケーキパターンの方が好きです。
— 吉村 優 / Yuu YOSHIMURA (@_yyu_) 2018年9月10日
私の記事では「バニラDIは設計破綻のスメルを察知しやすいという利点がある」と述べていますが、この文脈では、Cake Pattern (少なくとも Minimal Cake Pattern)はこの利点を備えていないと思います。この理由を説明します。(続く
— Kuniwak@A man using Vanilla DI/Mock (@orga_chem) 2018年9月10日
続き)記事で触れたように、コンポーネントの依存が増えてきたことは設計破綻のスメルであり、設計を直した方がよいと考えています(これが Vanilla DI のやり方です)。しかし、Cake Pattern は trait/mixin の仕組みによってこのスメルを隠せるようになっています。(続く
— Kuniwak@A man using Vanilla DI/Mock (@orga_chem) 2018年9月10日
続き)このスメルを隠せることを Cake Pattern の利点としてあげている記事もあるようです: https://t.co/eBbTVlUK75
— Kuniwak@A man using Vanilla DI/Mock (@orga_chem) 2018年9月10日
しかし、私はこの主張に賛同できません。なぜなら、根本原因への対処ではなく、表面的な対症療法にしかなっていないからです。
また、もう一つ欠点があると考えています。(続く
続き)この欠点はまたもやデメテルの法則がらみのことなのですが、Minimal Cake Pattern はデメテルの法則違反がコンパイルエラーにならないという欠点を持っていると思います: https://t.co/UYgYtImoCe
— Kuniwak@A man using Vanilla DI/Mock (@orga_chem) 2018年9月10日
しかし、コンストラクタ注入ではエラーになります: https://t.co/hCGHQB5eEu (続く
(訂正: 2020/12/29): Minimal Cake Pattern でも protected を使えばデメテルの法則違反を防げます。
続き)まとめると、DI コンテナも Cake Pattern もどちらも設計の不味さ(抽象の不足/責務過多/デメテルの法則違反)を隠しやすいという性質があり、したがって開発者から設計を再考する機会を奪いがちです。この点が、私の好みではない点です。
— Kuniwak@A man using Vanilla DI/Mock (@orga_chem) 2018年9月10日
(訂正: 2020/12/29): テストを書いていれば Minimal Cake Pattern でも設計の不味さの感度は保たてます(ただし運用状況に依存しますがこの点は Vanilla DI も同じ)。なお、テストを書かない上で悪い設計を無視する状況を正当化しうるのはごく短命なソフトウェアのみだと思います。