14

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

显然,输出是(稍微解释一下):

  • 狗在睡觉
  • 猫在睡觉

由于 C# 经常因其冗长的类型语法而受到嘲笑,您如何处理诸如 Ruby 之类的鸭子类型语言中的多态性/虚拟方法?

4

8 回答 8

22

到目前为止,所有答案对我来说都很好。我想我只是提到整个继承的事情并不是完全必要的。暂时排除“睡眠”行为,我们可以使用鸭子类型来实现整个期望的结果,并且根本不需要创建一个 Animal 基类。谷歌搜索“duck-typing”应该会产生任何数量的解释,所以在这里我们只说“如果它像鸭子一样走路并且像鸭子一样嘎嘎......”

“睡眠”行为可以通过使用 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

你知道剩下的...

于 2008-09-26T19:34:48.190 回答
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
}
于 2008-09-26T03:49:52.310 回答
10

使用惯用的 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
于 2008-09-26T05:31:50.253 回答
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”)

于 2008-09-26T04:02:06.290 回答
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
于 2008-09-27T18:21:31.570 回答
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
于 2010-06-25T21:00:54.783 回答
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 行与原始 C# 示例中的 41 行(实际上,您可以使用集合初始化程序减少 3 行)。不错!

于 2008-09-26T07:23:05.043 回答
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

  • 如果您使用RubyOnRails becomes的方法已经实现:becomes

  • 快速提示:如果您将多态性与STI混合使用,它将为您带来重构代码的最有效组合

我希望它对你有帮助

于 2016-05-10T11:44:15.807 回答