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

За мен е лесно да разбера, че когато предавам обекта @dog, той винаги ще има атрибута @type като нула. Въпреки това, ситуацията, която имам проблеми с разбирането, е, ако предам този обект @dog на друг клас. Както ако го направих:

 Owner.new(@dog)

Когато съм в класа на собственика и се обадя на @dog.popularity, как знае стойността на популярността за този случай? По време на изпълнение всички методи обработват ли се и тогава този екземпляр винаги е свързан със стойността в момента? Извинения, ако това няма смисъл или съм далеч.


person BC00    schedule 07.03.2013    source източник


Отговори (4)


Когато го направите

@dog = Dog.new

Правиш две страхотни неща

1) Създайте променлива на екземпляр @dog за всеки обект, в който в момента е кодът ви

2) Създайте нов екземпляр на Dog (с всичките му методи и атрибути) и присвоете препратка към него към @dog

@dog е променлива, която случайно сочи към екземпляра на Dog („екземпляр на клас“ обикновено има същото значение като „обект“), който сте създали в този момент. Можете да зададете други променливи да сочат към същия екземпляр, а в Ruby обикновено това е начинът, по който предавате данни. Обектите съдържат променливи на екземпляри и тези променливи на екземпляри сочат към още обекти.

С помощта на оператора за присвояване (т.е. "=") можете да посочите променлива към всеки друг обект.

За да отговорите на вашите въпроси на свой ред:

Когато съм в класа на собственика и се обадя на @dog.popularity, как знае стойността на популярността за този случай?

Трябва да внимавате в Ruby (и OO езиците като цяло), за да правите разлика между клас и обект във вашите описания и въпроси. Ruby Предполагам, че имате предвид ред код в класа Owner и че възнамерявате той да работи с обект собственик. Бих предположил също, че @dog е атрибут, който сте добавили към Owner.

В този случай Ruby знае, защото @dog сочи към обекта Dog, който сте добавили към собственика. Всеки обект Dog има собствено копие на всички променливи на екземпляр на Dog. Все пак трябва да внимавате в Ruby, тъй като променливите сочат към обекти, че не просто предавате един и същ обект Dog на всички собственици (т.е. всички те ефективно споделят едно куче). Така че трябва да разберете кога създавате нови екземпляри (чрез new) и кога просто боравите със съществуващи препратки.

По време на изпълнение всички методи обработват ли се и тогава този екземпляр винаги е свързан със стойността в момента?

Не. По време на изпълнение основният Ruby ще изпълнява само заданията, които сте кодирали. Променливите на екземпляра може дори да не съществуват, докато не бъде изпълнен кодът, който ги присвоява. Ако използвате методи attr_reader и т.н., тогава променливите поне ще съществуват (но ще бъдат нула, освен ако не присвоите нещо по време на инициализация)

person Neil Slater    schedule 07.03.2013
comment
А, добре, благодаря ви много полезно. Така че в моя пример, когато кучето се инстанцира, му се присвояват стойностите му в инициализацията и всички негови методи. Методите обаче не се оценяват, докато приложението/програмата не го поиска с нещо като @dog.popularity ... дали стойността, която методът за популярност връща, след това се свързва с този екземпляр на куче? като ако го извикам отново, ще обработи ли отново метода/асоциираните методи или просто ще знае стойността от момента, в който е бил извикан преди това? - person BC00; 08.03.2013
comment
Всеки път се обработва, не помни. Между другото, обикновено не искате или имате нужда и от двата attr_accessor :foo и def foo - само едното или другото. Ако всичко, което искате, е да четете и пишете променливи на екземпляри, тогава attr_accessor е най-простият. Всичко по-сложно, искате да дефинирате метод. - person Neil Slater; 08.03.2013

Niel има страхотен отговор за това, просто искам да добавя нещо към него.

Броене на кучета :)

Имате нужда от променлива на класа, за да направите това..

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, който не съдържа никакви променливи на екземпляр. Не се различава по никакъв начин от препратка към нов екземпляр на "Куче".

По същия начин можете да кажете 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“, или като дефинираме метода сами.

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

няма магически начин променливата на екземпляра да бъде автоматично достъпна. Те винаги са достъпни чрез метод, който може да бъде заменен по-късно.

Надявам се, че има смисъл за вас

person Tilo    schedule 07.03.2013

Когато създавате обект, не е необходимо да използвате символа @. Променливата е обектът. Така че, ако имате няколко кучета, бихте направили:

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
person BlackHatSamurai    schedule 07.03.2013

И "тип", и "популярност" са методи на "кучешки" инстанции. Определението им е следното:

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 е просто пряк път за дефиниране на метод за получаване. Ако сами дефинирате метод, няма смисъл да използвате 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, който връща своята променлива на екземпляр; @dog.popularity извиква метода на популярност на @dog, който прави изчисления (както е дефинирано от вас) в движение и връща резултата. Тук няма магия!

person Simon Perepelitsa    schedule 07.03.2013