2008-09-26 76 views
14

在C#中,我可以這樣做:你如何在Ruby中做多態?

class Program 
{ 
    static void Main(string[] args) 
    { 
     List<Animal> animals = new List<Animal>(); 

     animals.Add(new Dog()); 
     animals.Add(new Cat()); 

     foreach (Animal a in animals) 
     { 
      Console.WriteLine(a.MakeNoise()); 
      a.Sleep(); 
     } 
    } 
} 

public class Animal 
{ 
    public virtual string MakeNoise() { return String.Empty; } 
    public void Sleep() 
    { 
     Console.Writeline(this.GetType().ToString() + " is sleeping."); 
    } 
} 

public class Dog : Animal 
{ 
    public override string MakeNoise() 
    { 
     return "Woof!"; 
    } 
} 

public class Cat : Animal 
{ 
    public override string MakeNoise() 
    { 
     return "Meow!"; 
    } 
} 

顯然,輸出(略轉述):

  • 狗睡覺
  • 貓睡覺

由於C#經常因其冗長的類型語法而被嘲笑,你如何處理鴨子類型語言(比如Ruby)中的多態/虛擬方法?

+0

你提到了「真正的」多態性。什麼是你的定義爲「真正的」我讓你我的:http://en.wikipedia.org/wiki/Polymorphism_in_object-oriented_programming – OscarRyz 2008-09-26 04:02:58

回答

15

編輯:添加更多的代碼爲您更新的問題

免責聲明:我沒有在一年左右的時間用於Ruby和沒有它這個機器上安裝,所以語法可能是完全錯誤的。但是這些概念是正確的。


完全相同的方式,用類和覆蓋方法:在以前的答案

class Animal 
    def MakeNoise 
     return "" 
    end 
    def Sleep 
     print self.class.name + " is sleeping.\n" 
    end 
end 

class Dog < Animal 
    def MakeNoise 
     return "Woof!" 
    end 
end 

class Cat < Animal 
    def MakeNoise 
     return "Meow!" 
    end 
end 

animals = [Dog.new, Cat.new] 
animals.each {|a| 
    print a.MakeNoise + "\n" 
    a.Sleep 
} 
+0

對不起,但什麼版本的Ruby是「類貓:動物」?繼承是不是「<」? – 2008-09-26 04:04:36

+4

布倫特:這將是「由Python用戶記住的Ruby」 – 2008-09-26 04:06:57

+1

那麼,將class.name輸出動物還是狗?我真的很好奇。 – FlySwat 2008-09-26 04:07:54

2

大廈,這是你會如何做呢?澄清後


第二切口:

class Animal 
    def MakeNoise 
     raise NotImplementedError # I don't remember the exact error class 
    end 
    def Sleep 
     puts self.class.to_s + " is sleeping." 
    end 
end 

class Dog < Animal 
    def MakeNoise 
     return "Woof!" 
    end 
end 

class Cat < Animal 
    def MakeNoise 
     return "Meow!" 
    end 
end 

animals = [Dog.new, Cat.new] 
animals.each {|a| 
    puts a.MakeNoise 
    a.Sleep 
} 

(我會就是離開這個,而是 「self.class.name」 戰勝 「.to_s」)

10

使用慣用的紅寶石

class Animal 
    def sleep 
    puts "#{self.class} is sleeping" 
    end 
end 

class Dog < Animal 
    def make_noise 
    "Woof!" 
    end 
end 

class Cat < Animal 
    def make_noise 
    "Meow!" 
    end 
end 

[Dog, Cat].each do |clazz| 
    animal = clazz.new 
    puts animal.make_noise 
    animal.sleep 
end 
0

這是我會怎麼寫呢:

class Animal 
    def make_noise; '' end 
    def sleep; puts "#{self.class.name} is sleeping." end 
end 

class Dog < Animal; def make_noise; 'Woof!' end end 
class Cat < Animal; def make_noise; 'Meow!' end end 

[Dog.new, Cat.new].each do |animal| 
    puts animal.make_noise 
    animal.sleep 
end 

這不是真的不同於其他解決方案,但這是我更喜歡的風格。

這是12行與41行(實際上,您可以通過使用集合初始值設定項來刪除3行)從原始C#示例。不錯!

22

到目前爲止所有的答案看起來都不錯。我想我只是提到整個繼承的事情並不是完全必要的。排除「睡眠」行爲一段時間後,我們可以使用鴨子打字並且完全不需要創建動物基類來實現整個預期結果。谷歌搜索「鴨子打字」應該產生任何數量的解釋,所以在這裏,讓我們只是說「如果它走路像一隻鴨子和像鴨子一樣嘎嘎叫......」

「睡眠」行爲可以通過使用mixin模塊,如Array,Hash和其他Ruby內置類包括Enumerable。我並不是說這一定更好,只是一種不同的,也許更常用的Ruby方式。

module Animal 
    def sleep 
    puts self.class.name + " sleeps" 
    end 
end 

class Dog 
    include Animal 
    def make_noise 
    puts "Woof" 
    end 
end 

class Cat 
    include Animal 
    def make_noise 
    puts "Meow" 
    end 
end 

你知道其餘的...

1

鴨子打字的原理就是對象必須響應被調用的方法。所以,這樣的事情可以做的伎倆太:

module Sleeping 
    def sleep; puts "#{self} sleeps" 
end 

dog = "Dog" 
dog.extend Sleeping 
class << dog 
    def make_noise; puts "Woof!" end 
end 

class Cat 
    include Sleeping 
    def to_s; "Cat" end 
    def make_noise; puts "Meow!" end 
end 

[dog, Cat.new].each do |a| 
    a.sleep 
    a.make_noise 
end 
1

manveru解決方案的一點點變體,其動態創建不同類型的基於類類型的數組對象。沒有什麼不同,只是更清楚一點。

Species = [Dog, Cat] 

Species.each do |specie| 
    animal = specie.new # this will create a different specie on each call of new 
    print animal.MakeNoise + "\n" 
    animal.Sleep 
end 
0

有一個方法becomes它實現多態性(通過從給定類爲新的應對所有實例變量)

class Animal 
    attr_reader :name 

    def initialize(name = nil) 
    @name = name 
    end 

    def make_noise 
    '' 
    end 

    def becomes(klass) 
    became = klass.new 
    became.instance_variables.each do |instance_variable| 
     value = self.instance_variable_get(instance_variable) 
     became.instance_variable_set(instance_variable, value) 
    end 

    became 
    end 
end 

class Dog < Animal 
    def make_noise 
    'Woof' 
    end 
end 

class Cat < Animal 
    def make_noise 
    'Meow' 
    end 
end 

animals = [Dog.new('Spot'), Cat.new('Tom')] 

animals.each do |animal| 
    new_animal = animal.becomes(Cat) 
    puts "#{animal.class} with name #{animal.name} becomes #{new_animal.class}" 
    puts "and makes a noise: #{new_animal.make_noise}" 
    puts '----' 
end 

和結果是:

Dog with name Spot becomes Cat 
and makes a noise: Meow 
---- 
Cat with name Tom becomes Cat 
and makes a noise: Meow 
---- 
  • 一多態性可能有助於避免if聲明(antiifcampaign.com

  • 如果使用RubyOnRailsbecomes方法已經實現:becomes

  • Quicktip:如果你有STI混合多態性它帶給你的最有效的組合重構你的代碼

我希望它對你有所幫助