2008-09-26 29 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混合多态性它带给你的最有效的组合重构你的代码

我希望它对你有所帮助