【オブジェクト指向】「4つの依存」とその対応について
今回は、依存関係を理解し、どのように疎結合のコードを作っていくのかについて見ていきます。
こちらの記事は、
「オブジェクト指向設計実践ガイド ~Rubyでわかる 進化しつづける柔軟なアプリケーションの育て方」の第3章の内容を説明しています。
とてもためになる本だと思いますので、興味を持っていただいた方は、ぜひ本書も手に取って見てください。
では、始めていきましょう。
0 今回注目する箇所について
今回は、下の2つのGearクラスとWheelクラスを見ていきます。
自転車関係のコードです。
また、依存関係について見ていきたいのは下の箇所です。
では、具体的に見ていきましょう。
1 依存関係を理解する
まずは今回のコードの依存関係を理解しましょう。
1ー1 今回注目する箇所の内容
「 gear_inches」メソッドは、下のように、
しています。
1ー2 4つの依存を認識する
実はここで、Gearは4つの点について、Wheelに依存してしまっています。
どこで依存しているのでしょう。
1ー2ー1 他のクラス名への依存
まずは、gear_inchesメソッドは「Wheel」という名前に依存しています。
これでは、「Wheel」クラス名が変わったら、動かなくなってしまいますね。
でも、実はもっと大きい問題があるんです。
それは、このメソッドは「Wheel」のdiameter(直径)についてしか計算しないと言っていることになるからです。
これでは、仮に「Disc」というdiameterを持つクラスが登場したとしても、対応することができません。
1ー2ー2 メッセージの有無についての依存
このメソッドはWheelが「diameter」に応答してくれるということも知っています。
これも依存ですね。
1ー2ー3 必要な引数の依存
下のようにインスタンス化する過程で、引数を渡しています。
つまり、Wheelに2つの引数が必要であることを知っているのですね。
1ー2ー4 引数の順番の依存
最後に、引数を2つ以上渡す時、この順番で渡すということも知っています。
これも依存ですね。
2 疎結合なコードを書く
2ー1 依存オブジェクトの注入(dependency injection)
まず見ていきたいのが「依存オブジェクトの注入」の手法です。
下のように、Gearクラス内からWheelクラスのインスタンス化が消えました。
これは、インスタンス化の部分をクラスの外に出したためです。
中身を具体的に見てみると、「gear_inches」というメソッドは「wheel」が「diameter」に応答するということは知っています。
しかし、「rimやtireを使って計算する」というような依存からは切り離されました。
GearクラスはWheelクラスを知るという責任から大きく解放されました。
2ー2 依存を隔離する
上のように「依存オブジェクトの注入」までできないケースもあります。
その場合も、クラス内で依存を隔離することにより、改善を行うことが可能です。
2ー2ー1 初期化部分に隔離する
元々は「gear_inches」メソッド内にあったインスタンス化を初期化部分に隔離しています。
これによって、依存が明確になりました。
しかし、これには問題もあります。
それは初期化を通じて、「必ず」Wheelクラスのインスタンスができてしまうということです。
つまり、Gearクラスは必ずWheelのインスタンスを持つことになります。
2ー2ー2 独自のメソッド内に隔離する
下のように、インスタンス化の部分を独自のメソッドにすることで、「wheelインスタンスが必要な場合は作る」という関係にできます。
つまり、wheelが使われない場合は、Wheelのインスタンスが作られなくなるので、依存が減ります。
なお、wheelメソッド内では、nilガード(||=)が使われているので、@wheelがnilである初回時だけ、インスタンス化が実行されます。
2ー2ー3 外部メッセージを隔離する
では、少し違う観点からも隔離について見ていきましょう。
下の「diameter」は自身のクラスではない、「Wheel」クラスのインスタンスにメッセージを送っています。
外部にメッセージを送っているので、外部メッセージです。
ということは、外部である「Wheel」クラスの「diameter」というメソッド名が変更されてしまえば、動かなくなってしまいます。
上の場合は見やすいですが、下のように、前後にたくさんの処理が走っていたり、いろいろなところで使われていたらどうでしょう。
修正するのは大変そうです。
こんな時は、外部メッセージを使う部分を下のように隔離しましょう。
これであれば、「gear_inches」は自身の「diameter」メソッドにメッセージを送るようになりました。(外部に送っていません)
ということは、仮に外部であるWheelクラスの「diameter」という名前が変更になっても、隔離された「diameter」メソッドだけを直せば良くなりました。
いかがでしたでしょうか。
みなさんが依存の少ないコードを書く上での一助になれば幸いです。