[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에서 추상 클래스를 사용하는 유일한 이유는 특정 메서드가 “채워진”반면 다른 메서드는 추상 클래스에서 동작을 갖도록하기 위해서입니다. 덕 타이핑 언어에서는 클래스 / 타입이 아닌 메서드에 초점을 맞추므로 걱정을 그 수준으로 옮겨야합니다.

귀하의 질문에 기본적으로 abstractJava 에서 키워드 를 다시 만들려고 노력하고 있습니다 . 이것은 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


답변

Rails 세계의 모든 사람을 위해 ActiveRecord 모델을 추상 클래스로 구현하는 것은 모델 파일의 다음 선언으로 수행됩니다.

self.abstract_class = true


답변

내 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

물론이 경우 기본 클래스를 초기화하기 위해 다른 오류를 추가하는 것은 사소한 일입니다.


답변

루비를 프로그래밍 한 지난 6 년 반 동안 저는 추상 클래스가 한 번도 필요 하지 않았습니다 .

추상 클래스가 필요하다고 생각하는 경우 Ruby가 아닌이를 제공 / 요구하는 언어로 너무 많이 생각하고있는 것입니다.

다른 사람들이 제안했듯이 믹스 인은 인터페이스 (Java가 정의한대로)로 간주되는 것에 더 적합하고 디자인을 재고하는 것은 C ++와 같은 다른 언어의 추상 클래스를 “필요로하는”것에 더 적합합니다.

2019 년 업데이트 : 16 년 반 동안 Ruby에서 추상 클래스가 필요하지 않았습니다. 내 응답에 대해 언급 한 모든 사람들이 말하는 모든 것은 실제로 Ruby를 배우고 모듈 (일반적인 구현을 제공하는)과 같은 적절한 도구를 사용하여 해결됩니다. 내가 관리 한 팀에는 추상 클래스처럼 실패하는 기본 구현이있는 클래스를 만든 사람들이 있지만 프로덕션에서 NoMethodError와 똑같은 결과를 생성 하기 때문에 대부분 코딩 낭비입니다 AbstractClassError.