pythonによるデザインパターン[Proxy]

# Intro

この文章は結城 浩さんの「Java言語で学ぶデザインパターン入門」を、pythonで実装してみたサンプルコードです。

筆者の環境は以下の通りです。Python 3.6.3

まだ修行中の身なので間違いがあると思いますがご了承ください。 今回は「Proxy」です。プロキシサーバーのProxyですね。

# Summarise

本書のp324によると、 Proxyとは

本人オブジェクトの代わりに、代理人(Proxy)オブジェクトが(ある程度)仕事をこなしてやること

とあります。オブジェクトの生成に時間がかかる場合に、代理人(Proxy)オブジェクトが簡単な処理を肩代わりするといった感じですね。

# Practice

## Specification

class PrinternameFieldをもち、以下のメソッドを持ちます。

  • nameFieldの値をセットする def set_printer_name
  • nameFieldの値を取得する def get_printer_name
  • 引数に与えられたstringを表示する def pprint

しかし、class Printer はインスタンス生成時に5秒も時間がかかってしまいます。

今main関数では、以下のようにclass Printer のインスタンスを生成し、各メソッドを実行したいです。

現状では

p = Printer('Alice')

をした時に時間がかかってしまうので、簡単な

  • nameFieldの値をセットする def set_printer_name
  • nameFieldの値を取得する def get_printer_name

の処理の時は、代理人(Proxy)のclass PrinterProxy に任せてしまい、 def pprint が実行される時に初めてclass Printer のインスタンスを生成してください。

つまり、main関数を以下のように変更した時に、 def pprint が実行されるまでは高速に動くようにしてください。

## Explanation

まずclass Printerclass PrinterProxy は同じようなものなので、他クラスから見た時に同一視できるように抽象クラス(class Printable)にまとめましょう。このクラスは抽象メソッドdef set_printer_name , def get_printer_name ,def pprint を持ちます。

もちろんclass Printerclass PrinterProxyclass Printable を継承します。これによって、class PrinterProxyclass 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 Printerdef 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

--

--