We want an object talk to some other object but their interfaces don't match.
We simply wrap the adaptee with our new adapter class. This class implements an interface that the invoker understands, although all the work is performed by the adapted object.
Let's think of a class that takes two open files (a reader and a writer) and encrypts a file.
class Encrypter
def initialize(key)
@key = key
end
def encrypt(reader, writer)
key_index = 0
while not reader.eof?
clear_char = reader.getc
encrypted_char = clear_char ^ @key[key_index]
writer.putc(encrypted_char)
key_index = (key_index + 1) % @key.size
end
end
end
But what happens if the data we want to secure happens to be in a string, rather than in a file? We need an object that looks like an open file, that is, supports the same interface as the Ruby IO
object. We can create an StringIOAdapter
to achieve so:
class StringIOAdapter
def initialize(string)
@string = string
@position = 0
end
def getc
if @position >= @string.length
raise EOFError
end
ch = @string[@position]
@position += 1
return ch
end
def eof?
return @position >= @string.length
end
end
Now we can use a String
as if it were an open file (it only implements a small part of the IO
interface, essentally what we need).
encrypter = Encrypter.new('XYZZY')
reader= StringIOAdapter.new('We attack at dawn')
writer=File.open('out.txt', 'w')
encrypter.encrypt(reader, writer)