개인 클래스 메소드를 작성하는 방법은 무엇입니까?


216

개인 클래스 메소드를 작성하는 이러한 접근 방식은 어떻게 작동합니까?

class Person

  def self.get_name
    persons_name
  end

  class << self

    private

    def persons_name
      "Sam"
    end
  end
end

puts "Hey, " + Person.get_name
puts "Hey, " + Person.persons_name  #=> raises "private method `persons_name' called for Person:Class (NoMethodError)"

그러나 이것은하지 않습니다 :

class Person

  def self.get_name
    persons_name
  end

  private

  def self.persons_name
    "Sam"
  end
end

puts "Hey, " + Person.get_name
puts "Hey, " + Person.persons_name

7
방금이 기사에서 개인 클래스 메소드를 작성하는 방법을 설명하고 그것이 좋았다고 생각했습니다. jakeyesbeck.com/2016/01/24/ruby-private-class-methods/…
Nathan Long

답변:


265

private명시 적 객체 (귀하의 경우 self) 에서 메소드를 정의하는 경우 작동하지 않는 것 같습니다 . private_class_method클래스 메소드를 개인용 (또는 설명 된대로)으로 정의 하는 데 사용할 수 있습니다 .

class Person
  def self.get_name
    persons_name
  end

  def self.persons_name
    "Sam"
  end

  private_class_method :persons_name
end

puts "Hey, " + Person.get_name
puts "Hey, " + Person.persons_name

또는 (ruby 2.1+에서), 메소드 정의는 메소드 이름의 심볼을 반환하기 때문에 다음과 같이 사용할 수도 있습니다 :

class Person
  def self.get_name
    persons_name
  end

  private_class_method def self.persons_name
    "Sam"
  end
end

puts "Hey, " + Person.get_name
puts "Hey, " + Person.persons_name

105

ExiRe 는 다음 같이 썼습니다.

루비의 그런 행동은 정말 실망입니다. 개인 섹션 self.method로 이동하면 개인 섹션이 아닙니다. 그러나 클래스 << self로 옮기면 갑자기 작동합니다. 역겨워 요.

아마도 혼란 스러울 수도 있고, 좌절 할 수도 있지만, 역겨운 것은 아닙니다.

루비의 객체 모델을 이해하고 대응하면 그것은 완벽한 의미가 있습니다 방법은 흐름 조회 , 특히 그 고려 때 private입니다 NOT 액세스 / 가시성 수정, 실제로 메소드 호출 (의 수신자로 클래스와) 논의 여기 ... 루비에는 "비공개 섹션" 과 같은 것이 없습니다 .

프라이빗 인스턴스 메소드 를 정의하려면 private인스턴스의 클래스를 호출 하여 이후에 정의 된 메소드의 기본 가시성을 프라이빗으로 설정합니다. 따라서 클래스의 클래스를 호출 하여 프라이빗 클래스 메소드 를 정의하는 것이 좋습니다 private. 메타 클래스.

자체적으로 선포 된 다른 OO 언어는 혼동을 줄 이겠지만, Ruby의 메타 프로그래밍 기능을 사용하지 않으면 서 혼동스럽고 일관성이 없는 (일관되지 않은 ) 오브젝트 모델 과 비교할 수 있습니다.


따라서 올바르게 이해하면 루비 자체에는 액세스 수정 자 키워드 (공개, 개인 및 보호)가 없지만 액세스 수정 자 메소드 (공개, 개인, 보호)가 있습니까? 이것이 Matz가 적절한 키워드 액세스 수정자를 구현하기 위해 루비 버그 추적기에서 제기해야하는 것입니까, 아니면 예상되는 동작입니까?
Edward

13
@Edward 그것은있어 설계 방식 것으로 junichiito.blogspot.co.uk/2012/03/... . 왜 "적절한"?
iain April

1
이것부터 수행하는 대신에 private_class_method :method_name할 수 있습니다 private_class_method def method_name....
bjt38

send(private_method)개체 외부에서도 액세스 할 수 있습니다.
stevenspiel

1
@ bjt38 명확하게하기 위해private_class_method def self.method_name
Tom

78

기본적으로 모든 클래스 메소드는 공용입니다. 그것들을 비공개로 만들려면 @tjwallace가 작성한 것처럼 모듈 #private_class_method를 사용하거나 다르게 정의 할 수 있습니다.

class << self

  private

  def method_name
    ...
  end
end

class << selfself의 singleton 클래스를 열어 현재 self 객체에 대해 메소드를 재정의 할 수 있습니다. 클래스 / 모듈 ( "정적") 메소드를 정의하는 데 사용됩니다. 개인 메소드를 정의하면 실제로 개인 클래스 메소드가 제공됩니다.


17

완전성을 위해 private_class_method를 별도의 행으로 선언하는 것을 피할 수도 있습니다. 나는 개인적 으로이 사용법을 좋아하지 않지만 그것이 존재한다는 것을 아는 것이 좋습니다.

private_class_method  def self.method_name
 ....
end

5

나도이 영역에서 루비 (또는 최소한 그것에 대한 지식)가 부족하다는 것을 알게된다. 예를 들어 다음은 내가 원하는 것을 수행하지만 서투른 것입니다.

class Frob
    attr_reader :val1, :val2

    Tolerance = 2 * Float::EPSILON

    def initialize(val1, val2)
        @val2 = val1
        @val2 = val2
        ...
    end

    # Stuff that's likely to change and I don't want part
    # of a public API.  Furthermore, the method is operating
    # solely upon 'reference' and 'under_test' and will be flagged as having
    # low cohesion by quality metrics unless made a class method.
    def self.compare(reference, under_test)
        # special floating point comparison
        (reference - under_test).abs <= Tolerance
    end
    private_class_method :compare

    def ==(arg)
        self.class.send(:compare, val1, arg.val1) &&
        self.class.send(:compare, val2, arg.val2) &&
        ...
    end
end

위의 코드에서 내 문제는 Ruby 구문 요구 사항과 코드 품질 메트릭이 번거로운 코드를 만들기 위해 노력한다는 것입니다. 코드를 원하는대로 작동시키고 메트릭을 조용하게하려면 compare ()를 클래스 메서드로 만들어야합니다. 클래스의 공개 API에 포함시키고 싶지 않기 때문에 비공개이어야하지만 '비공개'자체로는 작동하지 않습니다. 대신 'private_class_method'또는 이와 같은 해결 방법을 사용해야합니다. 이것은 차례로 '== ()'에서 테스트하는 각 변수에 대해 'self.class.send (: compare ...'를 사용하도록 강요합니다.


send 를 사용해야한다는 사실은 클래스 메소드를 private으로 표시하는 "방법"과 관련이 없습니다. "외부"에서 개인 메소드를 호출 할 수 없습니다.
Pascal

4

인스턴스 메소드는 클래스 정의 블록 내에 정의됩니다. 클래스 메소드는 클래스의 싱글 톤 클래스에서 "메타 클래스"또는 "고유 클래스"라고도하는 싱글 톤 메소드로 정의됩니다. private키워드가 아니라 메소드 ( Module # private )입니다.

이 메소드의 호출이다 self#private/ A#private그렇지 않으면 전환 될 때까지 모든 향후 인스턴스 메소드 정의에 대한에 개인 액세스를 "전환"

class A
  private
    def instance_method_1; end
    def instance_method_2; end
    # .. and so forth
end

앞에서 언급했듯이 클래스 메소드는 실제로 싱글 톤 클래스에 정의 된 싱글 톤 메소드입니다.

def A.class_method; end

또는 특수 구문을 사용하여 익명의 싱글 톤 클래스 A의 정의 본문을 엽니 다.

class << A
  def class_method; end
end

"메시지 개인" -self -inside 의 수신자는 class A클래스 오브젝트 A입니다. class << A블록 안의 self 는 또 다른 오브젝트 인 singleton 클래스입니다.

다음 예제는 실제로 호출에 대해 두 개의 다른 수신자 또는 대상을 사용하여 private 이라는 두 개의 다른 메소드를 호출하는 것입니다. 첫 번째 부분에서는 프라이빗 인스턴스 메소드 ( "클래스 A")를 정의하고, 후자에서는 프라이빗 클래스 메소드 (실제로 A의 싱글 톤 클래스 오브젝트에 대한 싱글 톤 메소드)를 정의합니다.

class A
  # self is A and private call "A.private()"
  private def instance_method; end

  class << self
    # self is A's singleton class and private call "A.singleton_class.private()"
    private def class_method; end
  end
end

이제이 예제를 약간 다시 작성하십시오.

class A
  private
    def self.class_method; end
end

[루비 언어 디자이너]가 저지른 실수를 볼 수 있습니까? 다가오는 A의 모든 인스턴스 메소드에 대한 개인 액세스를 토글하지만 다른 클래스 인 싱글 톤 클래스에서 싱글 톤 메소드를 계속 선언하십시오.


-1

루비는 나쁜 해결책을 제시하는 것 같습니다. 설명하려면 개인 클래스 메소드에 대한 액세스를 보여주는 간단한 C ++ 예제로 시작하십시오.

#include <iostream>

class C
{
    public:
        void instance_method(void)
        {
            std::cout << "instance method\n";
            class_method();  // !!! LOOK !!! no 'send' required. We can access it
                             // because 'private' allows access within the class
        }
    private:
        void static class_method(void) { std::cout << "class method\n"; }
};

int main()
{
    C c;

    c.instance_method(); // works
    // C::class_method() does not compile - it's properly private
    return 0;
}

위의 실행

   % ./a.out
   instance method
   class method

이제 루비는 동등한 것을 제공하지 않는 것 같습니다. 루비의 규칙은 개인 메소드에 수신자가 접근해서는 안된다는 것입니다. 그건,

inst.pvt_method  # FAILS
pvt_method # WORKS only within the class (good)

개인 인스턴스 메서드에는 문제가 없지만 개인 클래스 메서드에 문제가 발생합니다.

루비가 이런 식으로 작동하기를 원합니다.

class C
    def instance_method
        STDOUT << "instance method\n"

        # Simple access to the private class method would be nice:
        class_method   # DOES NOT WORK. RUBY WON'T FIND THE METHOD
        C.class_method # DOES NOT WORK. RUBY WON'T ALLOW IT

        # ONLY THIS WORKS. While I am happy such capability exists I think
        # the way 'send' should be used is when the coder knows he/she is
        # doing a no-no.  The semantic load on the coder for this is also
        # remarkably clumsy for an elegant language like ruby.
        self.class.send(:class_method)
    end

    private_class_method def self.class_method() STDOUT << "class method\n"; end
end

그러나 아아, 위의 방법은 효과가 없습니다. 누군가 더 나은 방법을 알고 있습니까?

메소드 전에 '보내기'를 볼 때 코드가 API 디자이너의 의도를 위반하고 있다는 명백한 징후이지만,이 경우 디자인은 클래스의 인스턴스 메소드가 전용 클래스 메소드를 호출하도록하는 것입니다.


-14

루비 2.3.0 현재

class Check
  def self.first_method
    second_method
  end

  private
  def self.second_method
    puts "well I executed"
  end
end

Check.first_method
#=> well I executed

나는 private def self.second_method루비 2.3.3에서 작동하지 않는 각 메소드 표기법 전에 이것을 시도했다 . 그러나이 표기법은 저에게 효과적입니다.
Emile Vrijdags 2016 년

11
전화 Check.second_method도 문제없이 작동하므로 실제로는 비공개가 아니기 때문에 이것은 올바르지 않습니다.
Deiwin

1
작동하지 않습니다private_class_method :second_method
킹 사 브리
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.