1

我是 ruby​​ 和编程的新手,我正在尝试掌握一些关键概念。鉴于我有一个类 Dog,具有以下特征。

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:0x007fa8689ea238 @type=nil> 

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

 Owner.new(@dog)

当我在所有者类中并调用@dog.popularity 时,它如何知道该实例的流行度值?在运行时是否处理了所有方法,然后该实例总是与当时的值相关联?抱歉,如果这没有任何意义,或者我离题了。

4

4 回答 4

6

当你这样做

@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 对象传递给所有所有者(即它们都有效地共享一条狗)。因此,您需要了解何时创建新实例(通过 new)以及何时仅处理现有引用。

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

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

于 2013-03-07T20:59:18.813 回答
2

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

数狗:)

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

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

还有另一种方法可以使用“类对象的实例变量”来做到这一点,但这是一个有点高级的话题。

访问实例变量

在 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 知道它是一个对象引用,并查看对象内部以及它的继承链如何解决问题。

解析方法名称

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

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

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
             # e.g. it overrides the automatically generated "legs" method
 => 4 

attr_accessor :legs只是执行此操作的简写符号:

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

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

我希望这对你有意义

于 2013-03-07T21:56:06.987 回答
0

创建对象时,不需要使用@符号。变量是对象。因此,如果您有多只狗,您会这样做:

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
于 2013-03-07T20:56:49.977 回答
0

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

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 上的流行度方法,它会即时进行计算(由您定义)并返回结果。这里没有魔法!

于 2013-03-07T21:13:17.010 回答