【Ruby】オブジェクト指向設計を噛み砕いていく(#4 ダックタイピング)

投稿日:

プログラミング初心者の勉強ブログ #81

「オブジェクト指向設計」について理解を深めていくシリーズ。「オブジェクト指向設計実践ガイド」を読んでの備忘録になります。今回は設計テクニックの一つであるダックタイピングについて理解します。いかなる特定のクラスとも結びつかないパブリックインターフェースとは。

 

目次

 

前回までの内容

 

ダックタイピングとは

ダックタイピングとは、「いかなる特定のクラスとも結びつかないパブリックインターフェースを構成するため、抽象的なクラスを用いる技法」です。

「特定のクラスとも結びつかない」ことは、将来プログラムに起こる変化に対するコストを下げることに繋がります。不必要なオブジェクトの依存を減らすための方法になります。この本に書かれている内容全てにおいて「将来のコードの修正コストを下げる」ことが中心に据えられており、一貫した考えがあることで各章で述べられていることの理由が腑に落ちていく感想を抱きます。

今まで、「綺麗で洗練されたコーディンングとは」という問題について少しかじったことはありましたが、

---この書き方はコード数減らせる。

---この書き方は可読性が上がる。

---この書き方はDRYだ。

などを学んでいくにつれて、リファクタリングに関する軸が複数ある印象を受けており、「結局詰まる所、何が良いのか」というふわっとした理解をしておりましたが、ここに来てその軸に関する着地点を得ることができたような気がします。一つの本の内容を丸々鵜呑みにすることが良いことなのかはわかりませんが、「将来のコードの修正コストを下げる」という一つの軸を持つことが、現在の自分にとっての「綺麗で洗練されたコーディング」ができるようになるための第一歩として、良い方向に導いてくれるのではないかと感じております。

ここで、「クラスが具象的な事柄で定義されているか、抽象的であるか」は問題ではなく、あくまでイメージを捉えるための要素の一つぐらいに思っておく必要があります。大事なのは「メッセージ(メソッド)」を中心にオブジェクト指向設計を考えることです。

色々感想を書きましたが、これではダックタイピングが何なのかはよくわからないので、具体的なダックタイピングについて以下書いて行きたいと思います。

 

未特定のダックを見つける

未特定のダックを見つけるためには、まず「ダックタイピングされている」ということが、何を指すのかについて考えなければなりません。

目的は「オブジェクトが、他のオブジェクトを深く知っている」状況からの脱却であり、方法は「ポリモーフィズムの利用」です。

 

オブジェクトが、他のオブジェクトを深く知っている

ここについては、【Ruby】オブジェクト指向設計を噛み砕いていく(#2 オブジェクトの依存関係を管理する)など、過去の記事を見ていただくと書いております。

オブジェクトの中に、「他のオブジェクト名」が書き込まれているのはもちろん、「他のオブジェクトの振る舞い(メソッド)」が書き込まれている場合、その状況は「オブジェクトが、他のオブジェクトを深く知っている」に該当します。

この状況について、一概に全てが悪いと捉えるわけではなく、「将来のコードの修正コストを下げる」という軸に立ち返って、この状況と向き合います。

 

ポリモーフィズムの利用

オブジェクト指向設計におけるポリモーフィズムとは、「多岐にわたるオブジェクトが、同じメッセージ(メソッド)に応答できる能力」のことを指します。例えばオブジェクト指向の「継承」の概念がこれに当たります。クラスは、他のクラスやモジュールを継承し、親クラスの持つ振る舞いを自身も使えるようになります。

「もしオブジェクトがダックのように鳴き、ダックのように歩くならば、そのクラスは何であれ、ダックはダックだ」

どんなにアヒルに服を着せ仮面をつけ、見た目をアヒルからかけ離れるような状態にしても、それはあくまでアヒルだよ。という考え方がダックタイプの名前の由来であり、ポリモーフィズムの考えと共通しております。

 

結局、ダックタイピングとは何なのか

本を読み、ダックタイピングについて自分の言葉で書いてみると、

「あるオブジェクトの中に、他のオブジェクトやそのメソッドを含ませるより、あるオブジェクトで提起したメソッドを、他のオブジェクトにそれぞれ書き込んだ方が良いよ」

「あるオブジェクトと他のオブジェクトが、一対一の関係だったらそこまで気にする必要はないけど、他のオブジェクトが複数になる場合、この考え方による設計の方が、将来の変更に伴うコストを下げるよ」

という話になります。オブジェクト間のインターフェースの方向を少し変えるイメージです。

メッセージの送り手(この場合、あるオブジェクト)の視点から見て、入れ替え可能であることに合意することがポリモーフィズムを利用したオブジェクトの暗黙の了解です。

つまり、「あるオブジェクト」は、送り先がどこであるかという情報を知る必要がない点が、将来の変更コストを下げるポイントになります。

 

「あるオブジェクト」は、自身で提起したメソッド(ダック)で、「他のメソッド」にメッセージを送る。

「他のメソッド」は「あるメソッド」から送られたメッセージ(ダック)を理解し、「あるメソッド」に返事を返す。

ダックは「他のメソッド」が何であれ、ダックの振る舞いをする。

 

「あるオブジェクト」と「他のメソッド」はダックを信頼して、メッセージのやり取りを行います。

 

未特定のダックを認識するための具体的なコードパターン

ダックを認識するにあたってのコードパターンについてです。以下のような状況のとき、隠れたダックが認識できていない状況です。

 

case文

case文による条件分岐で、メッセージを渡すオブジェクトを分岐させている場合、それはダックが隠れている状況です。

 

is_a?メソッドとkind_of?メソッド

is_a?メソッドまたはkind_of?メソッドは、レシーバのオブジェクトが引数klassクラスのインスタンスであればtrue、そうでなければfalseを返します。

is_a?, kind_of? (Object) - Rubyリファレンス

これらを用いて条件分岐を行なっている場合も、ダックが隠れている状況です。

 

respond_to?メソッド

respond_to?メソッドは、レシーバのオブジェクトに対してメソッドを呼び出せるかどうかを調べます。引数nameにはメソッド名をシンボルか文字列で指定します。メソッドnameを持っていればtrue、なければfalseが返ります。

respond_to? (Object) - Rubyリファレンス

is_a?メソッドまたはkind_of?メソッドと似ております。他のオブジェクト名こそ書き込む必要は無くなりますが、依然として不必要な依存が存在します。

 

まとめ

オブジェクト指向設計は奥が深めですが、一貫した軸を持ってます。軸もブレてなければ、オブジェクト自体もブレてないです。ブレないスタイルを維持するためには設計者の手腕が必要ですが、その考え方のインプットとアウトプットには、難易度に大きな差がある分野な気がします。

「もしオブジェクトがダックのように鳴き、ダックのように歩くならば、そのクラスは何であれ、ダックはダックだ」

まさかこんな哲学的で人生の教訓みたいなことを、オブジェクト指向設計の勉強で取り扱うとは思いませんでした。

以上ありがとうございました。

-プログラミング学習
-, ,

Copyright© s u p ? , 2019 All Rights Reserved.