Ruby / Rails:了解ruby getter-setter方法和实例

我是ruby和编程的新手,我正在努力掌握一些关键概念。 鉴于我有一个类狗,具有以下特点。

class Dog attr_accessor :type, :popularity, :total def initialize(type = nil) @type = type end def total_dogs Dog.count end def total Dog.where(:type => self.type).size end def popularity total.to_f/total_dogs end end 

我想要了解的是,ruby如何通过getter / setter方法将属性持久化到实例。 我清楚地知道,如果我实例化一个新实例然后将属性保存到该实例,那么这些属性就与该实例相关联,因为如果我查看该对象,则属性如下所示:

  @dog = Dog.new @dog => # 

我很容易理解,当我传递@dog对象时,总是将@type属性作为nil。 但是,我无法理解的情况是我将这个@dog对象传递给另一个类。 就像我做的那样:

  Owner.new(@dog) 

当我在所有者类中并且我打电话给@ dog.popularity时,它如何知道该实例的受欢迎程度值? 在运行时是否处理了所有方法,然后该实例总是与当时的值绑定? 抱歉,如果这没有意义,或者我离开了。

当你这样做

 @dog = Dog.new 

你做了两件特别的事情

1)为代码当前所在的任何对象创建一个实例变量@dog

2)实例化Dog的新实例(包含其所有方法和属性)并将其引用分配给@dog

@dog是一个变量,恰好指向你在那时创建的Dog实例(“类的实例”,通常意思是“对象”)。 您可以将其他变量设置为指向同一个实例,而在Ruby中,这通常是您传递数据的方式。 对象包含实例变量,这些实例变量指向更多对象。

使用赋值运算符(即“=”),您可以将变量指向任何其他对象。

要依次回答您的问题:

当我在所有者类中并且我打电话给@ dog.popularity时,它如何知道该实例的受欢迎程度值?

您必须小心使用Ruby(以及一般的OO语言)来区分描述和问题中的类和对象。 Ruby我假设您指的是Owner类中的一行代码,并且您打算使用所有者对象。 我还假设@dog是你添加到所有者的属性。

在这种情况下,Ruby知道因为@dog指向您添加到所有者的Dog对象。 每个Dog对象都有自己的所有Dog实例变量的副本。 你确实需要在Ruby中注意,因为变量指向对象,你不是简单地将同一个Dog对象传递给所有的所有者(即它们都有效地共享一只狗)。 因此,您需要了解何时创建新实例(通过新实例)以及何时只是处理现有引用。

在运行时是否处理了所有方法,然后该实例总是与当时的值绑定?

不可以。在运行时,基本的Ruby只会执行您编码的分配。 在分配了分配它们的代码之前,实例变量甚至可能不存在。 如果你使用attr_reader等方法,那么变量至少会存在(但除非你在初始化期间分配一些东西,否则它们将是nil)

尼尔有一个很好的答案,我只是想添加一些东西。

数狗 🙂

你需要一个类变量来做这个..

 class Dog @@count = 0 # this is a class variable; all objects created by this class share it def initialize @@count += 1 # when we create a new Dog, we increment the count end def total @@count end end 

还有另一种方法可以使用“Class对象的实例变量”,但这是一个高级主题。

访问实例变量

在Ruby中,变量实际上只是对对象/实例的引用。

  > x = 1 => 1 > x.class => Fixnum > 1.instance_variables => [] 

x是对象’1’的引用,它是类Fixnum的一个实例。 ‘1’对象是Fixnum的一个实例,它不包含任何实例变量。 它与新的“Dog”实例的引用没有任何不同。

同样,你可以说x = Dog.new ,然后x是对Dog类实例的引用。

 class Dog attr_accessor :legs # this defines the 'legs' and 'legs=' methods! end x = Dog.new x.instance_variables => [] # if you would assign legs=4 during "initialize", then it would show up here x.legs = 4 # this is really a method call(!) to the 'legs' method x.instance_variables # get created when they are first assigned a value => [:legs] 

如果你将这样的引用传递给方法调用,或者只传递给另一个类或者只是自己评估它也没关系 – Ruby知道它是一个对象引用,并查看对象内部以及它是如何解决问题的inheritance链。

解析方法名称

这只是部分事实:)当解释x.legs ,Ruby会检查对象的类inheritance链中是否存在一个响应该名称“leg”的方法。 它并没有神奇地访问具有相同名称的实例变量!

我们可以通过执行“attr_reader:legs”或“attr_accessor:legs”来定义方法’leg’,或者通过自己定义方法来定义方法’leg’。

 class Dog def legs 4 # most dogs have 4 legs, we don't need a variable for that end end x.legs # this is a method call! it is not directly accessing a :legs instance variable! => 4 x.instance_variables => [] # there is no instance variable with name ":legs" 

如果我们尝试将其作为方法和实例变量实现,则会发生这种情况::)

 class Dog attr_accessor :legs # this creates "def legs" and "def legs=" methods behind the scenes def legs # here we explicitly override the "def legs" method from the line above. 4 end end x = Dog.new x.legs # that's the method call we implemented explicitly => 4 x.legs = 3 # we can still assign something to the instance_variable via legs= => 3 x.legs # the last definition of a method overrides previous definitions # eg it overrides the automatically generated "legs" method => 4 

attr_accessor :legs只是表示这样做的简写:

 class Dog def legs @legs end def legs=(value) @legs = value end end 

没有神奇的方式实例变量被自动访问。 它们总是通过一种方法访问,以后可以覆盖它。

我希望这对你有意义

创建对象时,不需要使用@符号。 变量是对象。 所以,如果你有多只狗,你会做:

 myDog = Dog.new(brown) yourDog = Dog.new(white) 

从那里,你可以说:

 yourDog.type #white myDog.type #brown 

你不会做的是:

 @dog = Dog.new #myDog @dog = Dog.new #yourDog 

如果您需要一个对象的多个版本,您只需给它们不同的名称。 因此,如果您创建多只狗并将它们传递给其他对象,它们将起作用。 例如:

假设您的所有者类是:

 Class Owner def initialize(pet) puts "my pet is #{pet.type}" end 

然后使用实例变量将是:

 me = Owner.new(myDog) #my pet is brown you = Owner.new(yourDog) #my pet is white 

“类型”和“流行度”都是“狗”实例上的方法。 他们的定义如下:

 class Dog # getter def type @type end def popularity total.to_f/total_dogs end end 

这大致相当于:

 class Dog attr_accessor :type def popularity total.to_f/total_dogs end end 

请注意,attr_accessor只是定义getter方法的快捷方式。 如果你自己定义一个方法,使用attr_accessor是没有意义的:

 class Dog attr_accessor :popularity # this will override getter defined by attr_accessor def popularity total.to_f/total_dogs end end 

回到你的问题:@ dog.type在@dog上调用type方法,返回它的实例变量; @ dog.popularity在@dog上调用流行度方法,它在运行中进行计算(由你定义)并返回结果。 这里没有魔法!