Proxyパターン (1) - アクセス制御Proxy
『Rubyによるデザインパターン』(ラス・オルセン著 ピアソン・エデュケーション刊)の例をPythonに変換して書いています。目次
- オブジェクトへのアクセス制限
- オブジェクトの実際の場所に依存しないアクセス
- オブジェクトの生成の遅延
という、3つの一見関係が無い問題の、共通の解決策がProxyパターンです。
たとえば、BankAccount(銀行口座)クラスを作っとします。
これを使えば、銀行業務の全てがユーザー端末上から行えますが、
今更
「特定のユーザーしか口座にアクセスできないようにする」
「実際には、BankAccountオブジェクトは(端末ではなく)サーバー上になければならない」
「パフォーマンスの問題上、オブジェクトの生成を出来るだけ遅らせたい」
という要望を実現しなければならない事になりました。
まずは、アクセス制御も何もしないProxyを作ります。
class BankAccount(object): """「銀行口座」クラス""" def __init__(self, starting_balance=0): self._balance = starting_balance def deposit(self, amount): self._balance += amount def withdraw(self, amount): self._balance -= amount def balance(self): return self._balance class BankAccountProxy(object): """「銀行口座」クラス""" def __init__(self, real_object): self.real_object = real_object def deposit(self, *a, **kw): self.real_object.deposit(*a, **kw) def withdraw(self, *a, **kw): self.real_object.withdraw(*a, **kw) def balance(self, *a, **kw): self.real_object.balance(*a, **kw) if __name__ == "__main__": account = BankAccount(100) account.deposit(50) account.withdraw(10) proxy = BankAccountProxy(account) proxy.deposit(50) proxy.withdraw(10)
accountを直接扱うのと、全く同様に操作できます。
さて、アクセス制御付きのProxyを作ってみましょう。
class IllegalAccessError(StandardError): """アクセス権限が無いのにアクセスしたときの例外""" class BankAccountProtectionProxy(object): """「銀行口座」クラスのアクセス制御付きProxy""" def __init__(self, real_object, owner_name): self.real_object = real_object self.owner_name = owner_name def check_access(self): """ユーザーにアクセス権が無い時、例外を送出する""" if self.owner_name != "doloop": raise IllegalAccessError("%r cannot access amount."%(self.owner_name,)) def deposit(self, *a, **kw): self.check_access() return self.real_object.deposit(*a, **kw) def withdraw(self, *a, **kw): self.check_access() return self.real_object.withdraw(*a, **kw) def balance(self, *a, **kw): self.check_access() return self.real_object.balance(*a, **kw) if __name__ == "__main__": account = BankAccount(100) proxy = BankAccountProtectionProxy(account, "doloop") proxy.deposit(50) proxy.withdraw(10) proxy2 = BankAccountProtectionProxy(account, "hogera") proxy2.deposit(50) #=> IllegalAccessError: 'hogera' cannot access amount. proxy2.withdraw(10)
メソッド呼び出しを、
BankAccountProxyでは、ただ単にself.real_objectに丸投げしていましたが、
BankAccountProtectionProxyでは、self.check_access()を呼び出してから、丸投げしています。
「口座管理」と「アクセス制御」の機能を、
それぞれBankAccountとBankAccountProtectionProxyに分離する事で、
一つのオブジェクトで同時に扱うのに比べて、見通しが良くなります。
つづく