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
これは例外を発生させます。
また、モジュールはインスタンス化できませんが、継承することもできません。新しいメソッドをプライベートにすることもできません。ポインタはありますか?
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
抽象メソッドのリストを使用し、抽象クラスのインスタンスから呼び出されると、NotImplementedError
例外が発生します。
遅くここでチャイムするために、誰かが抽象クラスをインスタンス化するのを止める理由はないと思います 特に、その場でメソッドを追加できるので 。
Rubyなどのダックタイピング言語は、実行時にメソッドの有無または動作を使用して、メソッドを呼び出す必要があるかどうかを判断します。したがって、抽象メソッドに適用される質問は意味があります
def get_db_name
raise 'this method should be overriden and return the db name'
end
そして、それは物語の終わりについてであるべきです。 Javaで抽象クラスを使用する唯一の理由は、特定のメソッドが「埋められる」一方で、他のメソッドは抽象クラスで動作することを主張することです。ダックタイピング言語では、クラス/タイプではなくメソッドであるため、心配はそのレベルに移動する必要があります。
あなたの質問では、基本的にJavaからabstract
キーワードを再作成しようとしています。これはRubyでJavaを実行するためのコード臭です。
これを試して:
class A
def initialize
raise 'Doh! You are trying to instantiate an abstract class!'
end
end
class B < A
def initialize
end
end
class A
private_class_method :new
end
class B < A
public_class_method :new
end
私の2¢:シンプルで軽量なDSLミックスインを選ぶ:
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
もちろん、この場合、基本クラスを初期化するための別のエラーを追加するのは簡単です。
Railsの世界の誰にとっても、ActiveRecordモデルを抽象クラスとして実装するには、モデルファイルの次の宣言を使用します。
self.abstract_class = true
Rubyのプログラミングの過去6年半の間、私は必要抽象クラスを一度もしていません。
抽象クラスが必要だと思っているなら、Rubyとしてではなく、それらを提供/要求する言語で考えすぎている。
他の人が示唆しているように、ミックスインはインターフェースであると想定されるもの(Java定義))により適切であり、設計を再考することは、 C++などの他の言語。
更新2019:Ruby使用16年半で抽象クラスを必要としませんでした。私の応答にコメントするすべての人々が言っているすべては、実際に学習することによって対処されますRubyそして、モジュールのような適切なツールを使用します(共通の実装も提供します)。失敗したベース実装を持つクラスを作成したチームの人々がいます(抽象クラスのような)が、これらのほとんどはNoMethodError
は生産中のAbstractClassError
とまったく同じ結果を生成するため、コーディングの無駄です。
インスタンス化できないクラスを使用したい場合は、A.newメソッドで、エラーをスローする前にself == Aかどうかを確認してください。
しかし、実際には、モジュールはここで必要なもののように見えます。たとえば、Enumerableは他の言語の抽象クラスのようなものです。技術的にはサブクラス化できませんが、include SomeModule
はほぼ同じ目標を達成します。これがうまくいかない理由はありますか?
抽象クラスで提供しようとしているのはどのような目的ですか? Rubyでそれを行うより良い方法はおそらくありますが、詳細は提供しませんでした。
私の指針はこれです。継承ではなく mixin を使用します。
この小さなabstract_type
gemもあり、控えめな方法で抽象クラスとモジュールを宣言できます。
例( 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
別の答え:
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>
他のポスターが言ったように、おそらく抽象クラスではなく、ミックスインを使用する必要があります。
このようにしたので、子クラスで新しいものを再定義して、非抽象クラスで新しいものを見つけます。 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
個人的には、抽象クラスのメソッドでNotImplementedErrorを発生させます。しかし、あなたが言及した理由のために、「新しい」方法から除外したいかもしれません。
アプローチに問題はありません。もちろん、すべてのサブクラスがinitializeをオーバーライドする限り、initializeでエラーを発生させることは問題ないようです。しかし、あなたはself.newをそのように定義したくありません。これが私がすることです。
class A
class AbstractClassInstiationError < RuntimeError; end
def initialize
raise AbstractClassInstiationError, "Cannot instantiate this class directly, etc..."
end
end
別のアプローチでは、すべての機能をモジュールに配置します。これは、先ほど述べたように、決して開始することはできません。次に、別のクラスから継承するのではなく、クラスにモジュールを含めます。ただし、これはスーパーのようなものを壊します。
そのため、どのように構造化するかによって異なります。モジュールは、「他のクラスが使用するように設計されたものをどのように書くのか」という問題を解決するためのよりクリーンなソリューションのように見えますが