『Effective Ruby』を読んだのでまとめました - 第2章 クラス、オブジェクト、モジュール - 後編


関連記事
- 『Effective Ruby』を読んだのでまとめました - 第1章 Rubyに体を慣らす -
- 『Effective Ruby』を読んだのでまとめました - 第2章 クラス、オブジェクト、モジュール - 前編
- 『Effective Ruby』を読んだのでまとめました - 第2章 クラス、オブジェクト、モジュール - 後編

ペースは遅いですが、空いた時間に少しずつまとめてます!今回は2章後編です(^^)

項目11 モジュールにコードをネストして名前空間を作ろう

Rubyのクラスはミュータブルであり、例えば以下のようにArrayクラスを宣言すると、元から組み込まれているArrayクラスを上書きしてしまいます。

class Array
  def overwrite
    '上書きしました!'
  end
end

p Array.new.overwrite

# 結果
"上書きしました!"

私が書いたoverwriteメソッドが呼び出されているので、Arrayクラスを書き換えてしまっています。

名前空間を作ろう

Rubyに元からあるクラスと同名のクラスを宣言したい場合、moduleで囲って名前空間を使用しましょう。

module NameSpace
  class Array
    def overwrite
      '上書きしました!'
    end
  end
end

p NameSpace::Array.new.overwrite

呼び出す場合は、名前空間とクラスの間を::で繋げます。
このクラスは、namespaceディレクトリのarrays.rbというファイルに対応しています。
名前空間で囲ったら、ディレクトリと一致させる事に気をつけましょう。

自分で宣言したクラスの中でオリジナルのクラスを呼ぶには?

以下のように自分で宣言したArrayクラスの中でオリジナルのArrayクラスを呼ぶには、どうしたら良いでしょうか。


module NameSpace
  class Array
    def overwrite
      Array.new.push('配列の要素') # 自分自身を読んでしまうため、配列を作れない!
    end
  end
end

p NameSpace::Array.new.overwrite

単にObject::Arrayと名前空間を指定して呼べば問題ないのですが、Rubyは::Arrayだけで省略して呼ぶ方法を許しています。

項目12 さまざまな等値の違いを理解しよう

Rubyにはオブジェクトの等値を調べる方法が4種類もあります。

==

値が等しいかどうかを調べます。別のクラスであっても値が同じであれば、trueになります。

irb(main):004:0> 1 == 1.0 # IntegerとFloatの比較だがtrueになる

=> true

===

基本的に==と同じですが、===は、case文内の比較で使用されます。
caseがxでwhenがy とするとy === xになる。
whenのオブジェクトが===をオーバーライドしていると比較の基準が変わるので注意が必要です。

eql?

==より厳密で同じクラスでないとfalseになります。

irb(main):001:0> 1.eql?(1.0) # IntegerとFloatの比較だとfalseになる

=> false

また、eql?はハッシュのキーの参照に使用されるため、以下は別のキーになります。

x = 1
y = 1.0

{ x: 'xです', y: 'yです' }

equal?

値のobject_idが同じかどうかまでを比較してtrueを返します。

項目13 "<=>"とComparableモジュールで比較を実装しよう

オブジェクトの順序を比較したい場合は、"<=>"を定義し、Comparableモジュールをincludeして比較しましょう。

ここで<=>演算子についてです。

irb(main):007:0> 1 <=> '1' # 左被演算子と右被演算子が比較出来ない場合nilを返す
=> nil
irb(main):008:0> 0 <=> 1   # 左被演算子が小さければ-1を返す
=> -1
irb(main):009:0> 1 <=> 1   # 左被演算子と右被演算子が同じであれば0を返す
=> 0
irb(main):010:0> 2 <=> 1   # 左被演算子が大きければ1を返す
=> 1 

この<=>演算子を定義し、更にComparableモジュールをインクルードすると<、<=、==、>、>=が使えるようになります。<=>とComparableモジュールの2つで比較に関する機能を実装する事が出来ます。

また、インスタンスをハッシュキーとして使う場合は、eql?を==の別名にすることを検討しましょう。
eql?のデフォルト実装はequal?と同じことをしますが、object_idが同じかどうかまでを比較するため無意味です。

項目14 protectedメソッドを使ってプライベートな状態を共有しよう

RubyのPrivateメソッドは明示的にレシーバを指定して呼び出せないという制限があります。

同じクラスのオブジェクトか、共通のスーパークラスを持つクラスであれば、protectedメソッドを使用する事により、レシーバを指定でき、プライベートという情報を共有する事が出来ます。

項目15 クラス変数よりもクラスインスタンス変数を使うようにしよう

クラス変数よりもクラスインスタンス変数を使うようにしましょう。

クラス変数はクラスで共有出来る変数ですが、変数が継承先にも引き継がれリセットされません。


class Test1
  @@class_variable = 'クラス変数'
end

class Test2 < Test1
  def class_variable
    p @@class_variable
  end
end

Test2.new.class_variable

# 出力結果
"クラス変数"

クラスインスタンス変数を使用するとそのクラスのみで共有する変数を作る事が可能です。
このクラスインスタンス変数は、クラスメソッドのみアクセス可能で、インスタンスメソッドからアクセスする事は出来ません。

class Test2 < Test1
  @class_instance_variable = 'クラスインスタンス変数'

  def self.class_instance_variable
    p @class_instance_variable
  end
end

Test2.class_instance_variable

あまりクラスインスタンス変数を使う機会がなく、わかりにくかったのですが、以下の記事が非常に役に立ちました。
https://qiita.com/mogulla3/items/cd4d6e188c34c6819709

まとめと感想

他にもやりたい事や読みたい本やがあるので、なかなか『Effective Ruby』に集中出来てませんが、ルーチンワーク的に『Effective Ruby』をまとめられるといいなあと思ってます。継続して行こう。