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 クラスを開く必要はありません。

Articles

コメントを残す

メールアドレスが公開されることはありません。