web-dev-qa-db-ja.com

Rubyでグローバル変数または定数値をどのように使用しますか?

次のようなプログラムがあります。

$offset = Point.new(100, 200);

def draw(point)
  pointNew = $offset + point;
  drawAbsolute(point)
end

draw(Point.new(3, 4));

$offsetの使用は少し奇妙に思えます。

Cでは、関数の外側に何かを定義すると、自動的にグローバル変数になります。なぜRubyである必要がありますが$offsetである必要がありますが、offsetであってグローバルであることはできませんか?offsetである場合、ローカル?しかし、それは非常にグローバルに感じられるので、どこでもローカルです。

上記のコードを書くより良い方法はありますか? $offsetの使用は、最初は少しugいように見えるかもしれません。


更新:このオフセットをclass定義内に配置できますが、2つまたは複数のクラスでこの定数を使用する必要がある場合はどうなりますか?この場合、$offsetを定義する必要がありますか?

66

理解する必要があるのは、Rubyすべてがオブジェクトです。ModuleまたはClass内でメソッドを定義しない場合、= RubyはObjectクラス内に配置します。したがって、コードはObjectスコープに対してローカルになります。

オブジェクト指向プログラミングの典型的なアプローチは、クラス内のすべてのロジックをカプセル化することです:

class Point
  attr_accessor :x, :y

  # If we don't specify coordinates, we start at 0.
  def initialize(x = 0, y = 0)
    # Notice that `@` indicates instance variables.
    @x = x
    @y = y
  end

  # Here we override the `+' operator.
  def +(point)
    Point.new(self.x + point.x, self.y + point.y)
  end

  # Here we draw the point.
  def draw(offset = nil)
    if offset.nil?
      new_point = self
    else
      new_point = self + offset 
    end
    new_point.draw_absolute
  end

  def draw_absolute
    puts "x: #{self.x}, y: #{self.y}"
  end
end

first_point = Point.new(100, 200)
second_point = Point.new(3, 4)

second_point.draw(first_point)

これが少し明確になることを願っています。

53
Igor

Rubyの変数スコープはシギルによってある程度制御されます。$で始まる変数はグローバル、@の変数はインスタンス変数、@@はクラスを意味します変数、および大文字で始まる名前は定数です。他のすべての変数はローカルです。クラスまたはメソッドを開くと、それは新しいスコープになり、以前のスコープで使用可能なローカルは使用できなくなります。

私は通常、グローバル変数の作成を避けることを好みます。クリーナーと考え​​るのと同じ目的を一般的に達成する2つの手法があります。

  1. モジュールに定数を作成します。そのため、この場合、オフセットを必要とするすべてのクラスをモジュールFooに配置し、定数Offsetを作成して、すべてのクラスがFoo::Offsetにアクセスできるようにします。

  2. 値にアクセスするメソッドを定義します。メソッドはグローバルに定義できますが、繰り返しますが、モジュールまたはクラスにカプセル化する方が良いと思います。これにより、必要な場所でデータを利用でき、必要に応じてデータを変更することもできますが、プログラムの構造とデータの所有権はより明確になります。これは、OO設計原則に沿ったものです。

110
Chuck

グローバル変数にプレフィックス(「シギル」と呼ばれる)が必要な理由の1つは、RubyではCとは異なり、変数を割り当てる前に変数を宣言する必要がないためです。シギルは、変数のスコープを明示する方法として使用されます。

グローバルの特定のプレフィックスがない場合、drawメソッド内でステートメントpointNew = offset + pointを指定すると、offsetはメソッド内のローカル変数を参照します(この場合はNameErrorになります) )。インスタンス変数の参照に使用される@およびクラス変数の@@についても同じです。

CJavaなどの明示的な宣言を使用する他の言語では、スコープを制御するために宣言の配置が使用されます。

10
mikej