Как вы делаете полиморфизм в Ruby? - PullRequest
14 голосов
/ 26 сентября 2008

В 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?

Ответы [ 8 ]

22 голосов
/ 26 сентября 2008

Все ответы пока выглядят довольно хорошо для меня. Я думал, что просто упомяну, что все наследование не является абсолютно необходимым. Исключая поведение «сна» на мгновение, мы можем достичь желаемого результата, используя типизацию по уткам и исключая необходимость вообще создавать базовый класс Animal. Поиск в Google для «типизации утки» должен дать любое количество объяснений, так что здесь давайте просто скажем «если он ходит как утка и крякает как утка ...»

Поведение "сна" может быть обеспечено с помощью модуля 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

Ты знаешь все остальное ...

15 голосов
/ 26 сентября 2008

edit: добавлено больше кода для вашего обновленного вопроса

Disclaimer: я не использовал 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
}
10 голосов
/ 26 сентября 2008

Использование идиоматического 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 голосов
/ 26 сентября 2008

Опираясь на предыдущий ответ, это как вы могли бы это сделать?


Второй срез после уточнения:

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

1 голос
/ 26 июня 2010

Небольшой вариант решения 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
1 голос
/ 27 сентября 2008

Принцип утиной типизации заключается в том, что объект должен реагировать на вызванные методы. Так что что-то подобное тоже может помочь:

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
0 голосов
/ 10 мая 2016

Существует метод 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

  • Quicktip: если вы смешаете полиморфизм с STI , это даст вам наиболее эффективную комбинацию для рефакторинга вашего кода

Хотелось бы, чтобы это помогло вам

0 голосов
/ 26 сентября 2008

Вот как бы я это написал:

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 строк против 41 строки (фактически, вы можете сбрить 3 строки, используя инициализатор коллекции) из исходного примера C #. Неплохо!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...