S.O.L.I.D Principalsでメンテナンスキトキメキトキス♪


この記事は、.NetRocksの1166話SOLID Principles and .NET with Chris Klug の書き取りと、日本語訳です。2015年7月16日放送。

使ったもの(tools used):Express Scribe & Music to Code by
翻訳にあたってCarl Franklin氏の了承は頂きました。

リスナーの投稿とか、CarlとRichardのBetter Know Frameworkは飛ばして、ゲストインタビューのみ。
日本語化してない、英語音声書き取りメモは、まんまここにおいてます


クリス・クルークは、スウェーデンのわくわくすることが大好きなソフトウェア開発者で、20歳の頃からコード書いて飯食ってる、それで学費も稼いで卒業して芸術監督になったんだけど、15年後、なぜかまだコード書いている。それだけでなく世界中を旅してソフトウェア開発について講演することになってしまってる。他にも人生でやりたいこともあるけど、芸術監督はもういいや、だそう。

Chris Klug(以下Ch): ははは
Carl Franklin(以下C): ようこそ。クリス。
Ch:ありがとう
C:写真良いじゃん。
Ch:浮いているよね
C:他と違うよね。性格が表現されてていいと思う。
Ch:多分ね

C:で、Bob MartinのSOLIDだけど、まあ、 Michael FeathersがSOLIDの略称を作ったんだけど、Bobが5つの原則を作ったよね。君の思うところを・・・や、まず最初に原則を一つずつ挙げてって、君のご意見を聞こうか、いや、それよりもまず、リスナーの投稿と、リチャードのレスについてはどう思った?
(訳者注:投稿は、ソフトウェアの価値は一体どこにあるのか、ということ。リチャードは利用者のことを考え、ソフトウェアクラフトマンシップを狭義の狂信にしない、みたいなこと含んで言ってました。)
Ch:良いレス返すと思ったよ。ソフトウェア・クラフトマンシップ(プロの匠みたいなもの)について最近よく耳にするけど SOLIDは僕の専門だから言っちゃうと、一部の人はもっと肩の力を抜いたほうがいいんじゃないかな?
正直言って、もっと広くソフトウェアをリリースする時もこの話題のうちに含まれなければいけないと思うんだ。
C:だよね。

Ch:ソフトウェア・クラフトマンシップの信奉者は大きな事を取り上げがちで、大変革を求めがちだけど、それだと小さなアプリでも作っているときは、あまり関係ない話になってしまっている。多分、それなりに適した物差しでどのくらいその「匠の意識」を取り込むかってのが重要だと思う。すべての条件がいつもすべてに適しているということはないからね。

C:作っているソフトが、一世紀先まで使われるようなもののコアで、大企業で100人の開発者がいてって状況だったら、全てを意識しないとかもしれないけど。でも小分けすればするほど、そしてソフトウェアの匠の5条に違反しなければしないほど、なにか後から変更を加えるときにラクにできるよね、ってそいうことをSOLIDは言っていて、将来に向けて起こってしまう変更を、なるべく影響すくなくしましょうよって、ことだよね。

Ch:うん。そんな感じ。だからそれなりの物差しも必要で、一世紀使われれるようなソフトウェアがどのように変化するとか。僕はいろいろなプロジェクトに関わって来たけど、正直言って、顧客がこれがほしいあれがほしい、この新しいフィーチャーを全てに適用するとか言ってたのに限って、6か月後にはポシャっている。全然実情に合ってなかったからね。
そういった状況では、僕は現実的に進めたいところだよ。最初だけでなく顧客へ出すところまでもSOLIDを適用して計画を立てることもできる。必要ならね。

C:ふうん。たとえば変更を加えなければいけない時?
Ch:そう、SOLIDを使いつづけるによって、変化が生まれる。最初は小さなところから適用してね。繰り返すけど僕は現実的に計画したいところなんだ。
C:了解。じゃ、5つ、一つづつ見ながら、ご意見を聞こうか。
Ch:いいよ。

Single Responsibility Principal (単責の条項)

C:最初は Single Responsibility Principal (単責の条項)
Ch:これが一番重要だと思うよ。長いことソフトウェア開発にかかわっていると、これはほぼ自然に身についてくるね。
SRPは、「クラスに変更を加えるときは、たった一つの理由で、一つの要件のみがクラスを変更する」ということ。
わかるよね。つまり小さなコードを保持しているなら、何か変えなければいけないときに、何を変えるべきかを見つけやすい。
絶対これが中でも一番重要な条項だと思う。

C:あとSRPはクラスが一つの仕事をするってだけでなく。そのクラスが2つ3つ Publicメソッドがあると、違反していることになるよね。一つのクラスがいくつかの仕事をするのではなく、インターフェースをつくって、そのうちの一つを必要な時にそこだけを変えてほかに影響させるな、ってことだよね?

Ch:そうだね。でも君が言ったのは後から話すようなところまで含んでしまってるよ。インターフェースとの組み合わせとかね。でもそう、僕はクラスを小さなかたまりにして、変更も小さなものにするってことだよね。

Richard Campbell(以下R):この話、C#に限定した方がいいの? コード自体の書き方から言うとどんな感じ?

Ch:コード自体では? 僕はC#くらいしかわからないけど、あとはJavascriptかな
R:いい選択だ(笑)
Ch:コードは他のクラスと同じに見えると思うよ。見て「ああこれは単一責任をちゃんとやってるクラスだ」とかはいえないし、SOLIDだともいいにくい。逆に、単一責任じゃないクラスってのは見分けやすいよ。
DBにアクセスしたりとか、ウェブサービスがフォーマット変えてたりとか。いろいろなことしてる。何か他のクラスを変えなければいけない時に、必ずそのクラスも変える儀式をしなければいけない。 単責をちゃんとやってないってのが、ちゃんと単責を守っているぞ、ってのより見つけやすい。だって、クラス自体を小さくしても、何か一つのことを変えるときに2つの違うクラスを変えていたりするってことあるだろ。

C:いい指摘だね。 僕は今、MVVMとかWPFの環境について考えてた。あれは、考え方そのものが単一責任じゃないよね。

Ch:そう、あれは、定義からして単責じゃない。ウェブサービスと接続してフォーマットしたりするのは単責違反だ。単責のみならず、SOLID違反でもあることも多い。でも、MVVMと同じことやるのに違う方法があるとは思えないんだよね。

C:たぶん、このビューモデル一つに一つのクラスをつくって、インターフェースを作ってそれを使えってのが、暗示される(単一責任の)遵守法じゃないかな?ただ、それって、まだるっこしいよね。
Ch:まったくだ。 僕もMVVMは長いことやってて、SilverlightのMVPだったんでMVVMは好きなんだけど、うん、単一責任の役割は担ってないよね。つまりは Viewに至る時に一つだけのことを管理しているべきでてところは守っているんじゃないかな。
C:うん。一方で複数のこともしているけどね。何かを変更するときに、その変更がViewに至った時に、君の単一責任の解釈にもよると思う。条項には沿ってないかもしれないけど、一つのことをしている、かな。
あと、巨大なView Modelを変えるのと、小さなクラスを変えるのは相当違うし、つまりここで言われているのは、「変更が加わった時に小さなクラスで小さなことするのを変える方が危険が少ないぞ」 ってことかな。Viewmodelでも。

Ch:だね。
R:でも、長いことコード書いているけど、これ、ちゃんと守ってないよ。
Ch:だよね。だから現実的にすすめないといけないんだ。大きなViewModelを作っちゃったとして、小さく切り分けるとするだろう? そうするとメンテナンスが大変なんだ。だから大きなViewModelを小さくするしてその組み合わせにしたいときは、WPFだったりしたら、Prismとか、複数のViewModelにするとか、Messaging使うとか、するよ。 現実的に進めたいんだ。それがまた時間がたって大きくなっていったら、適宜また小さくする。
R:ちょっとそれるけど、単責って、もしかしたら ダイナミックな言語をを使っているときに、もっと役に立つのかな? ああいうのって比較的コンパイラに頼らないで、何が悪いところか行ってくれない代わりに、テストが重要になってくる。
Ch:リファクタリング本にあったけど、そうじゃないと思う。著者はMartin Fowlerだったっけ?
C:うん。
Ch:あの本には同意できないな。リファクタリング本はリファクタを安全にしようってところで、C#においてはコンパイラがやってくれるところだもの。だからView Modelのクラスに変更を加えるとき、コンパイラが何か言ってくれるようなことなら、コンパイラなしでももうやっているような微細なことだよ。Javascriptでは話が違うと思うけど。

Ch:そうだよね。・・・いまジョーク言いそうになった。
R:Javascriptを馬鹿にするな。Javascriptは友達だろ?
C:Javascript大好きだよ。全てのJavascript開発者が好きだよ!!
Ch:ははは。僕はできるだけTypescriptにもってこうとしてるけど。
R:へえ。どう?C#っぽい?
Ch:構造がしっかりしてるよね。Visual StudioのIntelliSenseが、ちゃんとしているのがいい。JavascriptママのIntellisenseだと適当に今まで書いたソリューション内のコードを持ってくるだけだし。
Typescriptだともっと簡単で、Staticのタイプ使えるし、すぐにコンパイラが警告してくれるし、厳しく見てくれるから。間違ったことしないですむ。

C:そうなんだ。スタティックなタイプのいいところは、間違ったタイプ決めをすると警告してくれる。
Ch:そこが知りたいところだし。
R:ツールはそうでないとね。僕にとってはプログラミングはそういうものだと思っている。 もしダイナミック言語を使うなら、全く違う方法で対処するところだよね。考え方の仕様というか。問題になるのは、その二つを混ぜこせにしたとき。収拾がつかなくなる。

Open Close Principal (開放/閉鎖原則)

C:じゃ、次行こうか。開放/閉鎖原則。
R:拡張性を保ちつつ、変更はさせない。ざっくりとしているね。
Ch:定義があいまいで、理解するのが難しいのがこれ。SOLIDプレゼンをするときに、SOLIDって聞いたことあるかって聴衆に聞くと、95%は手を上げる。で、全部挙げられるかって聞くと、半分になる。で、Liskovを説明できるかっていうと、一人か二人の手が挙がってる程度だ。
でも正直言って、開放/閉鎖のほうがLiskovより理解しにくいよ。
R:そうだね。
C:聞きたいんだけど。どのくらいの開発者がこの条項を守っている?あまり多くないんじゃないかな?
Ch:多分みんな、無意識にやっているんじゃないかな。何を作っているかによるとおもうけど、意識的にこの条項を守っているという話を聞くことは本当に少ない。
このSOLIDに興味を持っていろいろ公演しているけど、この条項を理解して、どう役に立つかとか、それに関するルールとかすべて理解している人は少ない。ほぼみんな守ってないんじゃないかな。

C:じゃ行くよ。開放/閉鎖の条項はリチャードが行ったようクラスは拡張性を持ちつつ、変更を認めてはいけないと。しかもクラスだけでなく、ソフトウェア自体の変更をの話でもある。「拡張性を持たせ、しかし変更はできない」ように作る。と。つまり ログインのメソッドがあってそれを変えたかったら、ログイン2ってメソッドができて・・・

R:ははは!
Ch:ははは。そうそう。僕のコードってそんな感じ。でログイン1、ログイン2 新しいログイン、 もう一つのすげえログイン・・・
C:それじゃどれを呼べばいいか分からないじゃないか!
Ch:ははは。そうだね。まあ僕だったら、もうちょっと大きめに考えて、ログインメソッドじゃなく、そのログインメソッドを持つクラスの話になると思うけど。

C:だね。
Ch:で、そのクラスに変更を加えるときは、継承して、ログインメソッドをoverrideする。
C:分かってる。冗談のだよ。じゃさ、この条項に違反しているいい例をあげてみようよ。
Ch:けっこう例を挙げるのも難しいけど、コードを変更するとき、たとえばログイン・ファンクションを変更すると、何か他の物を壊すかもしれない。だって新しいバグを入れ混んじゃったからね。だからOCPがきちんと守られている元クラスを継承すれば、変更を加えれば、壊すことはないんだ。言うのは簡単だけど、ちゃんとやるのは難しいよね。

R:今君が行ったことそのまま、僕たちが脊髄反応でわかってることだよ。何かを変更すれば、何かを他の、みんなが使っているものを壊す。何かパラメーターを加えたり、機能を加える時は、現行のワークフローを壊してはいけないんだ。

C:そうそう。

Ch:この条項が興味をそそるのは、知れば知るほど単体テストのことが気になりだす。単体で何も壊さないようなテストだ。で、壊れるはずのテストを始めると、Regressionテストを始める。そこでひっかかると何かを変える。もしOCPを守って何も変更を加えないでいれば、とてもいいことだと思うけど、テストをする機会も少なくなる、で「完成」してからのテストは、これが初めてのテストになったりする。

R:「通るはず」のテストだね。非現実的な言葉で言うと
Ch:そう。「通るはず」なんだ。完全にOCPを遵守したベースクラスならね、そのクラスを作って、継承して、テ氏として、新しい機能は完璧のはず。なんだ。

R:それでいい話なんだけど、実際には継承を避ける傾向がある、継承はいいんだけど、ほかの問題をも生むからね。

Ch:だよね。だからOCPのクラスでも、dependency injectionとかdependence inversionを使って変更できる。よく聞くのはOCPは変更をImplementationで、ほかのdependency をつかって達成するものだということ。あと、MeyerとPolymorphicを比べて、Meyerの方ではクラス変更を加えるときに継承するためにあるという。
つまり、Implementationからはじめて、それから機能を継承する、けど潜在的にはInterfaceを変更する。Polymorphic にね。
一般的にやられているのは、InterfaceやAbstractのBase Classをキープして使うけど、結果的にはImplementationが変更されることになる。だから二つの方法がある。僕はMeyerのやり方で入って、クラスを使ってクラスを継承するってことは少ない。けど、何か変更しなければいけない時に、Interfaceを変えた方がもっとやりやすいんだよね。
みんなもこういう方法使ってない?

C:個人的に興味深い洞察だな。 実は.Net の初期にDeep Trainingってところでプログラミングを教えてたんだけど、マイクロソフトの企画したツアーでさ。その頃のオブジェクト指向の教え方って、「顧客ってオブジェクトがあって、現実にあるもののモデルがあって」って教え方だったよね。それとか「オレンジがあってフルーツから継承して」って教えてただろう?すべてモデル化してたんだ。それって.Netの中からそんな感じで、継承継承ばかりだろ?Frameworkを作っているなら、もっと元クラスがあってしかるべきなんだけど、ビジネスのモデルを作っているなら、いやビジネスレイヤかな? その場合はそんなに継承を使わなくていいんだけど、使いすぎて損をする。いろんなプロジェクトが、こけたりするのじゃ、2000年初期ぐらいの頃にオブジェクト指向を大げさに使いすぎて、ぶっ壊れちゃったのはそのせいなんだ。

Ch:全く同意するね。 オブジェクト指向が悪いってわけじゃないんだけど、大昔より違う使い方をしているよね。君が言ったように、昔から使い古されている「車があってドアがついてて、その車から継承して」 ってのは、実はあまりいい例ではないんだ。現実を作り変えてしまうからね。それだと元クラスががあまりに少ない現実になってしまうし、実際は元クラスはもっと多いはずなんだ。

C:僕もその悪い例に貢献しちゃってたよ。以前にVBのマスタークラス教えてた時、オブジェクト指向をそうやって教えてたからね。そうやって覚えた方が相関性を理解しやすいと思い込んでいた。でも現実はそうはモデルできない。
R:創作性を考え始めると、継承はちょっと邪魔になってくるよね。だから、何かを作る時に、メソッドと、拡張性だけ持ってきて組み上げれば、開放/閉鎖原則を遵守できるんじゃないかなあ。

R:結局そうなったじゃないか。こdotnetrocksの2002年とか2003年ごろのって、継承の束縛から逃れてなんとかカプセル化しつつ組み上げようってはなしをしてた。
Ch:そうなるだろうね。SOLIDを遵守しようとすると、そんなにお固くならずに構築やすさを無視して遵守しようとすると、巨大なツリー状の階層構造にまよいこんで、継承はただの生涯にしかならない。だから厄介ごとに巻き込まれないためには、くみやすさを優先して継承にこだわらないほうがいい。長い目で見ると継承は現実的ではなくなるからね。

C:全くだl なぁRichard 今何時だと思う?
R:ハッピータイムだね。
―――――――――――――
ここでプレゼントコーナー。Chrisは5000ドルあったら、クワドコプターがほしいんだそう。
―――――――――――――

Liskov's substitution Rule (リスコフの代替原則)

C:話をもどそうか。リスコフの代替原則。LSP? って。さっき君が言ってたみんな聞いたことないだろうけど結構簡単なことだよね。 継承したクラスは、元クラスと完全に代替できるものでなければならない。 もしDがAから継承したクラスなら、DはAの代役ができる。必ずしも逆が可能というわけではない。そういうことだよね?

Ch:うん。 今言ったのがずいぶんわかりやすくまとめてあるね。Google検索でリスコフで出てくるのは、「もしDのxが証明できるなら、xはどうの」とかで凄い分かりにくい。同じこと言ってるのにね。 これって開放/閉鎖原則にもつながるんだけど、継承したクラスが元クラスの代替になるということだよ

C:まあ当たり前だよね。今はあまりやってないけど、継承をたくさんやってたt頃はそんな感じだった。これを遵守してない例って、どんなの?

返り値は元クラスのより専化して

Ch:いや実は突っ込んで話すと、リスコフには4つの項があって、一つ目が「返り値を変えてもいいけど、返り値は元クラスのより専化してね」 っての。 元クラスが車を返しているなら、継承クラスはスポーツカーを返せ、と。

C:そりゃそうだ。
Ch:渡すパラメーターに関してはその逆だね。でもこれはC#みたいなスタティックなタイプの言語環境だと、変えることはできないけどね。 君は無視していいよ(笑)

C:ダイナミックな言語の方が、気にしなきゃいけないことなんだね。
R:いろいろ難しいよね(ダイナミック言語は)。
C:今まで見たSOLIDはそんな感じだよね。ダイナミック言語のプログラマ向けの約束事というか

前提条件を絞り込んで、後条件を弱くしたらダメ

(strengthening of pre conditions and weakening of post conditions violate Liskov Substitution principle)
Ch:次のは違反しがちだよ。前提条件を絞り込んで、後条件を弱くして。
これは、クラスに元クラスの前提条件を元クラスと違うものにしてしまうことだ。それやっちゃうと、メソッドのプロパティを特別な設定をしないといけない。だって元クラスにその条件はなかったからね。だから継承クラスが前提条件を変えてしまうと、リスコフ違反になってしまうんだ。逆も真なりで、継承すると元クラスにいろいろできることになってしまって、元クラスをの存在意義を弱めてしまう。
  だからそんなことしたくなる気持ちは分かるけど、まずは機能を追加したいとリクエストして出すという、のもあるけど。プロダクションでうまくいっているコードの前提条件を変えいるなら、それが元クラスを壊してしまう危険がるなら、結局リスコフ違反になっている。

R:これって、説明しづらいけど、プログラマならだれでもわかるわかるーって感じのことだよね。
Ch:そだね。
R:だって、継承するなら、拡張するためだろ?何で逆に行く必要が出てくるかわからない。
Ch:開放閉鎖の原理を守ってれば、拡張することしかできなくて、変更はできないのだから、そんな状況になることはないよね。
R:うん。生理的に気づくよね。変更すれば破壊することになる、とな。じゃあ拡張するしかない。

Ch:習慣になってるよね。何年か壊したりする経験積むと、「あ、これやっちゃだめ」って。
R:これやっちゃうと、みんなが困る
Ch:何回かどなられて、みんな覚えるんだ。(笑)
C:やめた時の気分がいいってな。
R:さいしょからやんなって。

C:じゃ、3 か4?Liskovの。
R:4。インターフェイス隔離。
C:3、やったっけ?

History Constraint (履歴シバリ)

Ch:あ、履歴シバリってのがあるんだ。イミュータブルのクラスを、ミュータブルにはできない、って。逆もしかり。
C:リスコフのアイテム内だね。
Ch:これも分かりやすいよね。皆これはそりゃそうだと思うだろ。 でも実はイミュータブルのクラスをミュータブルにするのはそんなに難しくない。プロパティを追加して他のプロパティやメソッドに影響するようにするだけだし。 あれ、最後のなんだっけ。

Interface Segregation(インターフェイス隔離)

C:インターフェイス隔離が次だね。 クライアントは使わないメソッドを無理に使わ(Implement)されてはいけない。(クライアントとインターフェイスの)契約は薄いものでないといけない。
Ch:言い方が分かりやすくていいね。僕が見つけたのは「クラスは必要のない機能に頼ってはいけない」というものだ。 逆もしかり。
インターフェイスを使うたび。必要のないものまでもらってしまっている。
C:あ、ぼくが読んでいるのは「SOLID Principal of C#」 2013年5月1日の、V.N.S Arun のものだよ。
 CodeGuruに載ってる

Ch:英語での言いかたがいいね。そう、で、簡単なことなんだ。たくさん詰め込みすぎてインターフェースを大きくしすぎるな、とか。小さなインプリメントしやすいインターフェースをつくったほうが、1つのメソッドを適用する方が 25のメソッドどうにかするよりずっといいだろ。しかも、その方がどれくらいクライアント側にアクセスをあげるかをコントロールしやすいしね。 まあでもこの話僕は大嫌いなのだけど。
R:ははは。
Ch:これよく聞く話なんだけど、「俺のインターフェースへのアクセスをあげすぎちゃうと、ひどい使い方するやつがいるからいやだ!」って、誰がコード書いてるんだっての。僕の会社では僕が書いて、同僚が書いてるよ。夜中に妖精さんがでてきてインターフェースの使い方をマズるってことなんかないよ。
C:それ聞いて思い出したよ。Silverlightが出てきたときに、ごちゃごちゃそのあたりのこと言うやつらがいたけど、結局NotImplementedExceptionのラッパーのことでさ。
Ch:その話聞いたことある。同じことがMicroframeworkの時もあったね。
C:僕が考えたのは、「これってあたりまえじゃないの? 適用されてなければ適用されてない例外を投げるのは?」
Ch:結局そうなるよね。僕はプレゼンをそれで少し変えたんだ。一番悪いISP(インターフェース隔離の条項)違反の例を加えてね。実はそれはマイクロソフトのものだったんだ。
Ch:MembershipProvider覚えてる?Asp.Net2.0のころでさ。自分のMembershipProviderを作りたかったらProvider 元クラスを継承しないといけない。そして使うメソッドをオーバーライドする。 いままでそれで。、ログインがほしいだけなのに、ユーザーネームとパスワードをチェックするだけにこれ継承する状況なんてたくさんあるよね。
C:ほかの、使わないメソッドまでオーバーライドしないといけない。
Ch:そうそう。ほら、27のほかのメソッドに、NotImplentedExceptionつけてあげてね、ってやつで。まあそれでもいいんだけど、すごく嫌な感じだよ。3つの必要なメソッドが巨大なクラスに積まれて、全力でいちいちNotImplementedException出してるってのは。ユーザーネームとパスワードを見るだけの単一責任、パスワードの複雑さをチェックする責任、ログイン失敗何回かで蹴りだす責任、そんなのが一々このクラスに詰まっている。
C:何々べからず!ってのも中に入ってるしね。しかも不安定な動きの。
R:はははは。
Ch:手っ取り早い解決法としては、分解することだよね。ユーザー認証があったら、パスワードの複雑さをみるInterfaceがあって、って、たくさんのInterfaceを作る。で、何かカスタムのロジックを使いたければ、必要なものだけをもってくる。で、もしユーザーに自分の情報を編集できるようにしたければ、それもMembershipProviderがやってることだけど、そうでなくてほかのInterfaceを使えるようにする。けっこうめんどくさくなるし設定もめんどくさいけど、その方が理屈にかなっていると思う。
C:そうだね。
R:ある程度経験のある開発者なら自然とこうするもんだとおもうけど、でもこうやって文字にして書いてあると、何かあった時に、何か違反事項を見かけた時に、ほら個々が間違っているよと説明しやすくていいよね。

C:それだけじゃなくて、何か問題があった時にこういった約束事があると、その問題を治す時に正しく治せるからいいよね。
Ch:古参の開発者がそれがわかっていると思ったら大間違いだよ。こないだオンラインで討論になってずいぶんとイラっとしたんだけど、リスコフの例でよく正方形が長方形であるか、ってのがでてくるんだけど、リスコフでは正方形は幅と高さが同じなので長方形ではない、という話になっているんだけど、リスコフから派生してそんな話になってしまったんだけど、古参開発者の中には、いやいや、それは違反だろう、俺の方がよく知ってる!というね。
C:ラベルをつけすぎる問題は現実にもよくあるしね。

R:こういう論争は、シンプルな例になればなるほど、分かる人が多いから起こりやすいんだろうね。シンプルすぎて例が必要じゃないこともおおい。長方形、正方形のになると、Rectとかのクラスは、普通に同じ幅と高さにして正方形を実現することはできるからね。

Ch:そのことで、さっき話してたオブジェクト指向の話に戻ると思うんだけど、Rectangleはコードのなかで一つのクラスでもう実現してしまうんだ。正方形でも長方形でもね。本当は正方形、長方形しかできない、専門クラスがあるべきで。
C:でもし特別なクラスが必要ないなら、馬鹿な使い方もされることない、と。
Ch:まったくだ。

Dependency Inversion (依存性の逆転)

C:じゃあ、Dependency Inversionの項にいく? これはInversion of Controlで、ここでもずいぶんと取り上げたよね。
ほぼ開発者にとっての基本の条項だといっていい。

Ch:たぶん僕が一番好きな条項かな。僕はNinjetを使うけど他のコンテナも使えるけど、一番最初にプロジェクトフォルダに入れる者のうちの一つだね。大抵のことは単純化されるからね。みんなIoC Containerと、Dependency Inversion、Dependency Injectionはソフトウェア開発にとって素晴らしい方法だってことはもうよく知ってるよね。ソフトウェアをフレキシブルで、変更を加えやすくする。

C&R そうだね。
Ch:ただし説明するのは難しい。全てが抽象的だから。たとえば僕のデーターベースがIDatabase インターフェースがあるとして、それをコンテナに入れてしまうと。で、将来Microsoft SQLを、Oracleに変更したときにその小さなクラスw変更するだけでいい。・・・っていうことだけなんだ。こういうことはまれにしか起こらない。
R:そう、実際はそうだ。
Ch:で、ほかをみてみることになる。僕は実際小さなソフトウェア作ったことがある。完結した販売機みたいなもので、ニュージーランドの顧客に頼まれて、タッチスクリーンがあって、カメラとウェブキャムがあって、コインを入れるところもあってプリンターもある。そういう、ここからでは物理的に見えないハードウェアも絡むものだったんだ。ここには同じ環境はない。でもこのInversionの全てをつかって、設定ファイルを送ることにした、そうすれば仮想でハードウェアを持てる。その設定を設置する時も、ハードウェア自体は必要なかった。設定ファイルと、外側から使うXmlファイルとでね。それで顧客が、新しいカメラを使いたい。新しい仕事のワークフローに、古いカメラでは対応できない、と言い出した時も、ここストックホルムにいたまま、ICamera InterfaceをImplementationし直して、顧客にそれを送って設定ファイルを変えさせた。
すぐに新しいカメラを使うことができたよ。 ビルドしなおす必要もなかったし、カメラに特別なセットアップする必要もなかった。本当に顧客にとっては簡単に仕上げられた。DIがなかったらできなかった芸当だよ。

C:ここで言わないといけないね。IOCの基本話、でどうしてそれが必要なのか、って話を7年前、Dotnetrocksの362話でJames Kovacsとやったんだけど、この話の初期の頃の考え方を知りたかったら、そっちを見てみてね。そう思わない、リチャード?(訳者注:オフィシャルで英語での書き取りが出てます。拙訳はこちら

R:そうだね。あの話はよかったよね。7年前の話だね。今となってはIoCは当たり前になってるけど、その頃はまだ新しいアイデアだったんだ。保持しやすいソフトウェアって考え方で、変更にもめげないって考えは最近になって当たり前になったけど。

C:特に、テストとかモックとかやる時はね。

R:そのあたりから始まった話じゃないっけ。そもそも。どれだけテストしやすいものを作るかってことから、じゃあ、これって、変更しやすいソフトウェアになるよね、って感じに変わった。

Ch:おもしろいのが、マイクロソフトが結局それを取り入れていったってところだよね。 MVCなんて、サービスロケーターが出てきたころから完全にIoCのアイデアに引っ付いているじゃないか。WebAPIもしかり、だし、ASPDotnet5も、DIは完全に基盤となっている。驚いたのはOwenをやっているとき、最初のスタートアップのクラスでDIを使ってて、そのインターフェイスが、アプリケーションで一番最初に走るもののうちのひとつなんだ。

C:うん。

R:どうしても時間がかかっちゃうんだよね。たとえマイクロソフトであっても、グリーンフィールド(最初からアプリケーションを組める)小さなチャンスに、こういった「DIをとりいれよう!」といった基盤にかかわる決定をするというのは。最初出たMVCは2007年か、2008年か、あのときだってMVCは比較的新しいものだった。今、彼らがDIやMVCを基盤にしているのは嬉しいことだよ。

Ch:だよね。でも、現実には、複雑になっちゃったんだけど。

R:そう。それはある。 特に誰かほかの人がコードを読もうとしたときに、複雑すぎてしまうという問題がある。「何でこんなことしてんだ?」って。

C:だね。これをデバッグしなきゃいけないってのに、実際にはなにがこのImplementationで起こるかは、実行してみないと分からない。

R:はははは

Ch:デバッガについてだけど、多分これは間違いじゃないけど、Visual StudioはF12 を押したときにInterfaceにはいかないよね。Implementationのほうに行く。僕はReshaperを使っているんだけど、Control とF12で、どのクラスがソリューションの中にあるか探してくれて、ImplementしているInterfaceも分かるようになってる。
R:うん

Ch:これなんだよ。あちこちのコードをみるようになると、そしてSOLIDをちゃんと利用していると、1500個のインターフェイスによって抽象化されていることになる。何か起こった時に、どこが悪いか探しているときに、まるで玉ねぎをc剥いているような気分になる。

C:そうだね。

R:で、そんなとき、名前付けの約束事が守れて入れたらいいんだけどね。

Ch:そうそう。どんどんそうやって複雑化してったとき、いったい何をしていたか、がわからなくなることがある。まったく、複雑化で、DIによって得られる良い結果を買っているみたいなものだ。まあ、繰り返しになるけど現実的に、どのくらいの条項をとりいれようかってところが問題になると思う。 「僕は、本当にもう一層の抽象化をここに取り入れたいのか」とか「抽象化は一層で十分か」とか。どうしても複雑化してしまうから。 新しいAspNet5では、またずいぶんそのあたりが違っているから、開発者の中でも完全にコントロールできるのはごく一部になるだろうね。

C:ところで、最近みんなに聞きまわり始めた質問なんだけど、実際の社名は言わないで、いままで見たコードでSOLIDに関して一番酷い違反しているのはどんなのだった?

Ch:あまり僕はみたことがないんだけど・・・残念なながら、見ちゃったのは、何かのコンセプトに沿おうとして、これか、どれか、わからなくなってしまって、結局その人はいなくなってしまって、完全にDIを誤解したものになったInterfaceはあったよ。めちゃめちゃだった。 
実際に見たわけじゃないけど、僕の顧客が見た話なんだけど、一番ひどい話ってのは、コンストラクタが700行あったってこと。

R:うわ。
C:なんだそら。
Ch:そういう時なんていうかな・・・
R:「どうしてこうなった」?
Ch:会社にはそのときいない人が書いたらしいけどね。何事もその人が書いたものは、ええと、みんな、「複雑すぎ」って周知されてたみたい。
C:その人はCobolプログラマ?
Ch:や、その辺はよくわからないけど。
C:そか。そうかな、と思っただけ。
Ch:なにかCobolプログラマに恨みがあるとか?
C:いやいやいやいや。なんか反オブジェクト指向なかんじがしたから、トップダウンで、ルーティン化した呼び出しで。なんでもやって。そんな感じ。
Ch:典型的な、ゴッドクラスだね。普通はコンストラクタじゃないはずだけど。
C:そうだね
Ch:コンストラクタは一番悪いところだよそういうのやるのには。僕は本当に本当にシンプルなものしか、置かないもの。もしどうしても必要なら、Initializeメソッドとか、変数の初期値とか、そんな感じのものだけだよね。だって、初期のオブジェクト起こしに時間かかっちゃだめだよ。
C:よく考えてみたら、オブジェクト指向を勉強する人って何を最初にすると思う? コンストラクタだ。 その人はそこでやめちゃったんだね。
R:ぶわははははは
C:「あ、なにかコード置くとこある。ここにいれちゃえ」
Ch:はははは。「俺のアプリは最高だ。オブジェクトひとつで、起き上がって、そこで終わる」
C:そ、ただ起き上がる。
 クリス。ショウに参加してくれて嬉しかった。
Ch:こちらこそ。二人と話せてよかった。
C:またノルウェーで会おう。
Ch:そうだね。いや、ほかの場所でも。僕はあちこちでコンファレンスに出てるから。U.S.のにも出ようとしているんだ。
C:そりゃいいな。そしたらまた会おう。
 じゃ、リスナーもまた会おう!