2010-01-25 6 views
31

루비에서 인스턴스 변수를 "private"(C++ 또는 Java 정의)로 만들 수있는 방법이 있습니까? 즉, 오류가 발생하는 다음 코드를 원합니다.Ruby에서 인스턴스 변수를 비공개로 설정하는 방법은 무엇입니까?

class Base 
    def initialize() 
    @x = 10 
    end 
end 

class Derived < Base 
    def x 
    @x = 20 
    end 
end 

d = Derived.new 
+1

약간 특이한 요청 인 것 같습니다. 그런 패턴의 유스 케이스는 무엇입니까? 아마도 당신은 내가하지 못하는 것을 알고 있습니다, 그것은 미래에 유용 할 것입니다. –

+2

C++ 환경에서 파생 된 클래스에서 액세스 할 수없는 기본 클래스의 전용 변수를 가지고 있으며 파생 클래스에서 수정되지 않는다는 확신이 생깁니다. 위의 예에서 @x가 수정 될 유일한 장소는 private 인스턴스 변수로 만들 수있는 경우 "Base"클래스에 있다고 확신 할 수 있습니다. – prasadvk

+0

좀 더 구체적인 사용 사례를 제공해 주시겠습니까? 그것은 복잡하지 않아도됩니다. 나는 객체가 자신의 슬롯에 접근 할 수 있기를 원하지 않는 문제 하나를 이해한다면 토론에 도움이 될 것 같은 기분이 든다. –

답변

33

루비에서 대부분의 것들과 마찬가지로, 인스턴스 변수는 진정으로 "개인"이 아니며 d.instance_variable_get :@x로 누구나 액세스 할 수 있습니다.

Java/C++과 달리 Ruby의 인스턴스 변수는 이고 항상 private입니다. 메소드처럼 공개 API에 포함되지 않습니다. 왜냐하면 메소드는 해당 상세 getter로만 액세스 할 수 있기 때문입니다. 따라서 API에 문제가 없으면 인스턴스 변수를 악용하는 사람에 대해 걱정하지 않아도됩니다. 대신 인스턴스 변수를 사용하게되므로 걱정하지 않아도됩니다. (누군가가 야생 가서 개인 방법 또는 인스턴스 변수에 액세스하고자하는 경우 물론, 그들을 막을 수있는 방법이 없습니다.)가 확장 할 때 누군가 실수 인스턴스 변수를 덮어 쓰는 경우

유일한 관심사는 당신의 수업. 가능성이 희박한 이름을 사용하면 피할 수 있습니다 (예 : @base_x).

+11

은 그의 코드에서 파생 클래스에서'@ x' 변수를 수정할 수 있다는 점에서 여기서 문제가되지 않습니까? 이는 파생 클래스가 개인 데이터 멤버에 액세스 할 수없는 C++의 기능과 상반됩니다. 그래서, '루비의 인스턴스 변수는 private입니다.'- 중요한 점은 C++에서 private의 의미와 다른 사적인 것입니다. – horseyguy

+7

C++의 말처럼 '루비의 인스턴스 변수는 다음과 같습니다.' 항상 보호 받는다. 완벽한 아날로그는 아니지만 개인의 C++ 의미보다 정확합니다. –

+1

한숨, 옙 ... OOP 지원을 제대로 구현하지 못한 스크립팅 언어로 클럽에 가입하십시오. – nus

5

가시성이 다른 메소드와 달리 Ruby 인스턴스 변수는 항상 private (객체 외부에서)입니다. 그러나 내부 객체 인스턴스 변수는 부모, 자식 클래스 또는 포함 된 모듈에서 항상 액세스 할 수 있습니다.

루비가 @x에 액세스하는 방법을 변경하는 방법이 없으므로 사용자가 제어 할 수 있다고 생각하지 않습니다. @x을 작성하면 해당 인스턴스 변수를 직접 선택하게되고 Ruby는 변수에 대한 가시성 제어를 제공하지 않으므로 함께 살게됩니다.

@marcgg에 따르면, 파생 클래스가 인스턴스 변수에 닿지 않게하려면, 파생 클래스를 전혀 사용하지 않거나 파생 클래스에서 보이지 않게하는 영리한 방법을 찾으십시오.

26

인스턴스 변수를 직접 사용하지 마십시오. 접근 자만 사용하십시오. 그러나

class Foo 
    attr_reader :bar 

    private 

    attr_writer :bar 
end 

privateprotected 당신이 그 의미를 생각 의미하지 않는다는 것을 명심 : 당신은에 의해 공중으로 독자와 작가 개인을 정의 할 수 있습니다. Public 메서드는 명명 된 수신기, 자체 수신기 또는 암시 적 수신기 (x.baz, self.baz 또는 baz)와 같이 모든 수신기에 대해 호출 할 수 있습니다. 보호 된 메서드는 자체 또는 암시 적으로 수신기 (self.baz, baz)로만 호출 할 수 있습니다. 개인 메서드는 암시 적 수신기 (baz)로만 호출 할 수 있습니다.

간단히 말해 Ruby가 아닌 관점에서 문제가 발생하고 있습니다. 항상 인스턴스 변수 대신 접근자를 사용하십시오. 귀하의 의도를 문서화하고 귀하의 API 소비자가 책임있는 성인이라고 가정하려면 public/protected/private을 사용하십시오.

+0

접근성과 수신자에 대한 부분은 내가 과거에 겪었던 몇 가지 문제점을 분명히 도왔다. – Andy

+0

"인스턴스 변수를 직접 사용하지 마십시오 ..."왜 안 되니? 그것들은 언어의 핵심 부분입니다. 나는 그것이 당신의 상황과 당신이 풀려고하는 문제에 달려 있다고 말할 것입니다. – mastaBlasta

+0

그것은 엄지 손가락의 규칙입니다. 물론'attr_reader'와'attr_writer'는 뒤에서 인스턴스 변수를 사용합니다. 그리고 당신은 투명한 memoization ('@_foo || = begin; # slow operation; end')을 위해 그것들을 직접 사용할 수도 있습니다. 그러나 인스턴스 변수를 직접 사용하는 경우 다른 곳의 코드를 변경하지 않고 값을 가져 오거나 설정할 때 해당 동작을 연결할 수 없습니다 (하위 클래스의 코드 포함). '@ isntance_variable'을 잘못 입력하면 예외가 발생하지 않지만'self.mtehod()'는 예외가됩니다. 그것들은 더 이상 "@@ class_variables"보다 "중심적인"것이 아니며 마찬가지로 동사 (verboten)입니다. –

1

인스턴스 변수는 클래스가 아니라 객체로 정의되기 때문에 원하는 것을 수행 할 수 없습니다.

상속보다는 composition을 사용하면 인스턴스 변수를 덮어 쓰는 것에 대해 걱정할 필요가 없습니다.

+0

+1. 대부분의 경우 컴포지션은보다 유연한 솔루션을 제공합니다. 개발자가 우연히 변수 이름을 재사용하는 경우를 방지하기 위해 파생 클래스가 private 멤버 변수에 액세스하지 못했지만, 다시 말하면 루비에서는 변수 미리 선언이 필요하지 않습니다. –

+0

Andrew의 첫 번째 진술은 사실이며 Java/C++에서 오는 프로그래머는 손으로 문신을해야합니다. 클래스는 인스턴스 변수를 선언하지 않습니다. 인스턴스 변수는 프로그램이 실행될 때 객체에 추가됩니다. 인스턴스 변수를 작성하는 메소드가 호출되지 않으면, 오브젝트는 절대로 해당 인스턴스 변수를 가지지 않습니다. – ComDubh

-1

나는 이것이 오래되었다는 것을 알고 있지만, @x에 대한 액세스를 막고 싶지 않은 경우를 만났습니다. 직렬화를 위해 리플렉션을 사용하는 모든 메소드에서 제외하려고했습니다.특히 나는 디버그 목적으로 종종 YAML::dump을 사용하고, 제 경우에는 @x는 Class 클래스였습니다. YAML::dump은 덤프를 거부합니다. 내가 생각했던이 경우

몇 가지 옵션

  1. "to_yaml_properties"

    def to_yaml_properties 
        super-["@x"] 
    end 
    

    그러나 이것은 단지 YAML과 다른 덤프 경우 일 것를 재정 의하여 단지 YAML이 주소 지정 (to_xml ?) 행복하지 않을 것입니다

  2. "instance_variables"를 다시 정의하여 모든 리플렉션 사용자에 대한 주소 지정

    def instance_variables 
        super-["@x"] 
    end 
    
  3. 또한, 나는 내 검색 한 this을 찾았지만, 위의이 정확히 무엇 영업 이익은 말했다하지 않을 수 있습니다 동안 내 요구 그래서

에 대한 간단한 것으로 테스트하지 않았습니다 그는 다른 사람이 액세스하지 않고 목록에서 제외 할 변수를 찾고있는 동안이 게시를 찾으면 이러한 옵션이 유용 할 수 있습니다.

+0

나는이 질문을 별도의 질문으로하고 스스로에게 대답 할 것을 제안한다. 여기에 응답하면 추가 소음이 발생합니다. – Kelvin

+0

@Kelvin 그것이 OP가 이것을 원했던 이유가 명확하지 않았기 때문에 나는 여기에서 대답했다. 그러나 그의 이유가 나의 것과 유사하다면 이것은 그를 도왔을 것이다. 그는 그의 이유를 밝히지 않았다. 만약 그가 그렇게했고 그의 전체 목표가 다르다면 나는 그것을 제거했을 것이다. 그것이 그렇듯이이 질문에 도달 한 사람이 특정 유스 케이스를 해결하려고 할 때 도움이됩니다. 나는 내가 그 대답을 이미 알고있는 질문을하는 것이 옳지 않다고 생각한다. (자신의 질문에 대답하는 것이 명백하다.) – nhed

11

이 동작에는 두 가지 요소가 있습니다. 첫번째는 의 읽기 전용 값x을 저장하고, 두 번째는이 서브 클래스에서 변경되지 않도록 보호하는 을 보호합니다.


읽기 전용 값

것이 가능 루비 읽기 전용 초기화시 값 저장. 이를 위해 Ruby 블록의 클로저 동작을 사용합니다.

class Foo 
    def initialize (x) 
    define_singleton_method(:x) { x } 
    end 
end 

x의 초기 값은 지금 우리가 게터 #x을 정의하는 데 사용하고 foo.x를 호출하여 제외하고는 접근 될 수 없다 블록 내부에 잠겨, 그것은 변경되지 않을 수 있습니다. 이 인스턴스 변수 @x로 저장되지 않습니다, 그러나 그것은 여전히 ​​우리가 define_singleton_method을 사용하여 만든 게터를 통해 사용할 수 있는지

foo = Foo.new(2) 
foo.x # => 2 
foo.instance_variable_get(:@x) # => nil 

참고. 루비에서 게터

보호


는, 모든 클래스의 거의 모든 방법은 런타임에 덮어 쓸 수 있습니다. method_added 후크를 사용하여이를 방지하는 방법이 있습니다.

class Foo 
    def self.method_added (name) 
    raise(NameError, "cannot change x getter") if name == :x 
    end 
end 

class Bar < Foo 
    def x 
    20 
    end 
end 

# => NameError: cannot change x getter 

이것은 게터를 보호하는 매우 무거운 방법입니다.

우리가 개별적으로 method_added 후크 각 보호 게터를 추가하고, 그렇다하더라도, 당신은 method_added 방법 자체를 덮어 쓰지 코더를 방지하기 위해 Foo와 그 서브 클래스에 method_added 보호를 더욱 강화하기 위해 필요한 것을 요구한다.

Ruby를 사용할 때 런타임에 코드를 교체하는 것이 현실감이 있다는 사실을보다 잘 이해할 수 있습니다.

+2

메서드를 정의하면 루비의 메서드 캐시가 무효화된다는 것을 명심하라. 이것들을 많이 만들면 성능에 나쁜 영향을 줄 수 있습니다. – Kelvin

+0

@Kelvin, 정말 고맙습니다. 감사합니다. Ruby에서이 성능 저하에 대해 더 알고 싶다면 https://github.com/charliesome/charlie.bz/blob/master/posts/things-that-clear-rubys-method-cache를 참조하십시오. MD –

관련 문제