1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【Ruby】ComparableをMix-inせずデリゲーションしよう

Posted at

Rubyでクラスを作っている時に、それを比較演算子で比較したい、あるいはソートしたいなどのニーズが出てくることがあります。そんな時に使えるのがComparableです(※ソートは宇宙船演算子さえあればできます)

class MyClass  
  include Comparable  

  def <=>(other)  
    # put your code here...
  end
end  

こうすることで、Comparableモジュールで提供する機能を利用できるようになりました!詳しい説明は公式docに譲ります。

問題点

さて、こうすることで比較が可能になりましたが、例えば以下のような場合を考えてみましょう。

class Human
  include Comparable
  attr_reader :income, :height


  def initialize(income, height) 
    @income = income # 年収 
    @height = height # 身長 
  end

  def <=>(other)
    @income <=> other.income 
  end 
end 

年収で人間を比べるんじゃない! というのは置いといて、人間がある一つの軸で比較可能であるという考え方は、素朴すぎるとは思いませんか?
実際のシステム開発においても、比較のロジックは欲しいけど、対象のクラスを比較する指標はいくつか考えられるし、とは言え毎度sort_byでブロックの中身を書くのもだるい...ということはよくあります。

解決策

クラス自身をComparableにするのではなく、Comparableなフィールドに移譲しましょう
婚活女子の山本さんは、男は年収が6割、身長が4割であると主張しています。彼女の考えを数値化した山本指数が婚活界隈で有名になり、流行に敏感なあなたは自分が開発するシステムにこれを取り込もうと考えました。簡単に実装すると以下の通りです。

class Human 
  # 中略

  def yamamoto_index
    @income * 0.6 + @height * 0.4
  end
end

# Humanの配列を山本指数でソートする
humans.sort_by(&:yamamoto_index)

書いてみれば特に難しいことはしていませんが、他の指標を追加する時は同じようにメソッドを実装するだけで済みますし、比較する際の指標も明示的でいい感じですね。

比較ロジックの拡張性を高める

筆者は、場合によっては上記のコードではまだ不完全であると考えています。
この実装では、比較ロジックを変更したい場合に手を加えられるのはHumanクラスです。しかし、この場合変わったのはHumanの振る舞いではなく評価指標です。であれば、変更を「評価指標クラス」に閉じたいのがプログラマの情ってもんですよね?
また、指標の実装が複雑な計算を伴うようになったとして、Humanクラスが肥大化していくのは避けたいです。

class YamamotoInex 
  include Comparable

  def initialize(income, height) 
    @income = income # 年収 
    @height = height # 身長 
  end

  def <=>(other)
    comparator <=> other.comparator 
  end

  def comparator 
    @income * 0.6 + @height * 0.4
  end
end 

このクラスをHumanクラスのyamamoto_indexメソッドで初期化して返すようにしてあげましょう。
これで山本指標が見直された場合でも、Humanクラスに変更を施す必要がなくなりました!

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?