Rubyデザインパターン 7日目 : Proxy

Rubyデザインパターン学習のために、自分なりに読書の結果をまとめていくことに決めました。第7日目はProxyです。(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>　7日目 Proxy**

7日目はProxyパターンです。
直訳で**代理**という意味です。

ものすごくざっくり言うと、プログラム本体のクラスの前にもう一つクラスを咬ませて、**<i class="fa fa-eye"></i>番人かつ<i class="fa fa-users"></i>代理人**のような役割を持たせるパターンです。





# **<i class="fa fa-code"></i> Proxy サンプルコード**

**銀行の管理プログラムを例にとってみましょう**

```rb
class BankAccount
  attr_reader :balance

  def initialize(starting_balance = 0)
    @balance = starting_balance
  end

  def deposit(amount)
    @balance += amount
  end

  def withdraw(amount)
    @balance -= amount
  end
end
```
ユーザーがこのオブジェクトにモバイルからアクセスしたいと考えましょう。

ここで、Proxyとしてもう一つクラスを作ってみましょう。

```rb
class BankAccountProxy
  def initialize(real_object)
    @real_object = real_object
  end

  def balance
    @real_object.balance
  end

  def deposit(amount)
    @real_object.deposit(amount)
  end

  def withdraw(amount)
    @real_object.withdraw(amount)
  end
end

account = BankAccount.new

proxy = BankAccountProxy.new(account)
proxy.deposit(50)
proxy.withdraw(10)
```
Proxyを一つ作ってみましたが、これだけではただ参照をするだけの役立たずクラスです。

# **<i class="fa fa-shield"></i>　防御Proxy**

先ほどの章で作ったProxyを、役に立つものに変えていきましょう。

とはいっても実装は簡単です。

```rb

require 'etc'

class AccountProtectionProxy
  def initialize(real_account, owner_name)
    @subject = real_account
    @owner_name = owner_name
  end

  def deposit(amount)
    check_access
    return @subject.deposit(amount)
  end

  def withdraw(amount)
    check_access
    return @subject.withdraw(amount)
  end

  def balance
    check_access
    return @subject.balance
  end

  def check_access
    if Etc.getlogin != @owner_name
      raise "Illegal access: #{Etc.getlogin} cannot access account."
    end
  end
end
```
引数は`real_account`と`owner_name`の二つを受け取るようにします。

`check_access`メソッドで、一度監査が入り、オーナーであれば本体のBankAccountオブジェクトへのアクセス権を得ることができ、オーナーではないと判断されればエラーが起きるような実装になっています。

こういった形で、データを管理するBankAccountの前に一つProxyを咬ませるような実装にすることで、本体オブジェクト(**以下、サブジェクトと呼びます**)へのアクセスを制御できます。

サブジェクトの前に咬ませるような構造なので、そのとっかえも、仕様変更も簡単です。セキュリティ仕様を変えたければAccountProtectionProxyだけを編集すれば良いし、希望があれば、除去することもできます。

こういった、**責務の分離**は非常に重要な概念です。

セキュリティの仕様変更をしたいときは、セキュリティに関する部分だけを見れば良いので、素早く問題解決に当たることができます。
さて、これだけでも立派なセキュリティ実装パターンが実現できているのですが、現状での問題点がもう一つあります。

それは、**負荷のかかるオブジェクト作成を初めてに行ってしまっている点です <i class="fa fa-bolt"></i>**

**`account = BankAccount.new(100)`**

アプリケーションにおいて、負荷のかかるオブジェクト作成を**可能な限り遅延させる**という方法は、アプリケーション全体のパフォーマンスに良い影響を与えることがよくあります。

## **<i class="fa fa-check-circle-o"></i> オブジェクト作成を遅延させる**
```rb

class VirtualAccountProxy
  def initialize(starting_balance = 0)
    @starting_balance = starting_balance
  end

  def deposit(amount)
    s = subject
    return s.deposit(amount)
  end

  def withdraw(amount)
    s = subject
    return s.withdraw(amount)
  end

  def balance
    s = subject
    return s.balance
  end

  def subject
    @subject || (@subject = BankAccount.new(@starting_balance))
  end
end
```

`subject`メソッドを実装することにより、可能な限りオブジェクトの生成を遅らせています。
オブジェクトが作られるのは、`subject`が実行され、かつ`@subject`にまだ中身がないときだけです。
**`@subject`が`true`であれば右辺は評価されません。**

```rb
  def subject
    @subject || (@subject = BankAccount.new(@starting_balance))
  end
```

## **<i class="fa fa-puzzle-piece"></i> 更に責務を分ける**

さて、だいぶ洗練されてきましたが、現在はまだ**オブジェクト作成機能をProxyが持っている**状態です。
Proxyはなるべく番人、代理人出会って欲しいので、言ってしまえばサブジェクトへの橋渡しが最も大事な業務です。余分な業務を持つ必要もないでしょう。

ここでまたRubyのコードブロックを使ってみましょう。

```rb

class VirtualAccountproxy
  def initialize(&creation_block)
    @creation_block = creation_block
  end

  def subject
    @subject || (@subject = @creation_block.call)
  end
end
```

こうすることで　Proxyがかなり洗練されました。持っている機能は、セキュリティ機能とサブジェクトへの橋渡しです。
実行は、ブロックを渡すだけです

```rb
account =  VirtualAccountProxy.new { BankAccount.new(100) }
requested_account = account.subject # => BankAccountオブジェクト

requested_account.deposit(1000)
requested_account.withdraw(500)
```

## **<i class="fa fa-diamond"></i> Rubyらしくしてみましょう！**

お待たせしました。Ruby感溢れるコードにしていきましょう。

ここではObjectクラスで定義されている`method_missing`メソッドを使います。

まずはお試しコードから...

```rb
class TestMethodMissing
  def hello
    puts ("hello from original method")
  end

  def method_missing(name, *args)
    puts ("Warning! unknown method error: #{name}")
    puts ("Arguments: #{args.join(' ')}")
  end
end

tmm = TestMethodMissing.new
tmm.hello # => "hello from original method"

tmm.good_by(10000) # => warning, unknown method error: good_by Arguments: 10000
```
`method_missing`はObjectクラスで定義されているものですが、こういった形でオーバーライドできます。
Rubyのクラス継承ツリーから行くと、TestMethodMissingも親であるObjectの子クラスなので、結果として`method_missing`をオーバーライドしていることになります。
逆に、メソッドを呼び出して、その名前のメソッドが見つからなければ常にObjectクラスの`method_missing`が呼び出されることになります。

```rb

class AccountProtectionProxy
  def initialize(real_account, owner_name)
    @subject = real_account
    @owner_name = owner_name
  end

  def method_missing(name, *args)
    check_access
    @subject.send(name, *args)
  end

  def check_access
    if Etc.getlogin != @owner_name
      raise "Illegal access: #{Etc.getlogin} cannot access account"
    end
  end
end
```
これで、当初求めていた防御Proxyの完全な実装になります。
`method_missing`使うことで、セキュリティ認証機能付きの、どんなメソッドでも受け付けるProtectionProxyが完成しました！

## <i class="fa fa-check-circle-o"></i> 実行時に何が起きているか？
`AccountProtectionProxy`にないメソッドが実行されれば、
 
1. `check_access`
2. `subject.send(name, *args)`

の順にメソッドが実行され、2でメソッドが見つからなければ本来のObjectクラスの`method_missing`が実行されます。

なので、このProxyは事実上どんなメソッド受け入れることができます。


# **<i class="fa fa-star"></i>　まとめ**


### <i class="fa fa-paw"></i>　Proxyパターンで解決できる３つのこと
1. オブジェクトへのアクセス制御
2. 場所に依存しないオブジェクトの取得法の提供
3. オブジェクト生成の遅延

うまく使えば、非常に柔軟な設計ができそうですね！