Delegator 클래스

Delegator 클래스

어떤 객체에 메서드 호출을 "넘겨주는"(delegate) 일은 의외로 자주 하게 돼요. 이 라이브러리는 그 위임을 설정하는 세 가지 방법을 제공해요.

가장 쓰기 쉬운 건 SimpleDelegator예요. 생성자에 객체를 넘기면 그 객체가 지원하는 모든 메서드가 위임되어요. 이 대상 객체는 나중에 바꿀 수도 있어요.

한 단계 더 나아가, 최상위 DelegateClass 메서드는 클래스 상속을 통해 손쉽게 위임을 설정하게 해 줘요. 이 방식은 훨씬 유연해서 아마 이 라이브러리에서 가장 흔하게 쓰일 거예요.

마지막으로, 위임 체계를 완전히 제어해야 한다면 추상 클래스 Delegator를 상속받아 필요에 맞게 커스터마이즈할 수 있어요. (이런 제어가 필요할 때는 표준 라이브러리의 Forwardable도 한 번 봐보세요. 여러분 요구에 더 맞을 수도 있어요.)

SimpleDelegator의 구현이 Delegator 사용법의 좋은 예시예요:

require 'delegate'

class SimpleDelegator < Delegator
  def __getobj__
    @delegate_sd_obj # return object we are delegating to, required
  end

  def __setobj__(obj)
    @delegate_sd_obj = obj # change delegation object,
                           # a feature we're providing
  end
end

주의

RDoc은 위임된 메서드를 감지하지 못한다는 점 알아두세요.

상수

  • VERSION — 버전 문자열

클래스 메서드

  • new(obj) — 메서드 호출을 위임할 obj를 받아요. obj가 지원하는 모든 메서드가 위임돼요.

    # File lib/delegate.rb, line 76
    def initialize(obj)
      __setobj__(obj)
    end
    

인스턴스 메서드

  • !()!__getobj__로 위임해요.

    # File lib/delegate.rb, line 181
    def !
      !__getobj__
    end
    
  • !=(obj) → true or false — 두 객체가 값이 같지 않다고 여겨질 때 true를 돌려줘요.

    # File lib/delegate.rb, line 165
    def !=(obj)
      return false if obj.equal?(self)
      __getobj__ != obj
    end
    
  • ==(obj) → true or false — 두 객체가 값이 같다고 여겨질 때 true를 돌려줘요.

    # File lib/delegate.rb, line 157
    def ==(obj)
      return true if obj.equal?(self)
      self.__getobj__ == obj
    end
    
  • __getobj__() — 서브클래스가 반드시 재정의해야 하는 메서드예요. 메서드 호출이 위임되고 있는 객체를 돌려줘야 해요.

    # File lib/delegate.rb, line 189
    def __getobj__
      __raise__ ::NotImplementedError, "need to define '__getobj__'"
    end
    
  • __setobj__(obj) — 서브클래스가 반드시 재정의해야 하는 메서드예요. 위임 대상 객체를 obj로 바꿔요.

    # File lib/delegate.rb, line 197
    def __setobj__(obj)
      __raise__ ::NotImplementedError, "need to define '__setobj__'"
    end
    
  • eql?(obj) → true or false — 두 객체가 같은 값으로 여겨질 때 true를 돌려줘요.

    # File lib/delegate.rb, line 173
    def eql?(obj)
      return true if obj.equal?(self)
      obj.eql?(__getobj__)
    end
    
  • freeze()__getobj__가 돌려주는 객체와 자기 자신을 모두 동결해요.

    # File lib/delegate.rb, line 238
    def freeze
      __getobj__.freeze
      super()
    end
    
  • marshal_dump()__getobj__가 돌려주는 객체의 직렬화를 지원해요.

    # File lib/delegate.rb, line 204
    def marshal_dump
      ivars = instance_variables.reject {|var| /\A@delegate_/ =~ var}
      [
        :__v2__,
        ivars, ivars.map {|var| instance_variable_get(var)},
        __getobj__
      ]
    end
    
  • marshal_load(data) — 직렬화된 객체에서 위임을 다시 설정해요.

    # File lib/delegate.rb, line 216
    def marshal_load(data)
      version, vars, values, obj = data
      if version == :__v2__
        vars.each_with_index {|var, i| instance_variable_set(var, values[i])}
        __setobj__(obj)
      else
        __setobj__(data)
      end
    end
    
  • method_missing(m, *args, &block) — 받은 메서드 호출을 위임 객체로 전달해요. 위임 대상이 그 메서드를 지원하거나 Kernel에 정의돼 있으면 처리하고, 그렇지 않으면 super로 넘겨요.

    # File lib/delegate.rb, line 83
                 def method_missing(m, *args, &block)
    r = true
    target = self.__getobj__ {r = false}
    
    if r && target_respond_to?(target, m, false)
      target.__send__(m, *args, &block)
    elsif ::Kernel.method_defined?(m) || ::Kernel.private_method_defined?(m)
      ::Kernel.instance_method(m).bind_call(self, *args, &block)
    else
      super(m, *args, &block)
    end
    

end


내부적으로 `BasicObject#method_missing`을 호출해요.

- `methods(all=true)` — 이 델리게이트 객체에서 쓸 수 있는 메서드를, 이 객체의 메서드와 `__getobj__` 메서드의 합집합으로 돌려줘요.

```ruby
# File lib/delegate.rb, line 132
def methods(all=true)
  __getobj__.methods(all) | super
end
  • protected_methods(all=true) — 보호 메서드들을 __getobj__ 메서드와의 합집합으로 돌려줘요.

    # File lib/delegate.rb, line 148
    def protected_methods(all=true)
      __getobj__.protected_methods(all) | super
    end
    
  • public_methods(all=true) — 공개 메서드들을 __getobj__ 메서드와의 합집합으로 돌려줘요.

    # File lib/delegate.rb, line 140
    def public_methods(all=true)
      __getobj__.public_methods(all) | super
    end
    
  • raise()Delegatorraise 메서드 호출을 위임할 객체가 없을 때는 __raise__를 사용해요. __raise__의 별칭이기도 해요.

    # File lib/delegate.rb, line 67
    
    
  • respond_to_missing?(m, include_private)__getobj__를 통해 호출을 전달해서, 이 위임 객체가 제공하는 메서드인지 확인해요.

    # File lib/delegate.rb, line 100
    def respond_to_missing?(m, include_private)
      r = true
      target = self.__getobj__ {r = false}
      r &&= target_respond_to?(target, m, include_private)
      if r && include_private && !target_respond_to?(target, m, false)
        warn "delegator does not forward private method \##{m}", uplevel: 3
        return false
      end
      r
    end
    

private 인스턴스 메서드

  • target_respond_to?(target, m, include_private)BasicObject 인스턴스를 처리해요. 대상이 Objectrespond_to?로, 아니면 Kernel#respond_to?를 바인딩해서 확인해요.

    # File lib/delegate.rb, line 115
          def target_respond_to?(target, m, include_private)
    case target
    when Object
      target.respond_to?(m, include_private)
    else
      if KERNEL_RESPOND_TO.bind_call(target, :respond_to?)
        target.respond_to?(m, include_private)
      else
        KERNEL_RESPOND_TO.bind_call(target, m, include_private)
      end
    end
    

end


> 출처: [Ruby 4.0 API - Delegator](https://docs.ruby-lang.org/en/4.0/Delegator.html)

## 더 알아보기

- [SimpleDelegator](https://docs.ruby-lang.org/en/4.0/SimpleDelegator.html) — 가장 쓰기 쉬운 위임 객체
- [Forwardable](https://docs.ruby-lang.org/en/4.0/Forwardable.html) — 위임 체계를 완전히 제어해야 할 때 대안이 되는 표준 라이브러리