Edited at

【OOP入門】Rubyでダックタイピングを理解する

オブジェクト指向設計実践ガイド ~Rubyでわかる 進化しつづける柔軟なアプリケーションの育て方を読んで、自分なりの頭の整理をしました。

本書の第五章の内容であるダックタイピングについてまとめています。


ダックタイピングとは

wikipedia師匠によると、


ダック・タイピング(duck typing)とは、Smalltalk、Perl、Python、Rubyなどのいくつかの動的型付けオブジェクト指向プログラミング言語に特徴的な型付けの作法のことである。それらの言語ではオブジェクト(変数の値)に何ができるかはオブジェクトそのものが決定する。つまり、オブジェクトがあるインタフェースのすべてのメソッドを持っているならば、たとえそのクラスがそのインタフェースを宣言的に実装していなくとも、オブジェクトはそのインタフェースを実行時に実装しているとみなせる、ということである。それはまた、同じインタフェースを実装するオブジェクト同士が、それぞれがどのような継承階層を持っているのかということと無関係に、相互に交換可能であるという意味でもある。


なるほど分からん。

私のイメージとしては、

メソッドの具体的な実行内容は、そのメソッドに応答できるオブジェクトが知っているということ。

つまり、どんなメソッドであろうとも、それに応答できるオブジェクトに渡してやると、あとは良しなにやってくれるよー、ということ。


具体例

給料計算を行うクラスがあったとします(ツッコミどころは色々ある書きっぷりかと思いますが、しばしお付き合いください)。

このクラス内では、従業員(employee)がどのクラスに所属しているかによって処理をcase文で分岐しています。

この場合、従業員の所属するクラス(従業員のタイプ)が増えるたびにcase文が長くなっていきます。

また業務形態毎に支払いロジックが変わるたびにこのメソッドが膨らんでいきます。


bad.rb

class Salary

def calc_salary(employees)
employees.each do |employee|
case employee
# 正社員なら保険をどうのこうのする
when Proper
employee.calc_insurance_fee(insurance)
# パートなら税金の控除をどうのこうのする
when PartTimer
employee.calc_tax_reduction(working_hour)
# 業務委託なら業績に応じてボーナスを出すとか
when Outsourcing
employee.calc_special_bonus(special_reward)
end
end
end
end

これに対して、個々の従業員(staffオブジェクト)は自分がどこのクラスに所属しているかは知っているので、

場合分けをせずに、すべての従業員が応答できるメソッド(calc_salary)を一つ作ってやります。

このメソッド名はすべての従業員のクラスで同一ですが、具体的な処理内容は

それぞれのオブジェクトの所属するクラスによって異なります。


better.rb

class Salary

def calc_salary(employees)
employees.each do |employee|
# employeeの所属するクラスのインスタンスメソッドを呼び出す
# selfを渡して、処理に必要なデータは呼び出し先で良しなに使ってもらう
employee.calc_salary(self)
end
end

class Proper
def calc_salary(data)
calc_insurance_fee(data.insurance)
# いろんな処理
...
end
end

class PartTimer
def calc_salary(data)
calc_tax_reduction(data.working_hour)
# いろんな処理
...
end
end

class Outsourcing
def calc_salary(data)
calc_special_bonus(data.special_reward)
# いろんな処理
...
end
end



メリット


  • オブジェクトの属性情報によって場合分けせずに済む


    • コードの見通しが良くなる

    • コードの追加や変更が比較的ラク




デメリット


  • (強いて挙げるならば、)OOPに慣れていないエンジニア(手続き的に処理を書くことに慣れているエンジニア)にとっては何をやっているのかわかりにくい


ダックの見つけ方

下記のような、オブジェクトのデータに依存した条件分岐がある場合には、ダックさんを使える余地があります。


  • オブジェクトの所属するクラスで分岐するcase文

  • kind_of?(またはis_a?)による場合分け

  • responds_to?による場合分け


雑感


  • ダックさんカッケー

  • カッケーけど、全部のケースで無理くり使おうとするんじゃなく、必要なケースというのを考える必要はありそう


合わせて読みたい


参考