Rubyデザインパターン 6日目 : Adapter

Rubyデザインパターン学習のために、自分なりに読書の結果をまとめていくことに決めました。第6日目はAdapterです。(http://www.amazon.co.jp/gp/product/4894712857/ref=as_li_qf_sp_asin_tl?ie=UTF8&camp=247&creative=1211&creativeASIN=4894712857&linkCode=as2&tag=morizyun00-22)

![スクリーンショット 2015-07-27 11.25.28.png](https://qiita-image-store.s3.amazonaws.com/0/80075/d68a4010-c177-4ef5-2f2e-899a76c76cb6.png)


# **<i class="fa fa-pencil"></i>　6日目 Adapter**

6日目はAdapterパターンです。

イメージとしては、名前の通り、**変換器**のような機能を作る時のパターンです。

具体的なコードに移ります。
# **<i class="fa fa-code"></i> Adapter サンプルコード**

```rb
class Render 
  def render(text_object) 
    @text = text_object.text
    @size = text_object.size_inches
    @color = text_object.color
    printData
  end

  private
    def printData
      puts @text
      puts @size
      puts @color
    end
end
```
これは与えられたオブジェクトの情報を表示するクラスです。

次にデータを提供するオブジェクトに移ります。

```rb
class TextObject
  attr_reader :text, :size_inches, :color

  def initialize(text, size_inches, color)
    @text = text
    @size_inches = size_inches
    @color = color
  end
end
```

実際にデータを表示する時には、

```rb
text_object = TextObject.new("テキストです", 12, "red")

render_object = Render.new
render_object.render(text_object)
```
こういった形になると思います。何も問題ありませんね。

さて、次にこの状態から、他にもRenderクラスによって表示させたいオブジェクトができたとしましょう。


# **<i class="fa fa-keyboard-o"></i> 属性の名前がちょっと違う!?**

さて、次に表示させたいオブジェクトがこんなクラスだった時はどうすればいいでしょうか？

```rb

class BritishTextObject
  attr_reader :string, :size_mm, :colour
  # ....
end
```
先ほどの`TextObject`と比べてみましょう。

```rb
class TextObject
  attr_reader :text, :size_inches, :color
  #....
end
```
データ型は同じだとして、属性名が異なっています。
これでは、データを表示させるためのRenderクラスで不具合が起きてしまいます！

こういった時に**Adapter(変換器)**が解決法を導いてくれます。

```rb
class BritishTextObjectAdapter < TextObject
  def initialize(bto)
    @bto = bto
  end

  def text
    return @bto.string
  end

  def size_inches
    return @bto.size_mm / 25.4
  end

  def color
    return @bto.colour
  end
end
```

使い方は簡単

```rb


class Render 
  def render(text_object) 
    @text = text_object.text
    @size = text_object.size_inches
    @color = text_object.color
    printData
  end

  private
  def printData
    puts @text
    puts @size
    puts @color
  end
end

class TextObject
  attr_reader :text, :size_inches, :color

  def initialize(text, size_inches, color)
    @text = text
    @size_inches = size_inches
    @color = color
  end
end


class BritishTextObject
  attr_reader :string, :size_mm, :colour

  def initialize(string, size_mm, colour)
    @string = string
    @size_mm = size_mm
    @colour = colour
  end
end

class BritishTextObjectAdapter
  def initialize(bto)
    @bto = bto
  end

  def text
    @bto.string
  end

  def size_inches
    @bto.size_mm / 25.4
  end

  def color
    @bto.colour
  end
end


british_text_object = BritishTextObject.new("Momozono", 12, "red")

fixed_british_text_object = BritishTextObjectAdapter.new(british_text_object) #この時点で属性名が変換される

render_object = Render.new
render_object.render(fixed_british_text_object)
```

BritishTextObjectAdapterをクラスとして作成し、引数としてBritishTextObjectを迎える形でRenderクラスで出力する際に必要な属性名に変換しています。

## **<i class="fa fa-diamond"></i> Rubyらしい構成に...**

上記の方法は結構堅苦しい方法です。ここからコードをRubyらしくリファクタリングしてみましょう。

ここでは**オープンクラス**を利用します。

### **<i class="fa fa-question-circle"></i> オープンクラスってなんやっけ？**

```rb
class Array
  def shuffle
    "シャッフルでけへんで！！！"
  end
end

array = %w[hoge huga foobar]
array.shuffle # => シャッフルでけへんで！！！
```
オープンクラスを使うとこういった形で、元より定義されているメソッドを上書きしたり、新しいメソッドを追加したりすることができます。モンキーパッチとも言いますね。

### **<i class="fa fa-check-circle-o"></i> AdapterをRubyのオープンクラスで表現してみましょう。**

```rb
class BritishTextObject
  attr_reader :string, :size_mm, :colour

  def initialize(string, size_mm, colour)
    @string = string
    @size_mm = size_mm
    @colour = colour
  end
end

class BritishTextObject
  def text
    return string
  end

  def size_inches
    return size_mm / 25.4
  end

  def color
    return colour
  end
end

british_text_object = BritishTextObject.new("momozonno", 12, "red")

british_text_object.text # => "momozono"
british_text_object.size_inches# => 0.4724409448818898
british_text_object.color# => "red"
```
わざわざAdapterクラスを作ることなく、Adapterの機能が完全に再現できていますね。

**<i class="fa fa-tint"></i> けど、クラス内部あんまりいじくりたくない...**

そんな時もあるでしょう。そういうときは**特異メソッド**で対応するのがいい解決法です。
Rubyにおける特異メソッドは、オブジェクト(ここではインスタンス)単体だけに特定のメソッドを持たせたいときに便利です。

大事な大事なクラスを汚染したりすることもありません！


```rb
bto = BritishTextObject.new('hello', 50.8, "rainbow")

class << bto
  def color
    colour
  end

  def text
    string
  end

  def size_inches
    size_mm / 25.4
  end
end

bto.color # => "rainbow"
```


# **<i class="fa fa-star"></i>　まとめ**
Adapterは、似たようなオブジェクトの属性名の変更を可能にしました。
Adapterをクラスで定義したり、オープンクラスでメソッドを追加したり、特異メソッドでクラス汚染を避けたりと、いろいろな方法があります。
どれを選択するかは、結局適用するプロジェクトによるでしょう。
ただし、大まかに選択する基準はあるようです。

**Ruby式オープンクラスが有効な場合**

 * 変更が単純なとき
 * クラスの仕様、機能が細部までわかるとき

**Adapterクラスの構築が有効な場合**
 
 * そのクラスの仕様がわからない
 * カプセル化という点ではAdapterクラスの構築のほうが有効です。