「Rubyによるデザインパターン」の読書メモです。
ただ1つのインスタンスしか持てないクラスで、その1つのインスタンスへのアクセスをグローバルに提供します。
シングルトンを使わないロガーの例
test.rb
class SimpleLogger
attr_accessor :level
ERROR = 1
WARNING = 2
INFO = 3
def initialize
@level = WARNING
end
def error(msg)
p "#{self.class}##{__method__}"
p msg
end
def warning(msg)
p "#{self.class}##{__method__}" if @level >= WARNING
p msg
end
def info(msg)
p "#{self.class}##{__method__}" if @level >= INFO
p msg
end
end
logger = SimpleLogger.new
logger.level = SimpleLogger::INFO
logger.info('1つめ')
logger.info('2つめ')
logger2 = SimpleLogger.new
p '---'
p logger.object_id
p logger2.object_id
出力結果
$ ruby test.rb
"SimpleLogger#info"
"1つめ"
"SimpleLogger#info"
"2つめ"
"---"
70311664013160
70311664012640
シングルトンを適用したロガーの例
test.rb
class SimpleLogger
attr_accessor :level
@@instance = SimpleLogger.new
ERROR = 1
WARNING = 2
INFO = 3
def initialize
@level = WARNING
end
def error(msg)
p "#{self.class}##{__method__}"
p msg
end
def warning(msg)
p "#{self.class}##{__method__}" if @level >= WARNING
p msg
end
def info(msg)
p "#{self.class}##{__method__}" if @level >= INFO
p msg
end
def self.instance
return @@instance
end
private_class_method :new
end
logger = SimpleLogger.instance
logger.level = SimpleLogger::INFO
logger.info('1つめ')
logger.info('2つめ')
logger2 = SimpleLogger.instance
p '---'
p logger.object_id
p logger2.object_id
出力結果
$ ruby test.rb
"SimpleLogger#info"
"1つめ"
"SimpleLogger#info"
"2つめ"
"---"
70234052873820
70234052873820
シングルトンモジュール
Singleton モジュールを使って、実装することができます。
include Singleton
シングルトンとしてのクラス
class ClassBasedLogger
ERROR = 1
WARNING = 2
INFO = 3
def self.error(msg)
p "#{self.class}##{__method__}"
p msg
end
end
シングルトンとしてのモジュール (自作)
module ClassBasedLogger
ERROR = 1
WARNING = 2
INFO = 3
def self.error(msg)
p "#{self.class}##{__method__}"
p msg
end
end
モジュールは、インスタンス化するためにあるのではないと少しは示すことができます。
Need-to-Know原則に基づくシングルトン
Need-to-Know原則とは > 「必要な人にだけ情報を開示する」という情報セキュリティにおける基本的な考え方です。
require 'singleton'
class DatabaseConnectionManager
include Singleton
def get_connection
p "#{self.class}##{__method__}"
end
end
class PreferenceManager
def initialize
@reader = PrefReader.new
@writer = PrefWriter.new
@preferences = {
:display_splash => false,
:background_color => :blue
}
end
def save_preferences
@writer.write(DatabaseConnectionManager.instance, @preferences)
end
def get_preferences
@preferences = @reader.read(DatabaseConnectionManager.instance)
end
end
class PrefWriter
def write(manager, preferences)
p "#{self.class}##{__method__}"
p preferences
manager.get_connection
end
end
class PrefReader
def read(manager)
p "#{self.class}##{__method__}"
manager.get_connection
p '読み取ったもの'
end
end
PreferenceManager.new.get_preferences
PreferenceManager.new.save_preferences
シングルトンは実装詳細なので、シングルトンであると知っているクラスを少なくした方が良いようです。