2009-10-14 5 views
25

개인 방법이 Ruby에서 작동하는 방식을 이해하는 데 시간이 걸렸습니다. 실제로는 매우 어색한 것으로 느낍니다. 사적인 방법이 그들과 같은 방식으로 다루어지기 좋은 이유가 있다면 누구라도 알고 있습니까? 역사적인 이유일까요? 또는 구현 이유? 아니면 거기에 좋은 단단한 논리적 인 이유 (즉 의미 론적)입니까? 예를 들어Ruby에서 '개인용'이 작동하는 좋은 이유가 있습니까?

: 라인

class Person 
    private 
    attr_reader :weight 
end 

class Spy < Person 
private 
    attr_accessor :code 
public 
    def test 
    code   #(1) OK: you can call a private method in self 
    Spy.new.code #(2) ERROR: cannot call a private method on any other object 
    self.code  #(3) ERROR!!! cannot call a private method explicitly on 'self' 
    code="xyz" #(4) Ok, it runs, but it actually creates a local variable!!! 
    self.code="z" #(5) OK! This is the only case where explicit 'self' is ok 
    weight  #(6) OK! You can call a private method defined in a base class 
    end 
end 
  • 루비의 행동은 (1), (2), (5) 합리적인 것 같다.
  • (6)은 ok입니다. 특히 Java와 C++에서 조금 이상합니다. 이것에 대한 정당한 이유는 무엇입니까?
  • 나는 정말로 (3) 실패 이유를 이해하지 못한다! 설명, 누구?
  • line (4)의 문제는 문법의 모호성처럼 보입니다. 문법은 '사적인'것과 관련이 없습니다.

아이디어가 있으십니까?

+1

저는 실제로 디자인 결정에 대한 추론을 알고 싶어합니다. 내 대답은 당신의 오해를 해결하는 설명 일뿐입니다. 방법의 약간을 제공하는 것을 설명하지만, 이유는 설명하지 않습니다. – EmFi

답변

30

당신은 그것을 도움 (액세스 제어 건너 뛰기) public, private and protected.의 루비의 정의를 읽어 찾을 수

루비의 개인 자바의 보호와 유사하다. Java의 private에 해당하는 Ruby는 없습니다. EDIT :이 솔루션은 이제 Ruby 객체에서 Java의 비공개에 대한 위조의 방법을 제공합니다.

개인은 만 암시 적으로 호출 할 수있는 메소드/변수로 정의됩니다. 이것이 문 2와 3이 실패하는 이유입니다. 다시 말해, 개인은 메소드/변수가 정의 된 클래스 또는 서브 클래스의 컨텍스트에 메소드/변수를 제한합니다. 상속은 private 메소드를 서브 클래스에 전달하므로 암시 적 자체를 사용하여 액세스 할 수 있습니다. (왜 성명 6이 효과가 있는지 설명하십시오.)

나는 당신이 보호받는 것에 더 가깝게 찾고 있다고 생각합니다. 가시성이 부여되지 않은 Java 접근 자와 비슷한 동작 (예 : public, private, protected) Spy에서 개인을 변경하여 모든 6 개의 명령문을 보호합니다. 보호 된 메서드는 정의 클래스 또는 해당 하위 클래스의 모든 인스턴스에서 호출 할 수 있습니다. 호출자가 호출에 응답하는 객체의 클래스이거나 해당 객체에서 상속되는 경우 보호 된 메서드에 대한 명시 적 또는 암시 적 호출은 유효한 문입니다.

class Person 
    private 
    attr_reader :weight 
end 

class Spy < Person 
protected 
    attr_accessor :code 
public 
    def test 
    code   #(1) OK: you can call a private method in self 
    Spy.new.code #(2) OK: Calling protected method on another instance from same class family or a descendant. 
    self.code  #(3) OK: Calling protected method on with explicit self is allowed with protected 
    code="xyz" #(4) Ok, it runs, but it actually creates a local variable!!! 
    self.code="z" #(5) OK! This is the only case where explicit 'self' is ok 
    weight  #(6) OK! You can call a private method defined in a base class 
    end 
end 

s = Spy.new 
s.test # succeeds 
s.code #(7) Error: Calling protected method outside of the class or its descendants. 

진술 4와 같습니다. 모호성을 피하는 것으로 가정합니다. 루비의 역동적 인 성질에 잠재적 인 피해를주는 것은 더 많은 안전 장치입니다. 나중에 클래스를 다시 열어 접근자를 재정의 할 수 없도록합니다. 예를 들어, 오염 된 코드를 평가하여 발생할 수있는 상황.

저는 이러한 행동을 유도 한 의사 결정에 대해서만 추측 할 수 있습니다. 대부분의 경우 나는 그것이 언어의 역동적 인 성격에 있다고 생각한다.

P. 당신이 정말로 사물의 자바 정의를주고 싶다면. 서브 클래스가 아닌 정의 된 클래스에서만 사용할 수 있습니다. 클래스에 self.inherited 메서드를 추가하여 액세스를 제한하려는 메서드에 대한 참조를 제거 할 수 있습니다.무게를 만들기

는 서브 클래스에서 접근 할 수없는 속성 :

class Person 
    private 
    attr_reader :weight 

    def initialize 
    @weight = 5 
    end 

    def self.inherited(subclass) 
    subclass.send :undef_method, :weight 
    end 
end 

class Spy < Person 
private 
    attr_accessor :code 
public 
    def test 
    weight  
    end 
end 

Person.new.send(:weight) # => 5 
Spy.new.send(:weight) #=> Unhelpful undefined method error 
그것은 이런 식으로 undef_method 전화를 대체 할 더 이해 할 수

: 훨씬 더 도움이 오류를 제공

def self.inherited(subclass) 
    subclass.class_eval %{ 
     def weight 
     raise "Private method called from subclass. Access Denied" 
     end 
    } 
    end 

과를 동일한 기능.

다른 클래스의 개인 메서드를 호출하기 위해 보내기가 필요합니다. 실제로 작동하는 것을 증명하는 데에만 사용됩니다.

뒤늦게 볼 때 사생활 보호 및 쓸모없는 것입니다. 만약 당신이 정말로 당신의 방법을 보호하기 위해 심각하다면 당신은 그들을 차단하기 위해 보내기를 무시해야합니다. 다음 코드는 객체의 private_methods에 따라 것을 수행합니다

def send_that_blocks_private_methods(method, *args) 
    if private_methods.include?(method.to_s) 
    raise "Private method #{method} cannot called be called with send." 
    else 
    send_that_allows_private_methods(method, *args) 
    end 
end 

alias_method :send_that_allows_private_methods, :send 
alias_method :send, :send_that_blocks_private_methods 
private :send_that_allows_private_methods 

당신은 대신 모든 개인 방법에 대한 액세스를 거부의 액세스를 차단하려는 private_methods의 class_variable을 지정할 수 있습니다. 비공개로 보낼 수도 있지만, 객체 외부에서 send를 호출하는 합법적 인 방법이 있습니다.

+0

+1 답변을 주셔서 감사합니다. 매우 명확합니다. – MiniQuark

+0

"어느 쪽이 사생활을 보호하고 쓸모 없게 만들 었는지." 그렇지 않아. 커뮤니케이션 가치가 있습니다. 'Foo # things'을 대중에게 공개한다면, 나는 이것을 "사용하라"는 말입니다. 비공개 인 경우 "이 문제에 의존하지 말고 구현 세부 사항을 변경할 수 있습니다."라고 말하고 있습니다. 루비가'Foo'를 다시 열어 ** **'# things'을 다시 정의하게한다면, ** 당신은 ** 메소드에 접근 할 수 있습니다. 그러나 그것은 당신이 예기치 않은 일을하고 있다는 것을 알 수 있도록'send'를 사용하게 만듭니다. 루비의 철학이라고 생각합니다. –

+0

'send_that_blocks_private_methods'는 ['Object # public_send'] (https://ruby-doc.org/core-2.4.1/Object.html#method-i-public_send)와 비슷합니까? –

관련 문제