131

Rubyには抽象クラスという概念がないことは知っています。しかし、それを実装する必要がある場合、どうすればよいのでしょうか? 私はこのようなことを試しました:

class A
  def self.new
    raise 'Doh! You are trying to write Java in Ruby!'
  end
end

class B < A
  ...
  ...
end

しかし、B をインスタンス化しようとするとA.new、例外が発生する呼び出しが内部的に行われます。

また、モジュールはインスタンス化できませんが、継承もできません。新しいメソッドをプライベートにしても機能しません。

誰にも指針がありますか?

4

17 に答える 17

123

ここで遅くまでチャイムを鳴らすために、誰かが抽象クラスをインスタンス化するのを止める理由はないと思います。特に、その場でメソッドを追加できるからです。

Rubyのようなダックタイピング言語は、実行時にメソッドの有無または動作を使用して、メソッドを呼び出す必要があるかどうかを判断します。したがって、あなたの質問は、抽象的なメソッドに適用されるので、理にかなっています

def get_db_name
   raise 'this method should be overriden and return the db name'
end

これで話は終わりです。Javaで抽象クラスを使用する唯一の理由は、特定のメソッドが「埋められ」、他のメソッドが抽象クラスで動作することを主張するためです。ダックタイピング言語では、クラス/タイプではなくメソッドに焦点が当てられているため、心配事をそのレベルに移動する必要があります。

あなたの質問では、基本的にabstractJavaからキーワードを再作成しようとしています。これは、RubyでJavaを実行するためのコードの臭いです。

于 2010-03-23T19:05:31.467 に答える
61

Ruby で抽象クラスを使用するのは好きではありません (ほとんどの場合、もっと良い方法があります)。ただし、それが状況に最適な手法であると本当に思う場合は、次のスニペットを使用して、どのメソッドが抽象的であるかについてより宣言的にすることができます。

module Abstract
  def abstract_methods(*args)
    args.each do |name|
      class_eval(<<-END, __FILE__, __LINE__)
        def #{name}(*args)
          raise NotImplementedError.new("You must implement #{name}.")
        end
      END
      # important that this END is capitalized, since it marks the end of <<-END
    end
  end
end

require 'rubygems'
require 'rspec'

describe "abstract methods" do
  before(:each) do
    @klass = Class.new do
      extend Abstract

      abstract_methods :foo, :bar
    end
  end

  it "raises NoMethodError" do
    proc {
      @klass.new.foo
    }.should raise_error(NoMethodError)
  end

  it "can be overridden" do
    subclass = Class.new(@klass) do
      def foo
        :overridden
      end
    end

    subclass.new.foo.should == :overridden
  end
end

基本的にabstract_methodsは、abstract であるメソッドのリストを使用して呼び出すだけで、abstract クラスのインスタンスによってそれらが呼び出されると、NotImplementedError例外が発生します。

于 2009-02-04T17:48:27.410 に答える
46

これを試して:

class A
  def initialize
    raise 'Doh! You are trying to instantiate an abstract class!'
  end
end

class B < A
  def initialize
  end
end
于 2009-02-04T18:06:50.073 に答える
20

Rails の世界では、ActiveRecord モデルを抽象クラスとして実装するには、モデル ファイルで次の宣言を行います。

self.abstract_class = true
于 2015-05-26T18:19:36.867 に答える
13

私の 2¢: シンプルで軽量な DSL mixin を選択します。

module Abstract
  extend ActiveSupport::Concern

  included do

    # Interface for declaratively indicating that one or more methods are to be
    # treated as abstract methods, only to be implemented in child classes.
    #
    # Arguments:
    # - methods (Symbol or Array) list of method names to be treated as
    #   abstract base methods
    #
    def self.abstract_methods(*methods)
      methods.each do |method_name|

        define_method method_name do
          raise NotImplementedError, 'This is an abstract base method. Implement in your subclass.'
        end

      end
    end

  end

end

# Usage:
class AbstractBaseWidget
  include Abstract
  abstract_methods :widgetify
end

class SpecialWidget < AbstractBaseWidget
end

SpecialWidget.new.widgetify # <= raises NotImplementedError

もちろん、この場合、基本クラスを初期化するための別のエラーを追加することは簡単です。

于 2012-05-10T06:15:35.203 に答える
7

3 つの ruby​​gem を試すことができます:
インターフェイス
抽象
単純 抽象

于 2011-08-12T10:32:22.280 に答える
6

抽象クラスで提供しようとしている目的は何ですか? おそらくRubyで行うより良い方法があるでしょうが、あなたは詳細を教えてくれませんでした.

私のポインタはこれです。継承ではなくmixinを使用します。

于 2009-02-04T20:19:45.610 に答える
4

別の答え:

module Abstract
  def self.append_features(klass)
    # access an object's copy of its class's methods & such
    metaclass = lambda { |obj| class << obj; self ; end }

    metaclass[klass].instance_eval do
      old_new = instance_method(:new)
      undef_method :new

      define_method(:inherited) do |subklass|
        metaclass[subklass].instance_eval do
          define_method(:new, old_new)
        end
      end
    end
  end
end

これは、実装されていないメソッドを報告する通常の方法に依存し#method_missingますが、抽象クラスが実装されないようにします (初期化メソッドがある場合でも)。

class A
  include Abstract
end
class B < A
end

B.new #=> #<B:0x24ea0>
A.new # raises #<NoMethodError: undefined method `new' for A:Class>

他のポスターが言ったように、おそらく抽象クラスではなく、ミックスインを使用する必要があります。

于 2009-02-04T20:23:05.727 に答える
4

NotImplementedError個人的には、抽象クラスのメソッドを上げています。newただし、前述の理由により、メソッドから除外したい場合があります。

于 2009-02-04T17:39:40.363 に答える
3

私はこのようにしたので、子クラスの new を再定義して、非抽象クラスの new を見つけます。ruby で抽象クラスを使用することにまだ実用性が見られません。

puts 'test inheritance'
module Abstract
  def new
    throw 'abstract!'
  end
  def inherited(child)
    @abstract = true
    puts 'inherited'
    non_abstract_parent = self.superclass;
    while non_abstract_parent.instance_eval {@abstract}
      non_abstract_parent = non_abstract_parent.superclass
    end
    puts "Non abstract superclass is #{non_abstract_parent}"
    (class << child;self;end).instance_eval do
      define_method :new, non_abstract_parent.method('new')
      # # Or this can be done in this style:
      # define_method :new do |*args,&block|
        # non_abstract_parent.method('new').unbind.bind(self).call(*args,&block)
      # end
    end
  end
end

class AbstractParent
  extend Abstract
  def initialize
    puts 'parent initializer'
  end
end

class Child < AbstractParent
  def initialize
    puts 'child initializer'
    super
  end
end

# AbstractParent.new
puts Child.new

class AbstractChild < AbstractParent
  extend Abstract
end

class Child2 < AbstractChild

end
puts Child2.new
于 2010-10-29T09:30:32.217 に答える
3

この小さなabstract_type宝石もあり、控えめな方法で抽象クラスとモジュールを宣言できます。

例 ( README.mdファイルから):

class Foo
  include AbstractType

  # Declare abstract instance method
  abstract_method :bar

  # Declare abstract singleton method
  abstract_singleton_method :baz
end

Foo.new  # raises NotImplementedError: Foo is an abstract type
Foo.baz  # raises NotImplementedError: Foo.baz is not implemented

# Subclassing to allow instantiation
class Baz < Foo; end

object = Baz.new
object.bar  # raises NotImplementedError: Baz#bar is not implemented
于 2014-09-08T05:15:12.670 に答える
1

2行のgem:https ://rubygems.org/gems/abstract

于 2011-07-05T12:01:11.097 に答える
1

あなたのアプローチには何も問題はありません。initializeもちろん、すべてのサブクラスがオーバーライドする限り、初期化子でエラーを発生させることは問題ないようです。しかし、あなたはそのように定義したくありませんself.new。これが私がすることです:

class A
  class AbstractClassInstiationError < RuntimeError; end
  def initialize
    raise AbstractClassInstiationError, "Cannot instantiate this class directly, etc..."
  end
end

別のアプローチでは、すべての機能をモジュールに配置します。これは、前述のようにインスタンス化することはできません。次に、別のクラスから継承するのではなく、モジュールをクラスに含めます。ただし、これはsuper.

モジュールは、「他のクラスが使用するように設計されたものをどのように作成するか」という問題を解決するためのよりクリーンなソリューションのように見えますが、それをどのように構築したいかによって異なります。

于 2009-02-04T17:48:33.713 に答える
1

これはRubyのようには感じられませんが、これを行うことができます:

class A
  def initialize
    raise 'abstract class' if self.instance_of?(A)

    puts 'initialized'
  end
end

class B < A
end

結果:

>> A.new
  (rib):2:in `main'
  (rib):2:in `new'
  (rib):3:in `initialize'
RuntimeError: abstract class
>> B.new
initialized
=> #<B:0x00007f80620d8358>
>>
于 2020-06-05T04:25:07.490 に答える