GoFのデザインパターン(Design Pattern)のプロキシ(プロキシ)をRubyのサンプルコードで紹介します。
プロキシパターンは1つのオブジェクトに複数の関心ことがある場合にそれを分離するために用います。たとえば、オブジェクトの本質的な目的とは異なる「セキュリティ要件やトランザクション管理など」を切り離して実装できます。
サッカーが専門の「サッカー選手」と、チームとの交渉や契約が専門の「代理人」のような関係です。
😸 Proxyの構成要素
プロキシの構成要素は次の2つです。
- 対象オブジェクト(subject):本物のオブジェクト
- 代理サブジェクト(proxy):特定の「関心事」を担当、それ以外を対象サブジェクトに渡す
プロキシオブジェクトは対象オブジェクトと同じインタフェースを持ちます。利用する際は、プロキシオブジェクトを通して対象となるオブジェクトを操作します。
🐮 プロキシの3つのタイプ
プロキシには次の3つの種類があります。
- 防御Proxy
- 仮想Proxy
- リモートProxy
今回は、防御プロキシと仮想プロキシについてサンプルソースで説明していきます。
🐝 サンプルソース1:防御プロキシ
このサンプルでは銀行の窓口業務(入金/出金)を担当するBankAccount
クラスと、ユーザー認証を担当するBankAccountProxy
クラスにより「関心ことを分離」するプロキシデザインパターンのモデルを作ります。
まず銀行の入出金の窓口業務を行うBankAccount
クラスを作成します。
# 銀行の入出金業務を行う(対象オブジェクト/subject) |
次に銀行の入出金業務とは関心ことの異なるユーザー認証を担当する防御プロキシとしてBankAccountProxy
クラスを作ります。このクラスはBankAccount
クラスと同じインタフェースを持っており、利用する側はプロキシを介して入出金を行います。
# etcはRubyの標準ライブラリで、/etc に存在するデータベースから情報を得る |
実際に動かしてみます。
# ログインユーザーの場合 |
このようにユーザー認証という「特定の関心事」を代理オブジェクトに分離させることができました。
🚕 仮想プロキシ
次に仮想プロキシのサンプルです。今回は、先ほどの入出金を行うBankAccount
クラスと、BankAccountのインスタンス生成を遅らせるためのVirtualAccountProxy
クラスを作成します。インスタンスの生成を遅らせる理由はここではシステム全体の性能向上と仮定します。
まずさきほどと同じ入出金業務のBankAccount
クラスです。
# 銀行の入出金業務を行う(対象オブジェクト/subject) |
次にシステム全体の性能向上を目的としてBankAccount
クラスの生成を遅らせる仮想プロキシとして、VirtualAccountProxy
クラスを作成します。
# BankAccountの生成を遅らせる仮想Proxy |
コーディング以上となります。ではこのサンプルを動かしてみます。
proxy = VirtualAccountProxy.new(100) |
結果のようにdeposit
メソッドを実行するまで、BankAccount
クラスが生成されないようになりました。この例では、VirtualAccountProxyが「BankAccountインスタンスの生成タイミング」という関心ことを分離しています。
🏀 Tips:method_missingによる委譲
ここでRubyの標準機能のひとつ、method_missing
を使ったプロキシを紹介します。
Rubyでは未定義のメソッドが呼び出された場合にmethod_missing
が呼び出されます。これを利用することでさきほどの防御プロキシのBankAccountProxyクラス
を次のように短くできます。
# ユーザーログインを担当する防御Proxy |
BankAccountProxy
クラスに存在しない#deposit
メソッド、#withdraw
が呼び出された場合、#method_missing
が呼ばれます。そして、#method_missing
内で@real_objectに格納したオブジェクトの同名のメソッドが呼び出されます。
この方法はForwardable
と同じ「委譲」のやり方のひとつです。
ただし、method_missingの利用には次のようなデメリットもあります。このデメリットをしっかり理解したうえで、利用することが大切です。
- ソースコードが追いづらくなる
- マシンパワーを消費する