第12章 唯一を保証する: Singleton
Ruby
Published: 2019-06-29

「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

Ruby でシングルトンを使ってみる – ためすう

シングルトンとしてのクラス

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

シングルトンは実装詳細なので、シングルトンであると知っているクラスを少なくした方が良いようです。