extend self in ruby
以下のコードは ruby 1.9.3 でテストされています。
Class is meant for both data and behavior
Lets look at this ruby code.
1class Util2 def self.double(i)3 i*24 end5end67Util.double(4) #=> 8
ここでUtil
クラスを作成しました。 しかし、このクラス上のメソッドはすべてクラスメソッドであることに注意してください。 このクラスはインスタンス変数を持っていません。
Similar utility tools in ruby
さて、この議論を理解するために、同様のことを行ういくつかの ruby のメソッドを見てみましょう。
1require 'base64'2Base64.encode64('hello world') #=> "aGVsbG8gd29ybGQ=compine "34require 'benchmark'5Benchmark.measure { 10*2000 }67require 'fileutils'8FileUtils.Datacenter'8FileUtils.Datacenter'8Benchmark.Measure { 10*2000 }67。chmod 0644, 'test.rb'910Math.sqrt(4) #=> 2
上記のすべてのケースで、最初にインスタンスを作成せずにクラスメソッドが呼び出されます。
1Base64.class #=> Module2Benchmark.class #=> Module3FileUtils.class #=> Module4Math.class #=> Module
つまりこれらはクラスではなくモジュールなんですね。 なぜ ruby-core の賢い人たちは、私が Util でやったようにクラスを作らず、モジュールとして実装したのでしょうか。
理由は、double
のようにメソッドだけを作成するにはクラスは重すぎるということです。 先に説明したように、クラスはデータと動作の両方を持つことになっています。
extend self is the answer
extend self
の説明に入る前に、Class
から Module
に移行した後の Util
クラスはどのように見えるかを説明します。
1module Util2 extend self34 def double(i)5 i * 26 end7end89puts Util.double(4) #=> 8
So how does extend self work
First lets see what extend does.まず最初に、extendが何をするか見てみましょう。
1module M2 def double(i)3 i * 24 end5end67class Calculator8 extend M9end10puts Calculator.double(4)
上記の場合、Calculator
はモジュールM
を拡張していて、したがってモジュールM
のすべてのインスタンスメソッドは直接Calculator
で利用可能になっています。
ここで、Calculator
がモジュールである場合のバリエーションを試してみましょう。
1module M2 def double(i)3 i * 24 end5end67module Calculator8 extend M9end10puts Calculator.double(4) #=> 8
ここで Calculator は他のモジュールを拡張しているモジュールです。
ここで、モジュールが他のモジュールを拡張できることを理解し、上記のコードを見て、なぜモジュール M
が必要かを疑問視してください。 なぜ、メソッド double
を直接モジュール Calculator に移動できないのでしょうか。
1module Calculator2 extend Calculator34 def double(i)5 i * 26 end7end8puts Calculator.double(4) #=> 8
モジュール M
を取り除き、モジュール Calculator
内に double
というメソッドを移動させています。 モジュール M
がなくなったので、extend M
から extend Calculator
に変更しました。
最後にもう一つ修正。
モジュール Calculator 内の self
とは何ですか。 self
は、モジュールCalculator
そのものです。 ですから、Calculator
を2回繰り返す必要はありません。 以下は最終版です
1module Calculator2 extend self34 def double(i)5 i * 26 end7end8puts Calculator.double(4) #=> 8
クラスをモジュール化する
extend self
みたいなコードに出会うたびに頭が一瞬ポカーンとなりますね。 そして、それをググるのです。 それについて読むでしょう。 3ヵ月後、私はこのプロセスを繰り返します。
それを学ぶ最善の方法は、それを使うことです。 だから、私はextend self
を使うケースを探し始めた。
ここで、私がプロジェクトで使用した Util
クラスのメソッドの before snapshot をお見せします。in_cents(amount); end4 def self.localhost2public_url(url, protocol); end5end
extend self
を使った後のコードは
1module Util2 extend self34 def config2hash(file); end5 def in_cents(amount); end6 def localhost2public_url(url, protocol); end7end
かなり良くなりましたね。 8305>
Another usage inline with how Rails uses extend self
ここで私は e コマース アプリケーションを作っており、それぞれの新しい注文はサード パーティの販売アプリケーションから新しい注文番号を取得する必要があります。 コードは次のようになります。 この議論には関係ないので、メソッドの実装は省略しています。
1class Order2 def amount; end3 def buyer; end4 def shipped_at; end5 def number6 @number || self.class.next_order_number7 end89 def self.next_order_number; 'A100'; end10end1112puts Order.new.number #=> A100
ここで、next_order_number
メソッドは他の販売システムに対して複雑な呼び出しを行っているかも知れません。 理想的には、クラスOrder
はメソッドnext_order_number
を公開しないことです。 そこで、このメソッドをprivate
にすることができますが、それでは根本的な解決にはなりません。 問題は、モデル Order
が新しい注文番号がどのように生成されるかを知らないことです。 next_order_number
メソッドを別の Util
クラスに移動することはできますが、それでは距離がありすぎます。
ここで、extend self
を使用した解決策を示します。
1module Checkout2 extend self34 def next_order_number; 'A100'; end56 class Order7 def amount; end8 def buyer; end9 def shipped_at; end10 def number11 @number || Checkout.next_order_number12 end13 end14end1516puts Checkout::Order.new.number #=> A100
だいぶ良くなりましたね。 Order クラスはメソッド next_order_number
を公開しておらず、このメソッドは同じファイル内にあります。 Util
クラスを開く必要はありません。
。