Rubyでどのようにポリモーフィズムを行いますか?
-
02-07-2019 - |
質問
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#はその冗長型構文でしばしばm笑されるため、Rubyなどのカモ型言語で多態性/仮想メソッドをどのように処理しますか?
解決
編集:更新された質問にコードを追加しました
免責事項:私は1年かそこらで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基本クラスを作成する必要をまったく省いて、望ましい結果全体を達成できます。 「ダックタイピング」のグーグル説明はいくらでもあるはずなので、ここでは「アヒルのように歩き、アヒルのように鳴く...」と言ってみましょう。
「スリープ」 Array、Hash、およびEnumerableを含むその他のRuby組み込みクラスなどのmixinモジュールを使用することで動作を提供できます。私はそれが必ずしもより良いことを提案しているのではなく、それを行うための異なる、おそらくより慣用的な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
前の答えに基づいて、これはどのように行うことができますか?
説明後の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
}
(これはそのままにしますが、&quot; self.class.name&quot;が&quot; .to_s&quot;に勝ちます)
ダックタイピングの原則は、オブジェクトが呼び出されたメソッドに応答する必要があるということだけです。そのため、そのようなことでもうまくいく可能性があります:
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
他のソリューションと本当に違いはありませんが、これは私が好むスタイルです。
元のC#の例の12行と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
-
クイックヒント:ポリモーフィズムと STI を混在させると、コードをリファクタリングする最も効率的なコンボ
お役に立てばと思います