Singleton

Singleton

Singleton 모듈은 싱글턴(Singleton) 패턴을 구현해 주는 모듈이에요. 클래스에 include 하면 그 클래스의 인스턴스가 프로그램 전체에서 단 하나만 만들어지도록 보장해 줘요.

출처: Ruby 3.3 API

본문

사용법

Singleton을 쓰려면 클래스에 모듈을 include 하면 돼요.

class Klass
  include Singleton
  # ...
end

이렇게 하면 Klass의 인스턴스는 단 하나만 만들 수 있어요.

a,b = Klass.instance, Klass.instance

a == b
# => true

Klass.new
# => NoMethodError - new is private ...

인스턴스는 Klass.instance()를 처음 호출하는 순간에 만들어져요.

class OtherKlass
  include Singleton
  # ...
end

ObjectSpace.each_object(OtherKlass){}
# => 0

OtherKlass.instance
ObjectSpace.each_object(OtherKlass){}
# => 1

이런 동작은 상속과 클론(clone) 상황에서도 그대로 유지돼요.

동작 원리

위 결과는 다음 방법으로 얻어져요.

  • Klass.newKlass.allocate를 private으로 만든다.
  • Klass.inherited(sub_klass)Klass.clone()을 오버라이드해서 상속·클론 시에도 싱글턴 속성이 유지되게 한다.
  • Klass.instance() 메서드를 제공해서 호출할 때마다 같은 객체를 돌려준다.
  • Klass._load(str)을 오버라이드해 Klass.instance()를 호출하게 한다.
  • Klass#cloneKlass#dup을 오버라이드해 TypeError를 던져 클로닝·듀핑을 막는다.

Singleton과 Marshal

기본적으로 Singleton_dump(depth)는 빈 문자열을 돌려줘요. 그래서 Marshal을 쓸 때 기본적으로 인스턴스 변수 같은 상태 정보가 빠져요. Singleton을 쓰는 클래스는 사용자 정의 _load(str)_dump(depth) 메서드를 제공해서 이전 상태 일부를 보존할 수 있어요.

require 'singleton'

class Example
  include Singleton
  attr_accessor :keep, :strip
  def _dump(depth)
    # this strips the @strip information from the instance
    Marshal.dump(@keep, depth)
  end

  def self._load(str)
    instance.keep = Marshal.load(str)
    instance
  end
end

a = Example.instance
a.keep = "keep this"
a.strip = "get rid of this"

stored_state = Marshal.dump(a)

a.keep = nil
a.strip = nil
b = Marshal.load(stored_state)
p a == b  #  => true
p a.keep  #  => "keep this"
p a.strip #  => nil

Constants

VERSION

Public Class Methods

_load()

기본적으로 instance()를 호출해요. 싱글턴 상태를 보존하려면 오버라이드하면 돼요.

instance()

싱글턴 인스턴스를 돌려줘요.

Private Class Methods

append_features(mod)

모듈이나 클래스가 아닌 대상에 포함되면 TypeError를 던져요.

included(klass)

klassnewallocate를 private 클래스 메서드로 만들고 SingletonClassMethods를 extend 해요.

Public Instance Methods

_dump(depth = -1)

기본적으로 marshal할 때 아무 상태도 보존하지 않아요.

clone()

클로닝을 막기 위해 TypeError를 던져요.

dup()

듀핑을 막기 위해 TypeError를 던져요.