目標:Python版・Ruby版のデザインパターンを作る
(PythonやRubyは型指定がないのでjavaやC#と比べるとかなり毛色が違いそう。。。)
多くのパターンが型指定が必要なjavaで修正しなくてよいという前提で構成されている(左記目的の場合にPython, Rubyでは不要な場合が多い)
デザインパターンを学ぶ目的
- 変更に強い(変更箇所が少ない?)プログラムを作成できる
- 再利用ができる
- バグが少ないプログラムが作成できる
- クラス生成用のクラスを作成する
- 抽象クラスを作成して具体クラスを追加していく
- クラスの生成を保証する
- クラスの入替が必要最低限で済む(ファクトリークラス生成用の引数と、新規ファクトリークラス)
- 同種のクラスが多い場合(RPGのキャラクターとか??)
- 鍋クラスを作成する
- 要素の保持・演算用クラス(Builderクラス)と構築用クラス(Directorクラス)を別々に作成する
- Builderはインターフェースとして用意してそれを継承した要素・演算用クラスとする
- 異なる要素だが同じ過程で作る(食塩水と砂糖水とか??)場合にDirectorクラスを再利用できる
- 食塩水を作成する
- クラス生成をメソッドで実施する(直接コンストラクタを呼ばない)
- 継承側で生成するインスタンスを決定したい場合(メソッドをオーバーライドする)
- 版材(cuttableクラス:芋、木等)を使って加工する(cutprintクラス:書く、切る、プリントする)
- プロトタイプ(インスタンス)からインスタンスを作成できるようにするパターン
- 複数のインスタンス生成後に同じかつ時間のかかる処理を複数適用する場合(一個作成しておいてコピーする)
- オブジェクト毎コピーするメソッドは既に用意されていそうだがそれとどう違うのかな?
- あるクラスの生成インスタンスが唯一であることを保証するパターン
- インスタンスが唯一であることを保証したい場合に使用する
- 図書館貸出の際の本が1つである?
-
Pythonのコードは少し特殊
Pythonコードの大体の流れ __init__の前にnewが呼び出されるのでnewの時点でraiseする getinstance時にinternalnewでnewをラッパーする 結果:通常インスタンス生成時はnewの例外がraiseされる 結果:getintance時にuniqueinstanceが無ければ生成しあれば生成しない 上記でinstanceの唯一性が確保される class Singleton: _unique_instance = None ## 1. get_instance時に__init__が呼び出されるので ## その前にraiseする def __new__(cls): raise NotImplementedError('Cannot initialize via Constructor') ## get_instance時に呼び出されるnewをwrapperする @classmethod def __internal_new__(cls): return super().__new__(cls) ## _unique_instanceが存在しない場合のみ生成を許可する @classmethod def get_instance(cls): if not cls._unique_instance: cls._unique_instance = cls.__internal_new__() # 変更 return cls._unique_instance
- 実装の中身は異なるが抽象概念(removeやadd等)が同一の場合に同一のインスタンスとして操作できる
呼び出し側がインスタンスを区別せずに利用できる
ディレクトリ・ファイル管理(シンボリックリンク)
- 機能を一部追加・変更する(javaではインターフェースを実装してオーバーライド)
- 継承では多重継承禁止等により機能追加・変更は限定的となるがインターフェースでは制約を受けないので柔軟に拡張することが可能
バニラアイスクラスの名前取得をカシューナッツバニラアイスクラスで名前取得メソッドを拡張する
- 複数のクラスから成る処理を代表として受け付ける(窓口)クラスを作成する
- 複数のクラスを用いて処理を実施する等の処理が複雑化してきた場合に呼び出し側を簡略化するために使用する
図書の検索(本が図書館に存在するか?本が貸し出し中か?の処理を代表クラス内でまとめる)
- メソッドを保有インスタンスに委譲させる(sortメソッドをSortImpleインスタンスに投げる)
- 抽象クラスから継承されている具体メソッドを再記述することなく利用したい場合
- sortメソッドをSortImpleインスタンスに委譲する
- インターフェースに互換のないクラス同士をつなげる(継承と委譲)
-
あるクラスが持つメソッドを他用途で利用したい。そのときインターフェースを統一したい場合に使用する(前提:既存のクラスは変更したくない)
例1 : 利用したいメソッドを持つクラスを継承した新しいクラスを作成し、インターフェースを実装する
例2 : 利用したいメソッドをインスタンスとして持つクラスを作成してインターフェースを実装する
- 同じインスタンスを共有させて無駄なインスタンス生成を防ぐ
- 同じインスタンスを共有できる状況で使用する。
- クラス生成用クラスのFactoryクラスを作成(Singleton)してインスタンスを共有する
- 同じ継承先のクラス同士で一部メソッドを利用(代理)する。
代理で一部他のメソッドを利用したい場合に使用する。
- クラスAを継承したクラスB及びクラスCで、クラスCがクラスBの一部メソッドを利用したい場合、クラスBのインスタンスをクラスCに所有させて、メソッドを呼び出す。
主要なメソッドをクラスとして生成する。
- 主要メソッドが頻繁に変更される可能性が高い場合に拡張するだけで使用可能
- 飽和食塩水の実験( 水を入れる、塩を足す等のステータスを変更するメソッドは定義しておき、飽和水溶液を作る:水を徐々に足していく等 といったメソッドはコマンドクラスとして定義する )
解析した結果を得られた手順に則って実現するパターン
カップラーメンの作り方
- 粉末スープ + 麺
- お湯を注ぐ(足す)
- 3分待つ
- 液体スープを入れる
メソッドをクラスで分離しておくことで呼び出し側で選択できるようにする
- クラスに同一の目的を持つ複数種類のメソッドを実装する場合に、複雑化を防ぐために使用する
- 2つのHumanClassで体重や身長を比較する
- 抽象クラスで処理の枠組み(描く、切る、プリントする等)を決めること(サブクラスで実装する)
- 必要なメソッド等を親クラスでをあらかじめ定めることができること(このパターンは親クラスが提供する機能そのものが利点)
- 木材に版画をする
- 各種オブジェクトのstateより動作条件等を決める場合に、stateを持つクラスと動作条件等を管理するMediatorクラスを用いる
- 動作条件等の変更頻度が高いものを別クラスで一括管理することで変更に強い設計にする。
Widgetの動作条件を管理(ラジオボタン1, ラジオボタン2がselectされている場合にボタンを押せる)