Абстрактный метод в Ruby - PullRequest
42 голосов
/ 22 июля 2011

Как заставить подкласс реализовать метод в Ruby. Кажется, в Ruby нет абстрактного ключевого слова, такого подхода я бы использовал в Java. Есть ли другой более Ruby-подобный способ применения абстрактного?

Ответы [ 6 ]

45 голосов
/ 22 июля 2011

Абстрактные методы должны быть менее полезными в Ruby, потому что они не строго статически типизированы.

Тем не менее, это то, что я делаю:

class AbstractThing
  MESS = "SYSTEM ERROR: method missing"

  def method_one; raise MESS; end
  def method_two; raise MESS; end
end

class ConcreteThing < AbstractThing
  def method_one
     puts "hi"
  end
end

a = ConcreteThing.new
a.method_two # -> raises error.

Однако это редко кажется необходимым.

15 голосов
/ 09 декабря 2014

Мне нравится ответ от pvandenberk, но я бы улучшил его следующим образом:

module Canine      # in Ruby, abstract classes are known as modules
  def bark
    fail NotImplementedError, "A canine class must be able to #bark!"
  end
end

Теперь, если вы сделаете класс, принадлежащий Canine "абстрактному классу" (т. Е. Классу, который имеет модуль Canine в своих предках), он будет жаловаться, если обнаружится, что метод #bark не реализован:

class Dog
  include Canine   # make dog belong to Canine "abstract class"
end

Dog.new.bark       # complains about #bark not being implemented

class Dog
  def bark; "Bow wow!" end
end

# Now it's OK:
Dog.new.bark #=> "Bow wow!"

Обратите внимание, что поскольку классы Ruby не являются статическими, но всегда открыты для изменений, сам класс Dog не может обеспечить существование методов #bark, поскольку он не знает, когда он должен быть завершен. Если вы, как программист, делаете это, то вы можете проверить это в это время.

7 голосов
/ 01 февраля 2013

Мой предпочтительный подход аналогичен, но немного отличается ... Я предпочитаю его следующим образом, потому что он делает код самодокументированным, давая вам нечто очень похожее на Smalltalk:

class AbstractThing
  def method_one; raise "SubclassResponsibility" ; end
  def method_two; raise "SubclassResponsibility" ; end
  def non_abstract_method; method_one || method_two ; end
end

Некоторые люди будут жаловатьсячто это менее СУХОЙ, и настаивайте на создании подкласса исключения и / или поместите строку "SubclassResponsibility" в константу, но ИМХО вы можете высушить вещи до точки раздражения, и это обычно не очень хорошовещь .Например, если у вас есть несколько абстрактных классов в базе кода, где бы вы определили строковую константу MESS?!?

4 голосов
/ 09 декабря 2014

Мне нравится использование такого драгоценного камня, как abstract_method , который дает абстрактные методы синтаксиса стиля рельсов dsl:

class AbstractClass
  abstract_method :foo
end

class AbstractModule
  abstract_method :bar
end

class ConcreteClass < AbstractClass
  def foo
    42
  end
end
1 голос
/ 20 марта 2015

Этот код не позволит вам загрузить класс, если методы «foo», «bar» и «mate» не определены в унаследованном классе.

Он не учитывает классы, определяемые во многих файлах, но давайте будем честными, многие ли из нас определяют методы классов во многих файлах?Я имею в виду, если ты не считаешь дополнения.(к чему это относится)

def self.abstract(*methods_array)
  @@must_abstract ||= []
  @@must_abstract = Array(methods_array)
end
def self.inherited(child)
   trace = TracePoint.new(:end) do |tp|
      if tp.self == child #modules also trace end we only care about the class end   
        trace.disable
        missing = ( Array(@@must_abstract) - child.instance_methods(false) )
        raise NotImplementedError, "#{child} must implement the following method(s) #{missing}" if missing.present?
      end
  end 
  trace.enable
end

abstract :foo
abstract :bar, :mate
0 голосов
/ 07 сентября 2017

Если вы хотите, чтобы при создании экземпляра класса выдается сообщение об ошибке, вы можете сделать следующее

class AbstractClass
  def self.new(args)
    instance = allocate # make memory space for a new object
    instance.send(:default_initialize, args)
    instance.send(:initialize, args)
    instance
  end

  #This is called whenever object created, regardless of whether 'initialize' is overridden
  def default_initialize(args)
    self.abstract_method #This will raise error upon object creation
  end
  private :default_initialize

  def initialize(args)
   # This can be overridden by new class
  end
end


class NewClass < AbstractClass
end

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