Rubyによるデザインパターン ~ その3
Rubyによるデザインパターンの内容をまとめようって試みですが、初っ端の10ページくらいをまとめて1つの記事にしちゃって、流石に楽をし過ぎた気がするのでもうちょっと書いてみます。
原則は前回まとめたので、今回は実際のパターンについて説明していきたいと思います。
ギャップを埋める: Adapter
ソフトウェアは思いつきで作られます。その為、世の中には互換性の無いオブジェクト(互いにたいわしたくてもインターフェースが合わないので出来ないオブジェクト)が腐るほどあります。そこで必要なのがAdapterです。
Adapterの例をコードで見てみましょう。例えば、ファイルを暗号化する既存のクラスがあるとします。
class Encrypter def initialize(key) @key = key end def encrypt(reader, writer) # readerとwriterはFileオブジェクト。readerの内容を暗号化してwriterに書き込む。読み込みはgetcメソッドで一文字ずつ行い、ファイルの終端判定はeof?メソッドで行う。 end end
Encryperクラスを使って一般的なファイルを簡単に暗号化する事が出来ます。2つのファイルを開き、選んだ秘密鍵を使ってEncrypterクラスのオブジェクトを生成し、encryptを呼ぶだけです。
reaer = File.open('message.txt') writer = File.open('message.encrypted', 'w') encrypter = Encrypter.new('my secret key') encrypter.encrypt(reader, writer) # この後、reader.closeとwriter.closeを行うべき
さて、それではファイルでは無く文字列を暗号化したい場合にはどうすれば良いでしょうか?こういった時にAdapterが活躍します。今回の例で言えば、外側からはFileオブジェクトに見えて(getcメソッドが使えて)、内部では文字列を扱うようなオブジェクトが必要です。そこで、StringOIAdapterを作ります。
class StringIOAdapter def initialize(strin) @string = string @position = 0 end def getc return nil if @position >= @string.length ch = @string[@position] @position += 1 return ch end def eof? @position >= @string.length end end reader = StringIOAdapter.new('message.txt') writer = File.open('message.encrypted', 'w') encrypter = Encrypter.new('my secret key') encrypter.encrypt(reader, writer)
Fileオブジェクトが持つgetcメソッドやeof?メソッドを実装する事で、Fileオブジェクトと同じインターフェースを持つオブジェクトを作る事が出来ました。このように、Adapterは既存のインターフェースと必要なインターフェースとの間の深い溝を橋渡ししてくれます。
ちなみに、上記のコードはほぼオルセン本のままなのですが、encrypt内でgetcとeof?が使われるという実装の詳細に依存した構造になっている為、あまりよろしく無い気がします。で、ちょろっと調べたところRubyにはStringIOというクラスが標準ライブラリとして用意されてるみたいです。
require 'stringio' sio = StringIO.new('hoge', 'r+') sio.getc # => "h" sio.getc # => "o" sio.eof? # => false sio.getc # => "g" sio.getc # => "e" sio.eof? # => true
理想通りの挙動ですね。このStringIOクラスも、Adapterであるといえるでしょう。
また、オルセン本では既に存在するクラスに拡張を行う事でAdapterの機能を果たす方法も紹介しています。例えば、
class BritishTextObject attr_reader :string, :size_mm, :colour # ... end
に対して、外部からtextメソッド、size_inchesメソッド、colorメソッドを用いてアクセスしたいとしましょう。Adapterを使う場合には、
class BritishTextObjectAdapter < TextObject def initialzie(bto) @bto = bto end def text @bto.string end def size_inches @bto.size_mm / 25.4 end def color @bto.clour end end
のようなクラスを作る事になります。一方、Rubyの動的な性質を利用して、
require 'british_text_object' class BritishTextObject def text string end def size_inches size_mm / 25.4 end def color colour end end
のように書く事も出来ます。ファイルの先頭のrequireで元のBritishTextObjectクラスをロードし、その後のclass BritishTextObject文でクラスを再オープンしていくつかのメソッドを追加で定義しています。わざわざAdapterクラスを作らなくてもインターフェースのギャップを埋める事が出来ました。
- 作者: Russ Olsen,ラス・オルセン,小林健一,菅野裕,吉野雅人,山岸夢人,小島努
- 出版社/メーカー: ピアソン桐原
- 発売日: 2009/04/01
- メディア: 単行本
- 購入: 13人 クリック: 220回
- この商品を含むブログ (64件) を見る