設計を意識したコードが書けるようになる為に、デザインパターン修行しました。
他のDesign Patternもちょくちょく出していきます。
前置き
- 増補改訂版Java言語で学ぶデザインパターン入門をJavaからPythonにしてます。(Pythonは3.4.2)
- githubにコード置いてあります(まだ動かないものもある)
デザインパターンをどういう時に、何を、どう使うのかを理解することが一先ずの目標。
(Javaというか静的型付言語は初めてで、且つpython歴もそんなに長くないので、Pythonistaぽっくないところがあると思います。ご指摘ございましたらご教授ください。)
まず、そもそもデザインパターンってどういうものかってとこから。
デザインパターンとは
ソフトウェア開発におけるデザインパターン(型紙(かたがみ)または設計パターン、英: design pattern)とは、過去のソフトウェア設計者が発見し編み出した設計ノウハウを蓄積し、名前をつけ、再利用しやすいように特定の規約に従ってカタログ化したものである。
要は、温故知新てことですね。
先人の使えるノウハウが使えるなら使いましょうってことね。
じゃあ、そのカタログにはどんな種類があるの?
- 生成に関するパターン(Creational patterns)
- 構造に関するパターン(Structural patterns)
- 振る舞いに関するパターン(Behavioral patterns)
- 並行処理制御に関するパターン(Concurrency patterns)
並行処理制御は初見だった。
他のは存在は知っていたけど、調べると興味深い。
そもそも、デザインパターンって何が美味しいんだ??って疑問が浮かぶので調べると・・・
- 初期の開発や、調査的な開発には先行事例としてお手本になって、開発効率が良い
- 後に大きな障害となる、軽微な問題(バグになりやすいコード)を発生しにくくする
- デザインパターンに精通している人には、コード自体の可読性が上がる
- ソフトウェア設計の堅牢性が増す
- 想定される仕様の追加や変更に柔軟に対応できる
言い過ぎてるような気もしたけど、納得はした。
(実際にデザインパターンは必ずしも最適解になるとは限らないと注釈があったし)
じゃあ、よく使いそうなもの、気になったものをピックアップしてPythonで書きます。
1回目は生成に関するパターンSingleton。一応、クラス図と説明の図を入れておく。
Singletonとは
唯一の存在を保証するためのパターン、このクラスのインスタンスはたった1つしか作らないし、作りたくないという時に使う。
つまり、ルールは
- 指定したクラスのインスタンスが絶対に1個しか存在しないことを保証したい
- インスタンスが1個しか存在しないことをプログラム上で表現したい
- Singleton パターンは、コンストラクタを private(外部参照させない)
- 同じ型のインスタンスが private なクラス変数として定義されている
- 同じ型のインスタンスを返すクラス関数が定義されている(今回はget_name())
アプリケーションの全てのリクエストを通して常に一つである場合とか
、オブジェクト自体が大きいものやパフォーマンスコストが高いものをSingletonにすると良さそう。
class Singleton(object):
__instance = None
def __new__(cls, *args, **keys):
if cls.__instance is None:
cls.__instance = object.__new__(cls)
return cls.__instance
def __init__(self, name):
self.__name = name
def get_name(self):
return self.__name
def main():
s1 = Singleton('aaa')
s2 = Singleton('bbb')
if s1 is s2:
print(id(s1), id(s2)) #=> 4444873840 4444873840
print(s1.get_name(), s2.get_name()) #=> bbb bbb
else:
print('s1 is NOT s2')
if __name__ == '__main__':
main()
まとめ
一意のオブジェクトが出来た(同じインスタンスが得られている)
それは、1つのクラス定義に対して、クラスオブジェクトは1つだけ存在し、インスタンスオブジェクトは生成したインスタンスの数だけ存在するが、2回目の呼び出しでは、1回目の既存のインスタンスがあるので、新たにインスタンスを作ることを行っていないから。
Githubのrepositoryには入れてないけど、superを使っても出来るみたい。
class MySingleton():
_instance = None
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = super(MySingleton, cls).__new__(cls, *args, **kwargs)
return cls._instance
def set_name(self, name):
self._name = name
return self._name
def get_name(self):
return self._name
from my_singleton import MySingleton
if __name__ == "__main__":
my = MySingleton()
my2 = MySingleton()
my.set_name("a")
my2.set_name("b")
print(id(my), id(my2)) # => 3074264748 3074264748
print(my.get_name(), my2.get_name()) # => b b
print(my is my2) # => True
でも、__new__を使うと既存オブジェクトを返してるけど、その度に__init__が何度も呼ばれはずだから、あんまし効率良くないのかもしれない。(だから__call__を使ってクラスのインスタンスを呼び出せるようにしている例もあるのかな)
Pythonのメタクラスも難しいなぁ。
参考
- [__new__とか__init__とかを再整理]
(http://qiita.com/kou_tana77/items/75a13a21576f2dabfeca) - [Python の new や init はコンストラクタではない]
(http://www.sakito.com/2012/10/python-new-init.html) - クラスについてちょっと調べてみた
- Pythonのクラスメンバのスコープまとめ
- Difference between _, __ and xx in Python
- The Singleton — Python 3 Patterns, Recipes and Idioms
- Python入門 - クラス アクセス制限
- Pythonによるデザインパターン(目次)
- 9.6. プライベート変数
- pythonにprivate変数がない件
- Wikipedia Software design pattern
- TECHSCORE デザインパターン
- Wikipedia デザインパターン