just messing around with Ruby and was trying the following. I know i could do it with a case statement but what would be the proper way to achieve it?
What i want it to be able to type
Animals.new.interact
and get a proper reply depending on the Animal.
At the moment i get back a reply
You are a eagle
Not defined !
(repl):9:in `fly'
(repl):14:in `interact'
(repl):1:in `<main>'
but i would expect something along those lines (if the sample is a Penguin for example)
You are a penguin
you cannot fly !
Any ideas appreciated.
class Animals
SPECIES = []
attr_accessor :name
def initialize
@name = SPECIES.sample
end
def fly
raise "Not defined !"
end
def interact
puts "You are #{self}"
fly
end
def to_s
"a #{@name}"
end
end
class Penguin < Animals
def initialize
@name = "penguin"
SPECIES << @name
end
def fly
puts "you cannot fly !"
end
end
class Eagle < Animals
def initialize
@name = "eagle"
SPECIES << @name
end
def fly
puts "you fly high up the mountains !"
end
end
Penguin.new
Eagle.new
While you've got roughly the right idea with subclasses and overriding methods, the mistake here is that your design requires you to instantiate Animals
instances before they show up in the parent class, a very unusual way of defining these things. You're also modifying what should be a constant, which is bad form, and your initialize method merely populates the name, it doesn't select the appropriate type.
In Ruby when your initialize
method is called your object is locked in with a particular type that cannot be changed. If you want to emit animals of various types then you need to store those in an array already pre-constructed.
You can always request notification when your base class is used for inheritance by defining an inherited
method:
class Animals
def self.inherited(subclass)
(@types ||= [ ]) << subclass
end
end
This allows you to select one at random using a factory method :
class Animals
def self.random
@types and @types.sample.new
end
end
This simplifies the implementation of the other instances and does not require you to do anything special to get them to link up properly:
class Penguin < Animals
def initialize
@name = "penguin"
end
def fly
puts "you cannot fly !"
end
end
class Eagle < Animals
def initialize
@name = "eagle"
end
def fly
puts "you fly high up the mountains !"
end
end
So when you call the factory method you should get a random animal instance:
Animals.random
# => #<Eagle:0x007fc8f4109090 @name="eagle">
You definitely haven't understood classes. Why do you expect to change the behavior of an object by changing one instance variable?
When you do Animals.new
you request a new animal. And an Animal behaves exactly like you defined in the Animal class, in which you defined the fly
method as:
def fly
raise "Not defined !"
end
So there's absolutely no reason to expect you cannot fly !
.
You could do something like this, however:
SPECIES = []
class Animals
attr_accessor :name
def initialize
@name = SPECIES.sample
end
def fly
raise "Not defined !"
end
def interact
puts "You are #{self}"
fly
end
def to_s
"a #{@name}"
end
end
class Penguin < Animals
SPECIES << self
def initialize
@name = "penguin"
end
def fly
puts "you cannot fly !"
end
end
class Eagle < Animals
SPECIES << self
def initialize
@name = "eagle"
end
def fly
puts "you fly high up the mountains !"
end
end
SPECIES.sample.new.interact
Now the SPECIES
variable that I have made global stores the different Animal subclasses. When you call SPECIES.sample
you get a class like Penguin or Eagle. You instantiate that class, ie you get an instance of Penguin that behaves exactly like a penguin and will correctly no whether it can fly.
Firstly, on the topic of why it doesn't work.
Class#new
always returns the newly allocated instance, no matter what is your last statement, return statements etc. Hence you will get a non-specific Animal
with Animal.new
.
Secondly, on how to "properly" do it.
A class that knows about all of his children is an OO wtf moment. Instead create a separate service for picking an animal:
module AnimalPicker
ANIMALS = [Penguin, Eagle]
def self.random
ANIMALS.sample.new
end
end
AnimalPicker.random
Thirdly, on "but I really want to do it this way!!!!1111!" .
You can redefine Animal#new
:
class Animal
def self.new
[Penguin, Eagle].sample.new
end
end
Animal.new
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.