我们如何在Ruby中进行多态?

时间:2020-03-06 14:45:53  来源:igfitidea点击:

在C#中,我可以这样做:

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!";
    }
}

显然,输出为(略述):

  • 狗在睡觉
  • 猫在睡觉

由于Cis经常因其冗长的类型语法而嘲笑,我们如何使用鸭子式语言(如Ruby)处理多态/虚拟方法?

解决方案

编辑:为我们更新的问题添加了更多代码

免责声明:我已经有大约一年没有使用Ruby了,也没有在该机器上安装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
}

在上一个答案的基础上,这是我们可能的做法吗?

澄清后的第二次切割:

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")

使用惯用的Ruby

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

这就是我的写法:

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

它与其他解决方案并没有真正的不同,但这是我希望的样式。

与原始Cexample相比,这是12行与41行(实际上,我们可以通过使用集合初始化程序来减少3行)。不错!

到目前为止,所有答案对我来说都不错。我以为我只想提到整个继承并不是完全必要的。除了暂时的"睡眠"行为外,我们可以通过鸭式输入并完全不需要创建Animal基类来实现整体所需的结果。谷歌搜索"鸭型"应该给出许多解释,因此在这里我们只说"如果它像鸭子一样走路,而像鸭子一样嘎嘎..."

可以通过使用混合模块(例如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

你知道其余的...

鸭子类型化的原则只是对象必须响应被调用的方法。所以类似的事情也可以解决这个问题:

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

manveru解决方案的一个小变体,它基于Class类型数组动态创建不同种类的对象。没什么不同,只是更清楚一点。

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