Variabel instance kelas Ruby vs variabel kelas

179

Saya membaca " Kapan variabel instance Ruby diatur? " Tapi saya dua pikiran kapan menggunakan variabel instance kelas.

Variabel kelas dibagi oleh semua objek kelas, variabel Instance milik satu objek. Tidak ada banyak ruang tersisa untuk menggunakan variabel instance kelas jika kita memiliki variabel kelas.

Bisakah seseorang menjelaskan perbedaan antara keduanya dan kapan menggunakannya?

Ini contoh kode:

class S
  @@k = 23
  @s = 15
  def self.s
    @s
  end
  def self.k
     @@k
  end

end
p S.s #15
p S.k #23

Saya mengerti sekarang, Variabel Kelas Instans tidak diteruskan sepanjang rantai pewarisan!

Elmor
sumber

Jawaban:

276

Variabel instance pada kelas:

class Parent
  @things = []
  def self.things
    @things
  end
  def things
    self.class.things
  end
end

class Child < Parent
  @things = []
end

Parent.things << :car
Child.things  << :doll
mom = Parent.new
dad = Parent.new

p Parent.things #=> [:car]
p Child.things  #=> [:doll]
p mom.things    #=> [:car]
p dad.things    #=> [:car]

Variabel kelas:

class Parent
  @@things = []
  def self.things
    @@things
  end
  def things
    @@things
  end
end

class Child < Parent
end

Parent.things << :car
Child.things  << :doll

p Parent.things #=> [:car,:doll]
p Child.things  #=> [:car,:doll]

mom = Parent.new
dad = Parent.new
son1 = Child.new
son2 = Child.new
daughter = Child.new

[ mom, dad, son1, son2, daughter ].each{ |person| p person.things }
#=> [:car, :doll]
#=> [:car, :doll]
#=> [:car, :doll]
#=> [:car, :doll]
#=> [:car, :doll]

Dengan variabel instan pada kelas (bukan pada instance kelas itu) Anda dapat menyimpan sesuatu yang umum untuk kelas itu tanpa memiliki sub-kelas secara otomatis juga mendapatkannya (dan sebaliknya). Dengan variabel kelas, Anda memiliki kemudahan untuk tidak harus menulis self.classdari objek instan, dan (bila diinginkan) Anda juga mendapatkan pembagian otomatis di seluruh hierarki kelas.


Menggabungkan ini bersama-sama menjadi satu contoh yang juga mencakup variabel instan pada instance:

class Parent
  @@family_things = []    # Shared between class and subclasses
  @shared_things  = []    # Specific to this class

  def self.family_things
    @@family_things
  end
  def self.shared_things
    @shared_things
  end

  attr_accessor :my_things
  def initialize
    @my_things = []       # Just for me
  end
  def family_things
    self.class.family_things
  end
  def shared_things
    self.class.shared_things
  end
end

class Child < Parent
  @shared_things = []
end

Dan kemudian beraksi:

mama = Parent.new
papa = Parent.new
joey = Child.new
suzy = Child.new

Parent.family_things << :house
papa.family_things   << :vacuum
mama.shared_things   << :car
papa.shared_things   << :blender
papa.my_things       << :quadcopter
joey.my_things       << :bike
suzy.my_things       << :doll
joey.shared_things   << :puzzle
suzy.shared_things   << :blocks

p Parent.family_things #=> [:house, :vacuum]
p Child.family_things  #=> [:house, :vacuum]
p papa.family_things   #=> [:house, :vacuum]
p mama.family_things   #=> [:house, :vacuum]
p joey.family_things   #=> [:house, :vacuum]
p suzy.family_things   #=> [:house, :vacuum]

p Parent.shared_things #=> [:car, :blender]
p papa.shared_things   #=> [:car, :blender]
p mama.shared_things   #=> [:car, :blender]
p Child.shared_things  #=> [:puzzle, :blocks]  
p joey.shared_things   #=> [:puzzle, :blocks]
p suzy.shared_things   #=> [:puzzle, :blocks]

p papa.my_things       #=> [:quadcopter]
p mama.my_things       #=> []
p joey.my_things       #=> [:bike]
p suzy.my_things       #=> [:doll] 
Phrogz
sumber
@ Pironz Apa perbedaan antara self.things dan self.class.things yang Anda sebutkan dalam kode?
cyborg
1
@cyborg self.thingsmereferensikan metode thingsdalam lingkup saat ini (dalam hal instance kelas, itu akan menjadi metode instance), di mana self.class.thingsreferensi thingsmetode dari kelas lingkup saat ini (sekali lagi dalam kasus instance dari kelas itu berarti metode kelas).
graffzon
Penjelasan yang indah.
aliahme922
30

Saya percaya perbedaan utama (hanya?) Adalah warisan:

class T < S
end

p T.k
=> 23

S.k = 24
p T.k
=> 24

p T.s
=> nil

Variabel kelas dibagi oleh semua "instance kelas" (yaitu subclass), sedangkan variabel instance kelas hanya khusus untuk kelas itu. Tetapi jika Anda tidak pernah berniat untuk memperpanjang kelas Anda, perbedaannya adalah murni akademis.

bioneuralnet
sumber
1
Itu bukan satu-satunya perbedaan. Instance "shared" vs "instance" melangkah lebih jauh dari sekadar pewarisan. Jika Anda meletakkan getter instan Anda akan mendapatkan S.new.s => nildan S.new.k => 23.
Andre Figueiredo
27

Sumber

Ketersediaan untuk metode instan

  • Variabel instance kelas hanya tersedia untuk metode kelas dan bukan untuk metode instance.
  • Variabel kelas tersedia untuk metode instan dan metode kelas.

Warisan

  • Variabel instance kelas hilang dalam rantai pewarisan.
  • Variabel kelas tidak.
class Vars

  @class_ins_var = "class instance variable value"  #class instance variable
  @@class_var = "class variable value" #class  variable

  def self.class_method
    puts @class_ins_var
    puts @@class_var
  end

  def instance_method
    puts @class_ins_var
    puts @@class_var
  end
end

Vars.class_method

puts "see the difference"

obj = Vars.new

obj.instance_method

class VarsChild < Vars


end

VarsChild.class_method
Sachin Saxena
sumber
15

Seperti yang dikatakan orang lain, variabel kelas dibagi antara kelas yang diberikan dan subkelasnya. Variabel instance kelas milik tepat satu kelas; subkelasnya terpisah.

Mengapa perilaku ini ada? Ya, semua yang ada di Ruby adalah objek — bahkan kelas. Itu berarti bahwa setiap kelas memiliki objek kelas Class(atau lebih tepatnya, subkelas Class) yang sesuai dengannya. (Ketika Anda mengatakan class Foo, Anda benar-benar mendeklarasikan konstanta Foodan menetapkan objek kelas untuk itu.) Dan setiap objek Ruby dapat memiliki variabel instan, jadi objek kelas dapat memiliki variabel instan juga.

Masalahnya adalah, variabel instan pada objek kelas tidak benar-benar berperilaku seperti biasanya Anda ingin variabel kelas berperilaku. Anda biasanya ingin variabel kelas yang didefinisikan dalam superclass untuk dibagikan dengan subkelasnya, tetapi bukan itu cara variabel instan bekerja — subkelas memiliki objek kelasnya sendiri, dan objek kelas itu memiliki variabel turunannya sendiri. Jadi mereka memperkenalkan variabel kelas terpisah dengan perilaku yang lebih Anda inginkan.

Dengan kata lain, variabel instance kelas adalah semacam kecelakaan desain Ruby. Anda mungkin tidak boleh menggunakannya kecuali Anda secara khusus tahu itu yang Anda cari.

Brent Royal-Gordon
sumber
jadi variabel kelas seperti variabel statis di Jawa?
Kick Buttowski
3

FAQ Ruby Resmi: Apa perbedaan antara variabel kelas dan variabel instance kelas?

Perbedaan utama adalah perilaku mengenai pewarisan: variabel kelas dibagi antara kelas dan semua subkelasnya, sementara variabel instance kelas hanya milik satu kelas tertentu.

Variabel kelas dalam beberapa cara dapat dilihat sebagai variabel global dalam konteks hierarki warisan, dengan semua masalah yang datang dengan variabel global. Sebagai contoh, variabel kelas mungkin (secara tidak sengaja) dipindahkan oleh salah satu subkelasnya, yang memengaruhi semua kelas lainnya:

class Woof

  @@sound = "woof"

  def self.sound
    @@sound
  end
end

Woof.sound  # => "woof"

class LoudWoof < Woof
  @@sound = "WOOF"
end

LoudWoof.sound  # => "WOOF"
Woof.sound      # => "WOOF" (!)

Atau, suatu kelas leluhur mungkin kemudian dibuka kembali dan diubah, dengan kemungkinan efek mengejutkan:

class Foo

  @@var = "foo"

  def self.var
    @@var
  end
end

Foo.var  # => "foo" (as expected)

class Object
  @@var = "object"
end

Foo.var  # => "object" (!)

Jadi, kecuali Anda tahu persis apa yang Anda lakukan dan secara eksplisit membutuhkan perilaku semacam ini, Anda sebaiknya menggunakan variabel instance kelas.

notapatch
sumber
2

Bagi mereka yang memiliki latar belakang C ++, Anda mungkin tertarik dalam perbandingan dengan setara C ++:

class S
{
private: // this is not quite true, in Ruby you can still access these
  static int    k = 23;
  int           s = 15;

public:
  int get_s() { return s; }
  static int get_k() { return k; }

};

std::cerr << S::k() << "\n";

S instance;
std::cerr << instance.s() << "\n";
std::cerr << instance.k() << "\n";

Seperti yang bisa kita lihat, kadalah staticvariabel like. Ini 100% seperti variabel global, kecuali bahwa itu dimiliki oleh kelas ( dicakup untuk menjadi benar). Ini membuatnya lebih mudah untuk menghindari perselisihan antara variabel yang bernama sama. Seperti variabel global apa pun, hanya ada satu instance dari variabel itu dan memodifikasinya selalu terlihat oleh semua.

Di sisi lain, sadalah nilai spesifik objek. Setiap objek memiliki nilai instansinya sendiri. Di C ++, Anda harus membuat instance untuk memiliki akses ke variabel itu. Di Ruby, definisi kelas itu sendiri adalah turunan dari kelas (dalam JavaScript, ini disebut prototipe), oleh karena itu Anda dapat mengakses sdari kelas tanpa instantiasi tambahan. Instance kelas dapat dimodifikasi, tetapi modifikasi sakan spesifik untuk setiap instance (setiap objek tipe S). Jadi memodifikasi satu tidak akan mengubah nilai yang lain.

Alexis Wilke
sumber
1

Meskipun mungkin langsung bermanfaat untuk menggunakan variabel instance kelas, karena variabel instance kelas dibagi di antara subclass dan mereka dapat dirujuk dalam metode singleton dan instance, ada kelemahannya. Mereka dibagikan dan subclass dapat mengubah nilai variabel instance kelas, dan kelas dasar juga akan dipengaruhi oleh perubahan, yang biasanya merupakan perilaku yang tidak diinginkan:

class C
  @@c = 'c'
  def self.c_val
    @@c
  end
end

C.c_val
 => "c" 

class D < C
end

D.instance_eval do 
  def change_c_val
    @@c = 'd'
  end
end
 => :change_c_val 

D.change_c_val
(irb):12: warning: class variable access from toplevel
 => "d" 

C.c_val
 => "d" 

Rails memperkenalkan metode praktis yang disebut class_attribute. Seperti namanya, itu menyatakan atribut tingkat kelas yang nilainya diturunkan oleh subclass. Nilai class_attribute dapat diakses dalam metode singleton dan instance, seperti halnya dengan variabel instance kelas. Namun, manfaat besar dengan class_attribute di Rails adalah subclass dapat mengubah nilainya sendiri dan tidak akan memengaruhi kelas induk.

class C
  class_attribute :c
  self.c = 'c'
end

 C.c
 => "c" 

class D < C
end

D.c = 'd'
 => "d" 

 C.c
 => "c" 
Donato
sumber
Panggilan bagus, saya belum pernah menggunakan ini sebelumnya. Tampaknya berfungsi meskipun Anda harus memastikan untuk menambahkan self.setiap kali Anda ingin mengakses atribut c, misalnya self.c. Dokumen mengatakan suatu default:parameter dapat diteruskan class_attributetetapi tampaknya tidak berfungsi karena poin yang baru saja saya sebutkan self.
Dex
Ketika Anda mengatakan "Meskipun mungkin langsung berguna untuk menggunakan variabel instance kelas", saya pikir maksud Anda adalah "variabel kelas", bukan "variabel instance kelas bukan?" (Lihat ruby-lang.org/en/documentation/faq/8/. )
Keith Bennett
Ya, jawaban ini sepenuhnya membingungkan "variabel instance kelas" dan "variabel kelas", yang merupakan inti dari pertanyaan.
stevo