pythonによるデザインパターン[Proxy]
# Intro
この文章は結城 浩さんの「Java言語で学ぶデザインパターン入門」を、pythonで実装してみたサンプルコードです。
筆者の環境は以下の通りです。Python 3.6.3
まだ修行中の身なので間違いがあると思いますがご了承ください。 今回は「Proxy」です。プロキシサーバーのProxyですね。
# Summarise
本書のp324によると、 Proxyとは
本人オブジェクトの代わりに、代理人(Proxy)オブジェクトが(ある程度)仕事をこなしてやること
とあります。オブジェクトの生成に時間がかかる場合に、代理人(Proxy)オブジェクトが簡単な処理を肩代わりするといった感じですね。
# Practice
## Specification
class Printer
はname
Fieldをもち、以下のメソッドを持ちます。
name
Fieldの値をセットするdef set_printer_name
name
Fieldの値を取得するdef get_printer_name
- 引数に与えられたstringを表示する
def pprint
しかし、class Printer
はインスタンス生成時に5秒も時間がかかってしまいます。
今main関数では、以下のようにclass Printer
のインスタンスを生成し、各メソッドを実行したいです。
現状では
p = Printer('Alice')
をした時に時間がかかってしまうので、簡単な
name
Fieldの値をセットするdef set_printer_name
name
Fieldの値を取得するdef get_printer_name
の処理の時は、代理人(Proxy)のclass PrinterProxy
に任せてしまい、 def pprint
が実行される時に初めてclass Printer
のインスタンスを生成してください。
つまり、main関数を以下のように変更した時に、 def pprint
が実行されるまでは高速に動くようにしてください。
## Explanation
まずclass Printer
とclass PrinterProxy
は同じようなものなので、他クラスから見た時に同一視できるように抽象クラス(class Printable
)にまとめましょう。このクラスは抽象メソッドdef set_printer_name
, def get_printer_name
,def pprint
を持ちます。
もちろんclass Printer
とclass PrinterProxy
は class Printable
を継承します。これによって、class PrinterProxy
は class Printable
が透過的になります。
次に大本命class PrinterProxy
を作成します。
重要なのはself.__real
というFieldです。self.__real
は本当のオブジェクト(今回はclass Printer
)を委譲(delegation)によって格納します。
まずdef set_printer_name
, def get_printer_name
を見てください。これらのメソッドは(if文をのぞいて)class PrinterProxy
の中で完結しています。class Printer
と同じようにclass PrinterProxy
の__name
Fieldをセットしたり、取得したりしています。
一方 def pprint
に注目してください。このメソッド内では別のメソッド def __realize
を呼び出しています。def __realize
では、そう本当のオブジェクトclass Printer
を生成し、self.__real
に格納しているのです。ここで初めて本当のオブジェクトclass Printer
を生成します。そしてself.__real
をセットした後、self.__real
を介してclass Printer
の def pprint
を実行します。
以上のことを踏まえた上でmain関数をもう一度見ましょう。
main関数はclass PrinterProxy
を生成し、ただそのメソッドを扱っているだけです。ここでclass Printer
を使っているという意識をしなくてもいいのです(抽象クラスclass Printabe
を扱っているだけ)。ですが裏ではいつの間にかclass Printer
が生成さているのです。
## Sample Code
python3 proxy.py 名前は現在Aliceです
名前は現在Bobです
Printerのインスタンス(Bob)を作成中.....完了
=== Bob ===
Hello, World
# Conclusion
Proxyパターンによって、重い処理を後回しにすることができました。これは、例えばアプリを起動する時に、重い処理は後回しにして、アプリを立ち上げることに専念することなどに有効です(p332)。また、proxyサーバーも、ホームページのキャッシュを取っておいて、元サーバーにデータを取りに行か(重い処理)なくても、ページを返せるようにしているので、概念としては似ています(実装がどうなっているかは知りません)(p333)。
もしかすると、こんなことをしなくても遅延実行すればいいじゃないか?と思うかもしれません。もちろんそれでもいいのですが、簡単な処理と重い処理を別々のクラスで管理(主体と代理人を分ける)することで、プログラムの部品化が進み、個別に修正を加えることができます(p332)。とはいってもクラスが増えてもめんどくさいので、少しの処理なら遅延実行で実装してもいいと思います。
Proxyを使うことで、簡単な処理を早く実行し、必要になって初めて重い処理を実行できるようになりました。だた一つ思うのは、何か変更点があった時に、抽象クラス・主体のクラス・代理人のクラス全てを変更しなければいけないのはめんどくさいなーと思います🤓🤓🤓
# ref
- 「増補改訂版 Java言語で学ぶデザインパターン入門」 結城浩(著)第11版