2011-01-06 5 views
2

Ruby on Rails에서 추상 클래스를 시뮬레이션하고 싶습니다. 나는. 다른 사람이 Abstract.new에 전화를하면 예외가 발생하지만 Child.new (Child < Abstract)으로 전화 할 수 있어야합니다.Ruby에서 추상 클래스 시뮬레이션 (레일즈)

어떻게 하시겠습니까? newinitialize 덮어 쓰기가 작동하지 않습니다.

+1

당신은 수 있습니다 Ruby에서이 작업을하지 않는 것이 훨씬 행복합니다. 루비는 이것도 필요로하지도 않으며 (당신이 알아내는 것처럼) 실제로 루비를 지원하지도 않습니다. –

+1

씨를보고 싶습니다. @ Jörg W Mittag가 이에 대해 말해야합니다. – Zabba

+1

@ Zabba : 한 마디 :하지 마라. :-) –

답변

7

왜이 작업을 수행 하시겠습니까? 추상/인터페이스 클래스의 핵심은 강력하게 유형화 된 언어를 동적 패러다임으로 해킹하는 것입니다. 클래스를 서명에 적합하게 만들려면 원래 클래스에 따라 메소드의 이름을 지정하거나 파사드를 만들고 플러그를 꽂으십시오. 컴파일러를 속이려 고하지 않아도됩니다.

def my_printer obj 
    p obj.name 
end 

그래서 나는

class person 
    attr_accessor :name 
    def initialize 
    @name = "Person" 
    end 
end 

class Employee 
    attr_accessor :name 
    def initialize 
    @name = "Employee" 
    @wage = 23 
    end 
end 

그래서 아무 것도 모두이 이름을 인쇄이

my_printer Person.new 
my_printer Employee.new 

중 하나를 사용하여 우리의 프린터 메소드를 호출에서 우리를 멈추지 이름 속성을 어떤 객체와 인터페이스를 정의 차질없이 : D

+12

기본적으로 이것은 주석이 아니라 대답입니다. – apneadiving

+0

그런데 좋은 코멘트이지만, 나는 apnea.diving.deep에 동의합니다. – OscarRyz

+0

코드 중복을 피하기 위해 이것을하고 싶습니다. 사실 추상 클래스는 추상적이지 않고, 모든 하위 클래스에 필요한 몇 가지 속성을 가지고 있으며 그 하위 클래스에 몇 가지 접근자가 있습니다. –

9

다른 코멘트에서, OP는 abstrac의 목적이 자녀가 필요로하는 행동 (방법)을 공유하는 것입니다. Ruby에서는 필요한 경우 메서드를 "혼합"하는 데 사용되는 모듈을 사용하는 것이 가장 좋습니다. 여기

module Foo 
    def foo 
    puts "foo!" 
    end 
end 

class Concrete 
    include Foo 
end 

Concrete.new.foo # => "foo!" 

을하지만 원래 요청이 만족 될 수있는 방법은 다음과 같습니다 :

class Abstract 
    def foo 
    puts "foo!" 
    end 
end 

class Concrete 
end 

Concrete.new.foo # => "foo!" 

이 : 대신 예를 들어

#!/usr/bin/ruby1.8 

class Abstract 

    def initialize(*args) 
    raise if self.class == Abstract 
    super 
    end 

end 

class Concrete < Abstract 
end 

Concrete.new # OK 
Abstract.new # Raises an exception 
+0

'Abstract

+1

@ d135-1r43이면 레일즈에서 작동하지 않습니다. 레일즈 (2.2.8) 콘솔에서 작동하는 것 같습니다. 그것은 어떻게 파열됩니까? 나는 인수에 따라 Abstract의 ctor를 전달하는 것을 잊었습니다. 그게 수정 됐어. –

+0

+1 믹스 인이가는 가장 좋은 방법입니다. – zetetic

0

당신이 STI를하고이를 원한다면, this thread에서 제안을 따를 수 있습니다 :

class Periodical < ActiveRecord::Base 
    private_class_method :new, :allocate 
    validates_presence_of :type 
end 

class Book < Periodical 
    public_class_method :new, :allocate 
end 

class Magazine < Periodical 
    public_class_method :new, :allocate 
end 

경고 : 이것이 효과적인 해결책인지 잘 모르겠습니다. 이렇게하면 newallocate이 기본 클래스에 숨겨져 하위 클래스에서 다시 활성화되지만 그만으로는 객체가 생성되는 것을 방지하지 못하는 것 같습니다. create!. type에 유효성 검사를 추가하면 기본 클래스가 작성되지 않습니다. 나는 create!을 숨길 수 있다고 생각하지만 레일스가 모델 객체를 인스턴스화 할 수있는 모든 방법을 다룰 지 확신 할 수 없습니다.

1

일부 제 3자가 일부 스텁을 구현할 때 API를 적용하려면 거의 항상이 작업을 수행해야하며 사용자가 실수를 저지를 것이라고 확신 할 수 있습니다. 당신은 당신의 부모 클래스의 특정 접두사 템플릿을 사용할 수 있습니다과 창조에 내부적으로 점검 모듈이 달성 :

에게
module Abstract 
    def check 
    local = self.methods - Object.methods 
    templates = [] 
    methods = [] 
    local.each do |l| 
     if l =~ /abstract_(.*)/ # <--- Notice we look for abstract_* methods to bind to. 
     templates.push $1 
     end 
     methods.push l.to_s 
    end 
    if !((templates & methods) == templates) 
     raise "Class #{self.class.name} does not implement the required interface #{templates}" 
    end 
    end 
end 

class AbstractParent 
    include Abstract 
    def initialize 
    check 
    end 
    def abstract_call # <--- One abstract method here 
    end 
    def normal_call 
    end 
end 

class Child < AbstractParent # <-- Bad child, no implementation 
end 

class GoodChild < AbstractParent 
    def call # <-- Good child, has an implementation 
    end 
end 

이 테스트 :

begin 
    AbstractParent.new 
    puts "Created AbstractParent" 
rescue Exception => e 
    puts "Unable to create AbstractParent" 
    puts e.message 
end 

puts 

begin 
    Child.new 
    puts "Created Child" 
rescue Exception => e 
    puts "Unable to create Child" 
    puts e.message 
end 

puts 

begin 
    GoodChild.new 
    puts "Created GoodChild" 
rescue Exception => e 
    puts "Unable to create GoodChild" 
    puts e.message 
end 

이 결과 :

[~] ruby junk.rb 
Unable to create AbstractParent 
Class AbstractParent does not implement the required interface ["call"] 

Unable to create Child 
Class Child does not implement the required interface ["call"] 

Created GoodChild