2011-02-04 8 views
17

작은 레일 앱에서 작업 중이며 루비의 OOP 모델에 문제가 있습니다. 나는 다음과 같은 간단한 클래스 구조를 가지고있다.Ruby에서 클래스 인스턴스 변수 초기화

class Foo 
    protected 
    @bar = [] 
    def self.add_bar(val) 
     @bar += val 
    end 
    def self.get_bar 
     @bar 
    end 
end 

class Baz < Foo 
    add_bar ["a", "b", "c"] 
end 

내 문제는 내가 바즈의 클래스 정의에 add_bar를 호출 할 때, @bar이 분명히 초기화되지 않았는지, 지금과 나는 + 운영자 nil에 사용할 수 없다는 오류가 발생합니다. add_barFoo에 직접 호출해도이 문제가 발생하지 않습니다. 왜 그런데 어떻게 @bar을 올바르게 초기화 할 수 있습니까?

내가 원하는 것을 분명히하기 위해이 클래스에서 기대하는 동작을 지적 할 것입니다.

Foo.add_bar ["a", "b"] 
Baz.add_bar ["1", "2"] 
Foo.get_bar # => ["a", "b"] 
Baz.get_bar # => ["a", "b", "1", "2"] 

어떻게하면됩니까?

답변

42

짧은 답변 : 문제는 (어떤 방법 이외의) 클래스의 본문에 @bar = []을 쓴 것입니다 : 인스턴스 변수는 서브 클래스

긴 대답에 의해 상속되지 않습니다. 인스턴스 변수를 설정하면 현재 self에 저장됩니다. 클래스 본체에있을 때 self은 Foo 클래스 개체입니다. 따라서 예에서 @foo은 Foo 클래스 객체에 정의됩니다.

나중에 인스턴스 변수를 검색 할 때 Ruby는 현재 self이 무엇이든 찾습니다. Baz에서 add_bar를 호출하면 self이 Baz입니다. 또한 self은 add_bar의 본문에있는 STILL Baz입니다 (해당 메소드가 Foo에도 있음에도 불구하고). 그래서 Ruby는 Baz에서 @bar을 찾아서 찾을 수 없습니다 (Foo에서 정의했기 때문에).

여기

class Foo 
    @bar = "I'm defined on the class object Foo. self is #{self}" 

def self.get_bar 
    puts "In the class method. self is #{self}"  
    @bar 
    end 

    def get_bar 
    puts "In the instance method. self is #{self} (can't see @bar!)" 
    @bar 
    end 
end 

>> Foo.get_bar 
In the class method. self is Foo 
=> "I'm defined on the class object Foo. self is Foo" 

>> Foo.new.get_bar 
In the instance method. self is #<Foo:0x1056eaea0> (can't see @bar!) 
=> nil 

이 명확이 인정 하듯이 약간의 혼란, 그리고 루비에 새로운 사람들을위한 일반적인 장애물 지점 만들 수도, 그렇게 나쁜 생각하지 않습니다 예입니다. 이 개념은 내가 프로그래밍 루비 (일명 "The Pickaxe")의 'Metaprogramming'장을 읽을 때 마침내 클릭했습니다.

문제를 해결하는 방법 : 레일즈의 class_attribute 메소드를 살펴보십시오. 그것은 당신이하려고하는 일을 허용한다. (하위 클래스에서 상속 (overidden) 될 수있는 부모 클래스의 속성 정의).

+2

+1 멋지게 대답했습니다 –

+0

레일즈 2.3. *, 내가 사용해야하는 것, ActiveSupport에 class_attribute 기능을 가지고 있지 않지만, class_inheritable_accessor 내가 원하는 것을 정확하게하는 것처럼 보입니다, 그래서 지금 사용하고 있습니다. 팁 고마워. – HalloDu

1

이 잘 작동하도록 나타납니다 @bar이 클래스의 인스턴스 변수로 정의되어 있기 때문에

class Foo 
    protected 
    @@bar = {} 
    def self.add_bar(val) 
    @@bar[self] ||= [] 
    @@bar[self] += val 
    end 
    def self.get_bar 
    (self == Foo ? [] : @@bar[Foo] || []) + (@@bar[self] || []) 
    end 
end 
class Baz < Foo 
end 

Foo.add_bar ["a", "b"] 
Baz.add_bar ["1", "2"] 
Foo.get_bar  # => ["a", "b"] 
Baz.get_bar  # => ["a", "b", "1", "2"] 
+0

에서 그런 일을 추가 할 수 있지만 문제는의 @@ 바 변수가 클래스의 전역으로 그것은이다 서브 클래스도 마찬가지입니다. 나는 내가 원하는 것을 분명히하기 위해 나의 질문을 편집 할 것이다. – HalloDu

+0

내 대답이 바뀝니다. 이제는 그게 당신의 문제를 해결한다고 생각합니다. –

3

잘가, 다음은 클래스 푸로 제한합니다. 확인이 :

class Foo 
    @bar = [] 
end 

class Baz < Foo 
end 

Foo.instance_variables #=> [:@bar] 
Baz.instance_variables #=> [] 

어쨌든,이 간단한 예를 들어, 당신은이 작업을 수행 할 수 있습니다

class Foo 
    protected 
    def self.add_bar(val) 
     @bar ||=[] 
     @bar += val 
    end 
    def self.get_bar 
     @bar 
    end 
end 

class Baz < Foo 
    add_bar ["a", "b", "c"] 
end 

here에 대해 자세히 알아보십시오.

2

나는 그렇게처럼 수행

class Base 

    class << self 

     attr_accessor :some_var 


     def set_some_var(value) 
      self.some_var = value 
     end 

    end 

end 


class SubClass1 < Base 
    set_some_var :foo 
end 

class SubClass2 < Base 
    set_some_var :bar 
end 

그럼 당신이 원하는해야한다. 당신이 원하는 경우

[8] pry(main)> puts SubClass1.some_var 
foo 
[9] pry(main)> puts SubClass2.some_var 
bar 

참고 set_some_var 방법은 선택 사항입니다, 당신은 SubClass1.some_var = ... 할 수 있습니다. 당신은 몇 가지 기본 값을 원하는 경우

는, 그래, 난 하나 너무 시도 않았다 class << self

def some_var 
    @some_var || 'default value' 
end 
관련 문제