Decorator
- 作者: Russ Olsen,ラス・オルセン,小林健一,菅野裕,吉野雅人,山岸夢人,小島努
- 出版社/メーカー: ピアソン桐原
- 発売日: 2009/04/01
- メディア: 単行本
- 購入: 12人 クリック: 193回
- この商品を含むブログ (58件) を見る
本日はSingletonパターンを勉強します。
デザインパターンと言えばSingletonかっていうくらい有名なパターンです。
Singletonとは「オブジェクトを1つしか作らない」ことを指します。
そういえば、僕が研修のとき、
初めて教えてもらったデザインパターンはSingletonパターンだったような気がする。
今回も本に載っているとおり、
Logger(ログを出力する)クラスを作ってSingletonパターンを実装してみます。
まずはログを出力するSimpleLoggerクラスを作成します。
SimpleLoggerクラスにはerror、warning、infoの3つのメソッドが存在します。
それぞれのメソッドはログの出力レベル(@level)によって出力有無を切り替えます。
class SimpleLogger attr_accessor :level ERROR = 1 WARNING = 2 INFO = 3 def initialize @log = File.open("log.txt", "w") @level = WARNING end def error(msg) @log.puts(msg) @log.flush end def warning(msg) @log.puts(msg) if @level >= WARNING @log.flush end def info(msg) @log.puts(msg) if @level >= INFO @log.flush end end
上記のSimpleLoggerクラスはまだSingletonになっていません。
なので通常とおりオブジェクトを生成して利用します。
呼び出しもとプログラムは以下のとおりです。
logger = SimpleLogger.new logger.level = SimpleLogger::INFO logger.info("info1") logger.warning("warning2") logger.error("error3")
ログファイル(log.txt)には以下のように出力されます。
info1 warning2 error3
上記のコードだと、newメソッドを呼び出すたびに新しいオブジェクトを生成することができます。
(つまり、Singletonではない、ということになります)
それでは、SimpleLoggerクラスを修正してSingletonにしてみます。
まずはSingletonとなるオブジェクトを保持するクラス変数を追加します。
次に、クラス変数に追加したオブジェクトを返すクラスメソッドを追加します。
class SimpleLogger attr_accessor :level ERROR = 1 WARNING = 2 INFO = 3 def initialize @log = File.open("log.txt", "w") @level = WARNING end def error(msg) @log.puts(msg) @log.flush end def warning(msg) @log.puts(msg) if @level >= WARNING @log.flush end def info(msg) @log.puts(msg) if @level >= INFO @log.flush end @@instance = SimpleLogger.new def self.instance @@instance end end
追加したのはコードの最後の部分です。
@@instance = SimpleLogger.new def self.instance @@instance end
この変更により、呼び出しもとでは、newメソッドの呼び出しからinstanceメソッドの呼び出しに変更することでSingletonなオブジェクトを取得することができます。
logger = SimpleLogger.instance logger.level = SimpleLogger::INFO logger.info("info1") logger.warning("warning2") logger.error("error3")
もう少し修正してみます。
今のコードでは相変わらずnewメソッドを呼び出すことができてしまいます。
そこで、newメソッドの可視性をprivateに変更します。
class SimpleLogger attr_accessor :level ERROR = 1 WARNING = 2 INFO = 3 def initialize @log = File.open("log.txt", "w") @level = WARNING end def error(msg) @log.puts(msg) @log.flush end def warning(msg) @log.puts(msg) if @level >= WARNING @log.flush end def info(msg) @log.puts(msg) if @level >= INFO @log.flush end @@instance = SimpleLogger.new def self.instance @@instance end private_class_method :new end
newメソッドをprivateにするために最後の1行を追加しました。
#Rubyには、とってつけたようなメソッドが用意されてるのがスゴいと思う。ちなみにインスタンスメソッドをprivateにする場合は、privateメソッドを利用するみたい。
private_class_method :new
これで直接newメソッドを呼び出すことを禁止できました。
以上がクラスベースでSingletonパターンを実現する流れです。
ここからはRubyらしくSingletonパターンを実現するにはどうするか、ってお話です。
というか、、
Singletonパターンを実現するためにRubyにはSingletonモジュールが用意されています。
#なんでもアリな気がしてきました。。
Singletonモジュールを使って、さきほどのSimpleLoggerクラスを修正してみます。
requireと、includeを追加するだけです。
require "singleton" class SimpleLogger include Singleton attr_accessor :level ERROR = 1 WARNING = 2 INFO = 3 def initialize @log = File.open("log.txt", "w") @level = WARNING end def error(msg) @log.puts(msg) @log.flush end def warning(msg) @log.puts(msg) if @level >= WARNING @log.flush end def info(msg) @log.puts(msg) if @level >= INFO @log.flush end end
Singletonモジュールをincludeすると、instanceメソッドが定義され、newメソッドがprivateに設定されます。
以上、おしまい。