题
在 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)中处理多态性/虚拟方法?
解决方案
编辑:为您更新的问题添加了更多代码
免责声明:我已经有一年左右没有使用 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
}
其他提示
到目前为止所有的答案对我来说看起来都很好。我想我只是提到整个继承的事情并不是完全必要的。暂时排除“睡眠”行为,我们可以使用鸭子类型实现整个期望的结果,并且根本不需要创建 Animal 基类。谷歌搜索“鸭子打字”应该会产生很多解释,所以在这里我们只说“如果它像鸭子一样走路并且像鸭子一样嘎嘎叫……”
“睡眠”行为可以通过使用 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
剩下的你都知道...
使用惯用的 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 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”)
鸭子类型的原则就是对象必须响应所调用的方法。所以类似的事情也可以达到目的:
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 解决方案的一个小变体,它动态创建基于类类型数组的不同类型的对象。没什么不同,只是更清晰一点。
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
我会这样写:
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 行 vs.原始 C# 示例中的 41 行(实际上,您可以使用集合初始值设定项减少 3 行)。不错!
有一个方法 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
小建议:如果你将多态性与 性病 它为您带来最有效的组合来重构您的代码
我希望它对你有帮助