Singleton 모듈
Singleton 모듈
Singleton 모듈은 싱글턴 패턴(Singleton pattern)을 구현해 주는 모듈이에요. "이 클래스의 인스턴스는 언제나 하나만 존재하게 하고 싶다"는 요구를 모듈 하나로 해결할 수 있죠.
출처: Ruby 4.0 API
본문
클래스에 include Singleton 한 줄만 넣으면 돼요.
class Klass
include Singleton
# ...
end
이렇게 하면 Klass의 인스턴스는 하나만 만들 수 있어요.
a,b = Klass.instance, Klass.instance
a == b
# => true
Klass.new
# => NoMethodError - new is private ...
Klass.instance를 몇 번을 불러도 항상 같은 객체가 돌아오고, new는 private이라 직접 호출할 수 없어요.
인스턴스는 Klass.instance()를 처음 호출하는 시점에 만들어져요.
class OtherKlass
include Singleton
# ...
end
ObjectSpace.each_object(OtherKlass){}
# => 0
OtherKlass.instance
ObjectSpace.each_object(OtherKlass){}
# => 1
처음에는 인스턴스가 0개였다가, instance를 호출한 뒤에는 정확히 1개가 되는 걸 볼 수 있어요.
이런 동작은 상속과 복제(clone)에서도 유지돼요.
구현 방식 (Implementation)
위 동작은 이렇게 이뤄져요:
Klass.new와Klass.allocate를 private으로 만든다.Klass.inherited(sub_klass)와Klass.clone()을 오버라이드해서, 상속·복제돼도 싱글턴 성질이 유지되게 한다.- 호출할 때마다 같은 객체를 반환하는
Klass.instance()메서드를 제공한다. Klass._load(str)을 오버라이드해서Klass.instance()를 호출하게 한다.Klass#clone과Klass#dup을 오버라이드해서TypeError를 던지게 해 복제를 막는다.
Singleton과 Marshal
기본적으로 Singleton의 _dump(depth)는 빈 문자열을 반환해요. 그러면 마샬링(Marshal)이 기본적으로 상태 정보(가령 인스턴스 변수)를 떼어버려요. 싱글턴을 쓰는 클래스는 커스텀 _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
_dump에서 @keep만 저장하고, _load에서 그 값을 복원해서 같은 싱글턴 인스턴스에 다시 넣어 주는 예시예요.
Constants
_load(str)
기본적으로 instance()를 호출해요. 싱글턴 상태를 보존하려면 오버라이드하면 돼요.
instance
싱글턴 인스턴스를 반환해요.