SimpleDelegator 클래스

SimpleDelegator 클래스

SimpleDelegatorDelegator의 구체적인 구현이에요. 생성자에 넘긴 객체로 지원되는 모든 메서드 호출을 위임(delegate)하고, 나중에 __setobj__로 위임 대상을 바꿀 수도 있죠.

출처: Ruby 4.0 API

본문

이 클래스는 그냥 객체를 감싸서(decorate) 메서드 호출을 전부 원본 객체로 넘겨주고 싶을 때 써요. 다음 예시를 볼게요.

class User
  def born_on
    Date.new(1989, 9, 10)
  end
end

require 'delegate'

class UserDecorator < SimpleDelegator
  def birth_year
    born_on.year
  end
end

decorated_user = UserDecorator.new(User.new)
decorated_user.birth_year  #=> 1989
decorated_user.__getobj__  #=> #<User: ...>

UserDecorator.new(User.new)에서 넘긴 User 객체가 내부 위임 대상이 돼요. born_on 같은 메서드는 정의하지 않아도 SimpleDelegator가 원본 객체로 전달해 주죠.

SimpleDelegatorDelegator의 하위 클래스라는 점을 이용하면, super를 호출해서 위임 대상 객체에서 메서드를 실행시킬 수도 있어요.

class SuperArray < SimpleDelegator
  def [](*args)
    super + 1
  end
end

SuperArray.new([1])[0]  #=> 2

[]에서 super를 부르면 위임 대상인 실제 배열의 []가 실행되고, 그 결과에 1을 더해 반환한 거예요.

위임 대상을 아무 때나 바꿀 수 있다는 점을 활용한 예시도 볼게요.

class Stats
  def initialize
    @source = SimpleDelegator.new([])
  end

  def stats(records)
    @source.__setobj__(records)

    "Elements:  #{@source.size}\n" +
    " Non-Nil:  #{@source.compact.size}\n" +
    "  Unique:  #{@source.uniq.size}\n"
  end
end

s = Stats.new
puts s.stats(%w{James Edward Gray II})
puts
puts s.stats([1, 2, 3, nil, 4, 5, 1, 2])

Prints:

Elements:  4
 Non-Nil:  4
  Unique:  4

Elements:  8
 Non-Nil:  7
  Unique:  6

@source가 가리키는 SimpleDelegator에 매번 다른 레코드 배열을 __setobj__로 넣어 주고, 그 위에서 size, compact, uniq를 호출하는 방식이에요.

Public Instance Methods

getobj → object

현재 메서드 호출이 위임되고 있는 객체를 반환해요.

setobj(obj) → obj

위임 대상을 obj로 바꿔요.

중요한 점은 이 변경이 SimpleDelegator 자신의 메서드에는 영향을 주지 않는다는 거예요. 그래서 위임 대상을 바꿀 때는 원래 위임 대상과 같은 타입의 객체로 바꾸는 게 보통 좋아요.

위임 대상을 바꾸는 예시를 볼게요.

names = SimpleDelegator.new(%w{James Edward Gray II})
puts names[1]    # => Edward
names.__setobj__(%w{Gavin Sinclair})
puts names[1]    # => Sinclair

names[1]이 처음엔 Edward, __setobj__ 후에는 Sinclair를 반환하는 걸 볼 수 있어요.