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와 같은 오리형 언어는 런타임에 메서드의 존재/존재 또는 동작을 사용하여 메서드를 호출할지 여부를 결정합니다.따라서 추상적인 방법에 적용되는 당신의 질문은 타당합니다.
def get_db_name
raise 'this method should be overriden and return the db name'
end
그리고 그것이 이야기의 끝에 관한 것이어야 합니다.Java에서 추상 클래스를 사용하는 유일한 이유는 특정 메서드가 "채움"되는 반면 다른 메서드는 추상 클래스에서 동작을 수행한다는 것을 주장하기 위한 것입니다.오리형 언어는 수업/유형이 아닌 방법에 중점을 두기 때문에 고민을 그 수준으로 옮겨야 합니다.
질문에 따르면, 당신은 기본적으로 다음과 같은 것을 재현하려고 합니다.abstract
루비에서 Java를 수행하기 위한 코드 냄새인 Java의 키워드입니다.
저는 루비에서 추상 수업을 사용하는 것을 좋아하지 않습니다(거의 항상 더 좋은 방법이 있습니다).상황에 가장 적합한 기술이라고 생각한다면 다음 스니펫을 사용하여 추상적인 방법을 보다 명확하게 설명할 수 있습니다.
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
예외가 발생합니다.
사용해 보십시오.
class A
def initialize
raise 'Doh! You are trying to instantiate an abstract class!'
end
end
class B < A
def initialize
end
end
Rails 세계의 모든 사용자가 ActiveRecord 모델을 추상 클래스로 구현하는 것은 모델 파일의 다음 선언으로 수행됩니다.
self.abstract_class = true
class A
private_class_method :new
end
class B < A
public_class_method :new
end
루비를 프로그래밍한 지난 6년 반 동안 추상 수업이 한 번도 필요하지 않았습니다.
만약 여러분이 추상적인 수업이 필요하다고 생각한다면, 여러분은 그것들을 제공하거나 필요로 하는 언어로 너무 많이 생각하고 있는 것이지 루비가 아닙니다.
다른 사람들이 제안했듯이, 믹스인은 인터페이스가 되어야 하는 것들(자바가 정의하는 것처럼)에 더 적합하며, C++와 같은 다른 언어의 추상 클래스가 "필요한" 것들에 더 적합합니다.
2022년 기준 업데이트: 20년 동안 루비에서 추상 수업이 필요하지 않았습니다.제 답변에 대해 언급하는 모든 사람들이 말하는 모든 것은 Ruby를 실제로 배우고 모듈과 같은 적절한 도구를 사용함으로써 해결됩니다(일반적인 구현까지 제공).에는 (처럼) , 은 대부분 입니다.NoMethodError
정확하게 동일한 결과를 생성할 것입니다.AbstractClassError
생산 중인
나의 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
물론 이 경우 기본 클래스를 초기화하기 위해 다른 오류를 추가하는 것은 사소한 일입니다.
추상적인 수업으로 어떤 목적을 수행하려고 합니까?루비에서 더 좋은 방법이 있을 수 있지만, 당신은 어떤 세부사항도 제시하지 않았습니다.
제 포인터는 이것입니다. 상속이 아닌 혼합을 사용하십시오.
당신은 3개의 루비젬을 시도할 수 있습니다.
추상적인 것
저는 개인적으로 키웁니다.NotImplementedError
추상 클래스의 방법으로.하지만 당신은 그것을 제외하는 것이 좋을 수도 있습니다.new
방법, 당신이 언급한 이유로.
또 다른 대답:
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>
다른 포스터들이 말했듯이, 여러분은 아마도 추상적인 수업보다는 믹스인을 사용해야 할 것입니다.
메소드에서 class와 , 당신의 A.new 에서, uninstantable class를 확인하세요.self == A
오류를 범하기 전에
하지만 실제로 모듈은 여러분이 여기서 원하는 것에 더 가깝습니다.예를 들어 Enumerable은 다른 언어에서 추상 클래스일 수 있는 종류입니다.기술적으로는 하위 분류를 할 수 없지만 전화를 걸면include SomeModule
거의 동일한 목표를 달성합니다.이것이 당신에게 효과가 없을 이유가 있나요?
저는 이런 식으로 했습니다. 그래서 추상적이지 않은 수업에 대한 새로운 것을 찾기 위해 아동 수업에 대한 새로운 것을 재정의합니다.저는 아직도 루비에서 추상 수업을 사용하는 것이 실용적이라고 생각하지 않습니다.
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
이렇게 작은 것도 있습니다.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
두 줄 보석: https://rubygems.org/gems/abstract
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>
>>
당신의 접근 방식에는 아무런 문제가 없습니다.모든 하위 클래스가 재정의되는 한 이니셜라이저에서 오류를 발생시키는 것은 문제가 없는 것 같습니다.initialize
물론이야.하지만 당신은 정의하기를 원하지 않습니다.self.new
이런 거죠.제가 할 일은 다음과 같습니다.
class A
class AbstractClassInstiationError < RuntimeError; end
def initialize
raise AbstractClassInstiationError, "Cannot instantiate this class directly, etc..."
end
end
또 다른 접근 방식은 모듈에 모든 기능을 넣는 것입니다. 말씀하신 것처럼 이 기능은 결코 인스턴스화될 수 없습니다.그런 다음 다른 클래스에서 상속하는 대신 모듈을 클래스에 포함합니다.하지만, 이것은 다음과 같은 것들을 깨뜨릴 것입니다.super
.
모듈은 "다른 클래스에서 사용할 수 있도록 설계된 자료를 어떻게 작성해야 합니까"라는 문제를 해결하기 위한 더 깨끗한 솔루션처럼 보이지만, 어떻게 구성하느냐에 따라 다릅니다.
언급URL : https://stackoverflow.com/questions/512466/how-to-implement-an-abstract-class-in-ruby
'programing' 카테고리의 다른 글
maven 3.8.5가 있는 intelij Idea에서 Springboot 2.7 프로젝트를 가져오는 중 오류 발생했습니다. (0) | 2023.07.02 |
---|---|
Pandas DataFrame에서 Parquet 파일을 읽는 방법은 무엇입니까? (0) | 2023.06.27 |
SQL 예외: 문자열 또는 이진 데이터가 잘립니다. (0) | 2023.06.27 |
Python mysqldb: 라이브러리가 로드되지 않음: libmysqlclient.18.dylib (0) | 2023.06.27 |
Excel 하이퍼링크 대량 업데이트 (0) | 2023.06.27 |