参考
@kidach1 さんの投稿をPythonに書き換えてるだけです。
@kidach1 さん、いつもありがとうございます。
https://qiita.com/kidach1/items/4b63de9ad5a97726c50c
概要
改めて基本を学ぶ。
参考「Rubyによるデザインパターン第1章」→この投稿はPython
デザインパターンとは
- プログラミングにおいて繰り返し現れる問題に対する、適切解のパターン。
- 無駄無く設計されたオブジェクト指向プログラムの実現をサポート。
パターンとしてカタログ化されていることで
車輪の再発明を防ぐ
デザインパターンの根底にある5つの考え
- 変わるものを変わらないものから分離する
- プログラムはインターフェイスに対して行う(実装に対して行わない)
- 継承より集約
- 委譲、委譲、委譲
- 必要になるまで作るな(YAGNI)
変わるものを変わらないものから分離する
ソフトウェアの仕様には必ず変更が加わるもの。
変わるものと変わらないものを分離しておくことで、
「仕様の変更」に対して「システムの変更」を出来る限り局所的にする。
プログラムはインターフェイスに対して行う(実装に対して行わない)
可能な限り「一般的・抽象的なもの」に対してプログラミングすること。
(ここで言うインターフェイスは、Javaの組み込み構文としてのインターフェイスではなく、
より広いレベルで「抽象度を高めたもの」を意味する。)
これにより、全体のコードの結合度を下げる。
具体性が高く、密結合なコード
if ( is_car() ):
my_car = Car()
my_car.drive(200)
else:
my_plane = AirPlane()
my_plane.fly(200)
乗り物が増える度にコード全体に変更が必要(変更に弱い)。
抽象度が高く、疎結合なコード
my_vehicle = get_vehicle()
my_vehicle.travel(200)
乗り物の数が増えても本コードに変更を加える必要はない(変更に強い)。
継承より集約
継承は望ましくないつながりを作ってしまう。
具体的には
- サブクラスの振る舞いは、スーパークラスの振る舞いに依存する。
- サブクラスからスーパークラスの中身を覗くことが出来る。
class Vehicle:
def start_engine(self):
# エンジンを動かすためのもろもろの処理..
print("engine started")
def stop_engine(self):
# エンジンを止めるためのもろもろの処理..
print("engine stopped")
class Car(Vehicle):
def drive(self):
self.start_engine()
# driving..
self.stop_engine()
car = Car()
car.drive()
Carからエンジンの実装が丸見え
エンジンを使用しない乗り物を作りたい場合は大改造が必要
→変わりやすい部分(Engine)を変わりにくい部分(Vehicle)から分離できていない。
代替案
集約を使う。
つまり、
オブジェクトに、「他のオブジェクトに対する参照」を持たせる。
オブジェクトが何かの一種である(is-a-kind-of)関係は避けて、
何かを持っている(has-a)関係にする。
Car has a Engineの例
class Car:
def __init__(self):
self.engine = Engine()
def drive(self):
self.engine.start()
# driving..
self.engine.stop()
class Engine:
def start(self):
print ("engine started")
def stop(self):
print ("engine stopped")
car = Car()
car.drive()
これでEngineがVehicleから分離され、またカプセル化された。
これにより、Engineの手軽な切り替えが可能に。
class Car:
def __init__(self):
self.engine = GasolineEngine()
def drive(self):
self.engine.start()
# ガソリンエンジンでドライブ..
self.switch_to_diesel()
# ディーゼルエンジンでドライブ..
self.engine.stop()
def switch_to_diesel(self):
self.engine = DieselEngine()
委譲、委譲、委譲
委譲(delegation)
(継承パターンと同じように)start_engineとstop_engineを
クラスの外に公開したいこともある。
そんな場合でも、処理の実態はEngineクラスに任せてしまう。
class Car:
def __init__(self):
self.engine = GasolineEngine()
def drive(self):
self.engine.start()
# driving..
self.engine.stop()
def switch_to_diesel(self):
self.engine = DieselEngine()
def start_engine(self):
self.engine.start() # Engineクラスに任せる
def stop_engine(self):
self.eigine.stop() # Engineクラスに任せる
これで「継承より集約」を実現しつつ、継承時と同じ機能を持つクラスが作成できた。
つまり、
集約と委譲の組み合わせは、強力かつ柔軟な継承の代替手段。
※ start_engineやstop_engineは委譲のために書かなければならない無駄なコードに思えるが、
method_missing等の利用で解決できる。
これはProxyパターンで別途詳細する。
必要になるまで作るな(YAGNI/You Ain't Gonna Need It.)
「将来使うかも」は、大抵使わない。
いたずらに柔軟性を持たせようとオーバーエンジニアリングして
コードの複雑性を増していては、本末転倒。
デザインパターン自身は目的ではない。
問題を解決し、目的を達成するための手段として役立てるべきであり、それ以上のものではない。
デザインパターンを考える上での戒め。
以上5原則を前提に
各パターンを見ていく。
【Template Method】-テンプレは準備した、あとはお好きに-
https://qiita.com/kotetsu75/items/2900eac0fa24f09f775b
【Strategy】-取り替え可能パーツ群を戦略的に利用せよ-
https://qiita.com/kotetsu75/items/186af7006b44703c0379
【Observer】-本日のニュースをお届けします-
https://qiita.com/kotetsu75/items/719a8fc2e4cc7789c1b4
【Composite】 -世界は再帰的(部分は全体、全体は部分)-
制作中
【Iterator】-君の子供たちに伝えたいのだけど-
制作中