Movatterモバイル変換


[0]ホーム

URL:


酒と泪とRubyとRailsと

プロキシ(Proxy) | Ruby デザインパターン


GoFのデザインパターン(Design Pattern)のプロキシ(プロキシ)をRubyのサンプルコードで紹介します。

プロキシパターンは1つのオブジェクトに複数の関心ことがある場合にそれを分離するために用います。たとえば、オブジェクトの本質的な目的とは異なる「セキュリティ要件やトランザクション管理など」を切り離して実装できます。

サッカーが専門の「サッカー選手」と、チームとの交渉や契約が専門の「代理人」のような関係です。

😸Proxyの構成要素

プロキシの構成要素は次の2つです。

  • 対象オブジェクト(subject):本物のオブジェクト
  • 代理サブジェクト(proxy):特定の「関心事」を担当、それ以外を対象サブジェクトに渡す

プロキシオブジェクトは対象オブジェクトと同じインタフェースを持ちます。利用する際は、プロキシオブジェクトを通して対象となるオブジェクトを操作します。

🐮プロキシの3つのタイプ

プロキシには次の3つの種類があります。

  • 防御Proxy
  • 仮想Proxy
  • リモートProxy

今回は、防御プロキシと仮想プロキシについてサンプルソースで説明していきます。

🐝サンプルソース1:防御プロキシ

このサンプルでは銀行の窓口業務(入金/出金)を担当するBankAccountクラスと、ユーザー認証を担当するBankAccountProxyクラスにより「関心ことを分離」するプロキシデザインパターンのモデルを作ります。

まず銀行の入出金の窓口業務を行うBankAccountクラスを作成します。

# 銀行の入出金業務を行う(対象オブジェクト/subject)
classBankAccount
attr_reader:balance

definitialize(balance)
@balance = balance
end

# 出金
defdeposit(amount)
@balance += amount
end

# 入金
defwithdraw(amount)
@balance -= amount
end
end

次に銀行の入出金業務とは関心ことの異なるユーザー認証を担当する防御プロキシとしてBankAccountProxyクラスを作ります。このクラスはBankAccountクラスと同じインタフェースを持っており、利用する側はプロキシを介して入出金を行います。

# etcはRubyの標準ライブラリで、/etc に存在するデータベースから情報を得る
# この場合は、ログインユーザー名を取得するために使う
require"etc"

# ユーザーログインを担当する防御Proxy
classBankAccountProxy
definitialize(real_object, owner_name)
@real_object = real_object
@owner_name = owner_name
end

defbalance
check_access
@real_object.balance
end

defdeposit(amount)
check_access
@real_object.deposit(amount)
end

defwithdraw(amount)
check_access
@real_object.withdraw(amount)
end

defcheck_access
if(Etc.getlogin != @owner_name)
raise"Illegal access:#{@owner_name} cannot access account."
end
end
end

実際に動かしてみます。

# ログインユーザーの場合
account = BankAccount.new(100)
# login_userの部分はこの処理を行うMac/Linuxのログイン中のユーザー名に書き換えて下さい
proxy = BankAccountProxy.new(account,"login_user")
puts proxy.deposit(50)
#=> 150
puts proxy.withdraw(10)
#=> 140

# ログインユーザーではない場合
account = BankAccount.new(100)
proxy = BankAccountProxy.new(account,"no_login_user")
puts proxy.deposit(50)
#`check_access': Illegal access: no_login_user cannot access account. (RuntimeError)

このようにユーザー認証という「特定の関心事」を代理オブジェクトに分離させることができました。

🚕仮想プロキシ

次に仮想プロキシのサンプルです。今回は、先ほどの入出金を行うBankAccountクラスと、BankAccountのインスタンス生成を遅らせるためのVirtualAccountProxyクラスを作成します。インスタンスの生成を遅らせる理由はここではシステム全体の性能向上と仮定します。

まずさきほどと同じ入出金業務のBankAccountクラスです。

# 銀行の入出金業務を行う(対象オブジェクト/subject)
classBankAccount
attr_reader:balance

definitialize(balance)
puts"BankAccountを生成しました"
@balance = balance
end

defdeposit(amount)
@balance += amount
end

defwithdraw(amount)
@balance -= amount
end
end

次にシステム全体の性能向上を目的としてBankAccountクラスの生成を遅らせる仮想プロキシとして、VirtualAccountProxyクラスを作成します。

# BankAccountの生成を遅らせる仮想Proxy
classVirtualAccountProxy
definitialize(starting_balance)
puts"VirtualAccountPoxyを生成しました。BankAccountはまだ生成していません。"
@starting_balance = starting_balance
end

defbalance
subject.balance
end

defdeposit(amount)
subject.deposit(amount)
end

defwithdraw(amount)
subject.withdraw(amount)
end

defannounce
"Virtual Account Proxyが担当するアナウンスです"
end

defsubject
@subject|| (@subject = BankAccount.new(@starting_balance))
end
end

コーディング以上となります。ではこのサンプルを動かしてみます。

proxy = VirtualAccountProxy.new(100)
#=> VirtualAccountPoxyを生成しました。BankAccountはまだ生成していません。

puts proxy.announce
#=> Virtual Account Proxyが担当するアナウンスです

puts proxy.deposit(50)
#=> BankAccountを生成しました
#=> 150

puts proxy.withdraw(10)
#=> 140

結果のようにdepositメソッドを実行するまで、BankAccountクラスが生成されないようになりました。この例では、VirtualAccountProxyが「BankAccountインスタンスの生成タイミング」という関心ことを分離しています。

🏀Tips:method_missingによる委譲

ここでRubyの標準機能のひとつ、method_missingを使ったプロキシを紹介します。

Rubyでは未定義のメソッドが呼び出された場合にmethod_missingが呼び出されます。これを利用することでさきほどの防御プロキシのBankAccountProxyクラスを次のように短くできます。

# ユーザーログインを担当する防御Proxy
classBankAccountProxy
definitialize(real_object, owner_name)
@real_object = real_object
@owner_name = owner_name
end

# Rubyでは未定義のメソッド呼び出しが発生 => method_missingが呼び出される
# method_missingを用いることでdeposit, withdrawを省略
defmethod_missing(name, *args)
check_access
@real_object.send(name, *args)
end

defcheck_access
if(Etc.getlogin != @owner_name)
raise"Illegal access:#{@owner_name} cannot access account."
end
end
end

BankAccountProxyクラスに存在しない#depositメソッド、#withdrawが呼び出された場合、#method_missingが呼ばれます。そして、#method_missing内で@real_objectに格納したオブジェクトの同名のメソッドが呼び出されます。

この方法はForwardableと同じ「委譲」のやり方のひとつです。

ただし、method_missingの利用には次のようなデメリットもあります。このデメリットをしっかり理解したうえで、利用することが大切です。

  • ソースコードが追いづらくなる
  • マシンパワーを消費する

😀サンプルソース

😎参考リンク

🖥 VULTRおすすめ

VULTR」はVPSサーバのサービスです。日本にリージョンがあり、最安は512MBで2.5ドル/月($0.004/時間)で借りることができます。4GBメモリでも月20ドルです。 最近はVULTRのヘビーユーザーになので、「ここ」から会員登録してもらえるとサービス開発が捗ります!

📚 おすすめの書籍

Rails/Modelバリデーションコールバック変更前後の値の確認値の保存・更新アソシエーション削除メソッドSQLの実行日付カラムの設定便利なDB関連設定読込み専用(Read Only)Concern(共通処理)クエリTipsRails/Controllerrenderレシピ集HTTPステータスシンボルIPの取得についてStrong ParametersControllerのTipsRails/ViewHAMLチートシート画像アップロードフォームS3への画像アップロードForm Objectフォームヘルパー検索可能なselectタグenum - selectタグシンプルな検索ボックス別ウィンドウ・フォームRailsいろいろi18nについてRails.cacheメール送信ロギングCapistrano3でデプロイセッション管理環境ごとのYAML設定読込xx分前の時間表記Tipsいろいろ便利ライブラリ簡単Webmock:VCRN+1対策:bullet環境変数の管理: dotenvi18nサポート: i18n-tasksLDAP:net-ldapヘッダ:secureheadersrack-mini-profileractiverecord-precountカバレッジ計測パンくずリスト+MetadataRSpecの記法自動修正アプリランキング取得Unicorn導入手順unicorn-worker-killerRSpecController SpecCapybaraとヘッドレスChromeRequest SpecAPI認証 Spec便利Tipsデザインパターン概要アブストラクトファクトリビルダファクトリメソッドアダプタコンポジットデコレータプロキシコマンドインタプリタイテレータオブザーバストラテジテンプレートメソッドRuby基礎文字列/String配列/Arrayハッシュ/Hash構造体/StructSpawn(コマンド実行)BundlerについてTipsRails 5.1へのアップデートRails 5へのアップデートrbenvバージョンアップpumaをsystemdで動かすURI.encodeとCGI.escapeURLのパラメータを変更処理時間を計測画像JPEG/PNG/GIF判定トラブルシュートNo route matches assets

[8]ページ先頭

©2009-2025 Movatter.jp