Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

字句スコープとRubyクラス変数

Rubyのクラス変数は紛らわしいです。熟練したRubyユーザーでさえ、直感的に理解するのが難しい場合があります。最も明白な例は、継承と関係があります:

class Fruit
  @@kind = nil

  def self.kind
    @@kind
  end
end

class Apple < Fruit
  @@kind = "apple"
end

Apple.kind
# => "apple" 

Fruit.kind
# => "apple" 

kindの変更 子クラスの変数は、親クラスでも変更します。それはかなりめちゃくちゃです。しかし、これはまさにその言語が機能することになっている方法です。 Smalltalkを模倣することは、ずっと前に行われた設計上の決定でした。

悪化します

クラス変数の奇妙さの他の例は、実装の癖ほどアーキテクチャ上の選択ではないようです。今日は、私が面白いと思うこれらの1つについて少しお話します。

次に、2つのコードを比較します。それらは同じ結果を生成するはずのように見えますが、そうではありません。

最初の例では、クラス変数を設定し、それを返すメソッドを作成します。ここでは何も派手なことはありません。そして、すべてが期待どおりに機能します。

class Foo
  @@val = 1234

  # This is shorthand for declaring a class method.
  class << self
    def val
      @@val
    end
  end
end

Foo.val
# => 1234

おそらくあなたはこれを知らなかったでしょうが、class << self クラス定義内にある必要はありません。以下の例では、それを外部に移動しました。クラスメソッドが追加されましたが、クラス変数にアクセスできません。

class Bar
  @@val = 1234
end

class << Bar
  def val
    @@val
  end
end

Bar.val

# warning: class variable access from toplevel
# NameError: uninitialized class variable @@val in Object

関数からクラス変数にアクセスしようとすると、警告と例外が発生します。何が起こっているのですか?

字句スコープを入力

語彙スコープがRubyの奇妙で紛らわしい側面の99%の原因であると私はますます確信するようになっています。

この用語に精通していない場合、字句スコープとは、抽象オブジェクトモデルのどこに属するかではなく、コード内のどこにあるかに基づいて物事をグループ化することを指します。例を示すだけの方がはるかに簡単です:

class B
  # x and y share the same lexical scope
  x = 1
  y = 1
end

class B
  # z has a different lexical scope from x and y, even though it's in the same class. 
  z = 3
end
クラスは字句的に決定されます

では、クラス変数の例では、字句スコープはどのように機能していますか?

クラス変数を取得するには、Rubyはどのクラスから取得するかを知っている必要があります。字句スコープを使用してクラスを検索します。

実際の例を詳しく見ると、クラス変数にアクセスするコードが物理的にクラス定義に含まれていることがわかります。

class Foo  
  class << self
    def val
      # I'm lexically scoped to Foo!
      @@val
    end
  end
end

動作しない例では、クラス変数にアクセスするコードは、字句的にクラスにスコープされていません。

class << Bar
  def val 
    # Foo is nowhere in sight. 
    @@val
  end
end

しかし、それが語彙的にクラスにスコープされていない場合、それは何にスコープされますか? Rubyが出力する警告は、私たちに手がかりを与えます:warning: class variable access from toplevel

動作しない例では、クラス変数が字句的に最上位オブジェクトにスコープされていることがわかります。これにより、非常に奇妙な動作が発生する可能性があります。

たとえば、字句スコープがmainにあるコードからクラス変数を設定しようとすると、クラス変数はObjectに設定されます。 。

class Bar
end

class << Bar
  def val=(n)
    # This code is lexically scoped to the top level object. 
    # That means, that `@@val` will be set on `Object`, not `Bar`
    @@val = i
  end
end

Bar.val = 100

# Whaa?
Object.class_variables
# => [:@@foo]
その他の例

クラスの字句スコープ外でクラス変数を参照する方法はたくさんあります。それらのすべてはあなたに問題を与えるでしょう。

ここにあなたの楽しみのためのいくつかの例があります:

class Foo
  @@foo = :foo
end

# This won't work
Foo.class_eval { puts @@foo }

# neither will this
Foo.send :define_method, :x do 
  puts @@foo
end

# ..and you weren't thinking about using modules, were you? 
module Printable
  def foo
    puts @@foo
  end
end

class Foo
  @@foo = :foo
  include Printable
end

Foo.new.foo

そして今、あなたは誰もがRubyでクラス変数を決して使用しないと言う理由を知っています。 :)


  1. Rubyで環境変数を使用する方法

    環境変数はキーと値のペアであり、次のようになります。 KEY=VALUE これらの変数を使用して、コンピューター内のすべてのプログラム間で構成オプションを共有します。 そのため、それらがどのように機能するか、およびENVを使用してRubyプログラムからそれらにアクセスする方法を学ぶことが重要です。 特別な変数。 環境変数の例 : デフォルトのエディターの構成 宝石の場所をRubyに伝える(GEM_PATH / GEM_HOME ) APIキーを、ソース管理(git)にコミットせずにアプリケーションに渡す オペレーティングシステムがバイナリファイル(Windowsでは.exe)を検

  2. Rubyのデコレータデザインパターン

    デコレータのデザインパターンは何ですか? そして、Rubyプロジェクトでこのパターンをどのように使用できますか? デコレータデザインパターンは、新機能を追加することでオブジェクトを強化するのに役立ちます クラスを変更せずにそれに。 例を見てみましょう! ロギングとパフォーマンス この例では、rest-clientのようなgemを使用してHTTPリクエストを作成しています。 次のようになります: require restclient data = RestClient.get(www.rubyguides.com) 今 : 一部のリクエストにログを追加したいが、RestCli