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

これまでのすべての答えは、私にはかなり良さそうです。継承全体が完全に必要なわけではないことを述べておきたいと思いました。「スリープ」動作を少し除いて、ダックタイピングを使用し、アニマル基本クラスを作成する必要をまったく省略して、目的の結果全体を達成できます。「アヒルのタイピング」をグーグルで検索すると、さまざまな説明が得られるはずなので、ここでは、「アヒルのように歩き、アヒルのように鳴く場合...」とだけ言っておきましょう。

「スリープ」動作は、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

あなたは残りを知っています...

于 2008-09-26T19:34:48.190 に答える
15

編集:更新された質問のコードを追加しました

免責事項: Ruby を 1 年ほど使用しておらず、このマシンにインストールしていないため、構文が完全に間違っている可能性があります。しかし、概念は正しいです。


クラスとオーバーライドされたメソッドを使用して、まったく同じ方法で:

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

前の回答に基づいて、これはあなたがそれを行う方法ですか?


明確化後の 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

クラスタイプの配列に基づいてさまざまな種類のオブジェクトを動的に作成する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 に答える
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 に答える
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

他のソリューションとそれほど違いはありませが、これが私が好むスタイルです。

これは、元のC#の例の41行(実際には、コレクション初期化子を使用して3行を削減できます)に対して12行です。悪くない!

于 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 に答える