Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Ruby

เข้าใจ Ruby Class Variables และ Scope: คู่มือชัดเจน

Class variables ของ Ruby สับสน แม้ expert Ruby users ก็หา intuition ยาก ตัวเด่นคือ inheritance:


class Fruit
  @@kind = nil
  def self.kind
    @@kind
  end
end

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

Apple.kind # => "apple"
Fruit.kind # => "apple"

เปลี่ยน kind variable บน child class เปลี่ยนบน parent ด้วย มัน messed up แต่เป็น design decision แบบเก่าเลียนแบบ Smalltalk

แย่กว่านี้

มี weirdness อื่นๆ ที่ไม่ใช่ architectural choices แต่ implementation quirks วันนี้พูดถึงอันที่ผมสนใจ

เปรียบเทียบสองชิ้นโค้ด ดูเหมือนควรผลลัพธ์เหมือนกัน แต่ไม่

ตัวอย่างแรก: set class variable สร้างเมธอดคืนค่า ไม่มีอะไร fancy ทุกอย่าง work ตามคาด


class Foo
  @@val = 1234
  # Shorthand สำหรับ declare class method
  class << self
    def val
      @@val
    end
  end
end

Foo.val # => 1234

อาจไม่รู้ว่า class << self ไม่จำเป็นต้องอยู่ใน class definition ตัวอย่างย้ายออกมา class method เพิ่มแตเข้าถึง class variable ไม่ได้


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

พยายามเข้าถึง class variable จาก function ได้ warning และ exception เกิดอะไรขึ้น?

เข้าสู่ Lexical Scope

ผมเชื่อมากขึ้นว่า lexical scope เป็นต้นทาง 99% ของ weird/confusing aspects ของ Ruby

ถ้าไม่คุ้น lexical scoping: grouping things ตามที่ปรากฏในโค้ด ไใช่ตาม abstract object model ตัวอย่างง่าย:


class B
  # x และ y share same lexical scope
  x = 1
  y = 1
end

class B
  # z lexical scope ต่างจาก x และ y แม้อยู่ในคลาสเดียวกัน
  z = 3
end

Class กำหนดโดย Lexically

Lexical scope ทำงานใน class variable example ยังไง?

เพื่อ retrieve class variable Ruby ต้องรู้ว่า class ไหน ใช้ lexical scoping หา class

ดูตัวอย่างที่ work โค้ดเข้าถึง class variable อยู่ physically within class definition:


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

ตัวอย่าง non-working โค้ดเข้าถึง class variable ไม่ได้ lexically scoped to the class:


class << Bar
  def val
    # Foo ไม่อยู่ที่ไหนเลย
    @@val
  end
end

แตถ้าไม่ได้ lexically scoped to the class scoped to อะไร? Warning ให้ clue: warning: class variable access from toplevel

ตัวอย่าง non-working class variable lexically scoped to top-level object อาจทำ strange behavior

เช่น set class variable จาก code lexically scoped to main class variable set บน Object:


class Bar
end

class << Bar
  def val=(n)
    # Code นี้ lexically scoped to top-level object
    # หมายความว่า `@@val` จะ set บน `Object`, ไม่ใช่ `Bar`
    @@val = n
  end
end

Bar.val = 100
# Whaa?
Object.class_variables # => [:@@foo]

ตัวอย่างเพิ่มเติม

มีหลายวิธี reference class variables นอก lexical scope ของ class ทั้งหมดทำปัญหา

ตัวอย่างเพื่อความเพลิดเพลิน:


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

ตอนนี้คุณรู้ทำไมทุกคนบอกว่าอย่าใช้ class variables ใน Ruby :)