pythonによるデザインパターン[Prototype]
# Intro
この文章は結城 浩さんの「Java言語で学ぶデザインパターン入門」を、pythonで実装してみたサンプルコードです。
筆者の環境は以下の通りです。Python 3.6.3
まだ修行中の身なので間違いがあると思いますがご了承ください。 今回は「Prototype」です
# Explanation
本書のp67によると、 Prototypeとは
クラスからインスタンスを作成するのではなく、インスタンスから別のインスタンスを作り出す
とあります。
インスタンスからインスタンスをコピーして使いたい時はどのような時でしょうか?本書p66に以下のような時にインスタンスからインスタンスを生成するメリットがあると書いてあります。
- 種類が多すぎてクラスにまとめられない時(後ほど具体例)
2. クラスからのインスタンス生成が難しい時
3. フレームワークと生成するインスタンスを分けたい時
# Practice
## Specification
与えられた文字列を枠線で囲って表示したり、下線を引いて表示します。今回は`Hello World`が与えられたら
~ Hello World ~
~~~~~~~~~~~~~~~***************
* Hello World *
***************\\\\\\\\\\\\\\\
\ Hello World \
\\\\\\\\\\\\\\\
のように3種類の方法で、classを用いて表示します。
素直に上からclass Under, class Box1, class Box2とクラスを定義をすると、コードは以下のようになります。
class Under:
def __init__(self, deco):
..... def display(self, ...):
....
class Box1:
def __init__(self, deco):
..... def display(self, ...):
....
class Box2:
def __init__(self, deco):
..... def display(self, ...):
....
この方法では一つの表示方法につき一つのクラスが必要で、表示方法が無数にあると前述の通り「1. 種類が多すぎてクラスにまとめられなく」なります。下線を引く表示の仕方と枠線で囲って表示する仕方(class Under と class Box)とでは、classは分けなければいけません。しかし、`*`で枠線で囲って表示する仕方と`\`で枠線で囲って表示する仕方(class Box1 と class Box2)は纏めれそうです。
そこでclass Managerを作り、そこにshowcaseという辞書型のattributeを定義します。また下線を引く表示の仕方をclass UnderLinePenとして、枠線で囲って表示する仕方をclass MassageBoxとして定義します。辞書型のshowcaseにそれぞれの表示の仕方について、class UnderLinePen もしくは class MassageBox のインスタンスをコピーしていきます。これによって枠線で囲って表示する仕方を一つのclassに纏めることができます。
特定の表示方法を用いる時は、showcaseから引っ張ってきてきます。この時渡されるのはインスタンスですが、これはshowcaseに代入した時のインスタンスのコピーであることに注意していください。
一方、class MassageBox と class UnserLinePen は同じような関数を持っているので、Productというinterface(pythonの場合はabstract class)に書き出しましょう。これによって「3. フレームワークと生成するインスタンスを分け」ることができ、ManagerはProductという抽象クラスのみを扱うので、具体的にどのように実装しているかはManager側は気にしなくてもよくなります。
## Sample Code
## Explanation
重要なのは def create_clone
です。
def create_clone(self):
return copy.deepcopy(self)
deep copyをすることで、インスタンスからインスタンスを生成することができます。
あとは上で述べたようにshowcaseという辞書を定義することで、目的のインスタンスを取り出すことができます。もちろん、取り出してきたインスタンスは元のコピーなので、以下のコードはFalseになります。
mbox = MessageBox('*')
manager.register('warning box', mbox)
p1 = manager.create('warning box')print(mbox == p1) # False
# Conclusion
結局のところ重要なのはdeep copyです。むしろ、他のサイトでpythonのprototype patternの方法を探してみると、deep copy1行で済ましているところもありました。
prototypeを使って、無駄な(冗長に)クラスを増やしていくことを避けたいですね。
# ref
- 「増補改訂版 Java言語で学ぶデザインパターン入門」 結城浩(著)第11版
- 「8.10. copy — Shallow and deep copy operations — Python 3.6.4 documentation」https://docs.python.org/3.6/library/copy.html