Reputation: 1955
I know PHP & Javascript, but I'm just starting to learn Ruby.
This is currently working :
class Animal
attr_accessor :name, :mammal
def initialize(name)
@name = name
end
end
class Fish < Animal
def initialize(name)
super(name)
@mammal = false
end
end
class Cow < Animal
def initialize(name)
super(name)
@mammal = true
end
end
animals = [
Fish.new('Moppy'),
Cow.new('Marguerite'),
]
animals.each do |animal|
puts "Is #{animal.name} a mammal ? #{animal.mammal}"
end
See the @mammal var in the sub classes ?
They are 'static' variables which do not depend of the instance, but of the class itself (a cow will always be a mammal, while a fish won't)
I was wondering if I was declaring the @mammal var at the right place. Instinctively, I would rather have done this
class Cow < Animal
@mammal = true
def initialize(name)
super(name)
end
end
but then it does not work... Could someone tell me if how you should handle this with Ruby ?
Thanks !
Upvotes: 1
Views: 543
Reputation: 110685
For each subclass, @mammal
is the same for all instances and is not intended to change. It therefore should be a constant or the value returned by a class method. I initially chose the former, but have been convinced by @tadman in the comments that a class method would be preferable, in particular a method with a name ending with a question mark. If interested, see my original answer in the edit history.
class Animal
attr_accessor :name
def initialize(name)
@name = name
end
end
class Fish < Animal
def initialize(name)
super
end
def self.mammal?
false
end
end
class Cow < Animal
def initialize(name)
super
end
def self.mammal?
true
end
end
i = Fish.new('Moppy')
"#{i.name} is a mammal: #{i.class.mammal?}"
#=> "Moppy is a mammal: false"
i = Cow.new('Marguerite')
"#{i.name} is a mammal: #{i.class.mammal?}"
#=> "Marguerite is a mammal: true"
Upvotes: 0
Reputation: 42292
@var
is an instance variable and @@var
is a class variable (both private).
@var
in class scope also works, but it's an instance variable on the class which is a bit different because (as all instance variables) it's going to be private to the class instance and thus not accessible to the class's instances, so you need to define a class-level accessor for it.
A big difference between class variables (@@
) and class instance variables (@
in class scope) is that the first is shared throughout the inheritance tree while the second is private to one specific class. But given your structure Mammal
being a class would make more sense than either.
See Ruby class instance variable vs. class variable for various examples.
Upvotes: 0
Reputation: 121000
If you want a class-wide variable, define an instance variable on class level.
class Animal
attr_accessor :name
def initialize(name)
@name = name
end
def self.mammal # class level
@mammal
end
end
class Fish < Animal
@mammal = false # class level
end
class Cow < Animal
@mammal = true # class level
end
[
Fish.new('Moppy'),
Cow.new('Marguerite'),
].each do |animal|
puts "Is #{animal.name} a mammal ? #{animal.class.mammal}"
end
Also, one might use so-called “class variables” @mammal
, but they behave weirdly under some circumstances, so instance variable on the class level would fit your needs better.
Upvotes: 1
Reputation: 8212
The simplest solution is:
class Fish < Animal
def mammal
false
end
end
class Cow < Animal
def mammal
true
end
end
Personally I'd be tempted to do something like:
class Animal
attr_accessor :name
def initialize(name)
@name = name
end
def mammal
false
end
end
class Mammal < Animal
def mammal
true
end
end
class Fish < Amimal
end
class Cow < Mammal
end
Upvotes: 3