3つの一般的な基本設計パターン
-
作成モード
適切な状況に合わせて、インスタンス化メソッドと対応する作成メソッドを提供します。
-
構造化モデル
エンティティ間の関係を処理するために使用され、これらのエンティティがよりうまく連携できるようにします。
-
行動モード
異なるエンティティ間の通信に使用され、エンティティ間の通信のためのより簡単で柔軟な通信方法を提供します。
詳細モード紹介
作成タイプ
1.ファクトリーメソッド
-
効果
オブジェクトを作成するためのインターフェースを定義し、インスタンス化するクラスをサブクラスに決定させます。ファクトリメソッドは、クラスのインスタンス化をそのサブクラスに遅らせます。
-
該当するシーン
- クラスが作成する必要があるオブジェクトのクラスを知らない場合。
- クラスがそのサブクラスで作成するオブジェクトを指定する必要がある場合。
-
サンプルデモ
class ChinaGetter(object): def __init__(self): self.trans = dict(dog='小狗',cat='小猫') def get(self, msg_id): try: return self.trans[msg_id] except KeyError: return str(msg_id) class EnglishGetter(object): def get(self, msg_id): return str(msg_id) def get_localizer(language='English'): languages = dict(English=EnglishGetter, China=ChinaGetter) return languages[language]() # create localizer e, g = get_localizer('English'), get_localizer('China') for text in "A cat sits on the desk".split(): print(e.get(text), g.get(text))
2.抽象ファクトリ
-
効果
特定のクラスを指定せずに、関連オブジェクトまたは相互依存オブジェクトを作成するためのインターフェースを提供します。
-
該当するシーン
- システムが製品の作成、組み合わせ、および表示から独立している場合
- システムを構成するために複数の製品ファミリーの1つが必要な場合
- 共同で使用する一連の関連製品オブジェクトのデザインを強調する場合
- 製品クラスライブラリを提供するが、実装ではなくインターフェースのみを表示する場合。
-
サンプルデモ
import random class Cat(object): def speak(self): return 'meow' def __str__(self): return 'Cat' class Dog(object): def speak(self): return 'woolf' def __str__(self): return 'Dog' class CatFactory(object): def get_pet(self): return Cat() def get_food(self): return 'Cat food' class DogFactory(object): def get_pet(self): return Dog() def get_food(self): return 'Dog food' class PetShop(object): def __init__(self, animal_factory=None): self.factory = animal_factory def show_pet(self): pet = self.factory.get_pet() print('This is a lovely ', str(pet)) print('pet speaks ', pet.speak()) print('pet eats ', pet.get_food()) def get_factory(): return random.choice([DogFactory, CatFactory])
3.ビルダー
-
効果
複雑なオブジェクトの構築をその表現から分離して、同じ構築プロセスで異なる表現を作成できるようにします。
-
該当するシーン
- 複合オブジェクトの作成が、オブジェクトを構成するサブコンポーネントから独立している場合、およびサブコンポーネントがどのように組み立てられているか
- 異なるオブジェクトを異なる構築プロセスで作成できるようにする
-
サンプルデモ
class Director(object): def __init__(self): self.builder = None def construct_building(self): self.builder.new_building() self.builder.build_floor() self.builder.build_size() def get_building(self): return self.builder.new_building class Builder(object): def __init__(self): self.building = None def new_building(self): self.building = Building() class BuilderHouse(Builder): def build_floor(self): self.building.floor = "One" def build_size(self): self.building.size = "Big" class BuilderFlat(Builder): def build_floor(self): self.building.floor = "Two" def build_size(self): self.building.size = "Small" class Building(object): def __init__(self): self.floor = None self.size = None def __repr__(self): return "Floor : %s | Size: %s" % (self.floor, self.size) if __name__ == '__main__': director = Director() director.builder = BuilderHouse() director.construct_building() building = director.get_building() print(building) director.builder = BuilderFlat() director.construct_building() building = director.get_building() print(building)
4.プロトタイプ(プロトタイプ)
-
効果
プロトタイプインスタンスを使用してオブジェクトを作成し、これらのプロトタイプをコピーして新しいオブジェクトを作成します。
-
該当するシーン
- 動的ロード、つまり、インスタンス化されるクラスが実行時に指定されるとき
- 製品クラスレベルと並行するファクトリクラスレベルの作成を回避するには
- クラスのインスタンスが複数の異なる状態の組み合わせしか持てない場合
毎回適切な状態で手動でインスタンス化するよりも、対応する数のプロトタイプとそれらのクローンを作成する方が便利です。
-
サンプルデモ
import copy class Prototype: def __init__(self): self._objects = {} def register_object(self, name, obj): self._objects[name] = obj def unregister_object(self, name): del self._objects[name] def clone(self, name, **attr): obj = copy.deepcopy(self._objects.get(name)) obj.__dict__.update(attr) return obj def main(): class A: def __str__(self): return "I"'m A.' a = A() prototype = Prototype() prototype.register_object('a', a) b = prototype.clone('a', a=1, b=2, c=3) print(a) print(b.a, b.b, b.c) if __name__ == '__main__': main()
5.シングルトン(シングルケース)
-
効果
クラスのインスタンスが1つだけ存在することを確認してください
-
該当するシーン
- クラスが1つのインスタンスのみを許可し、クライアントがグローバル変数からインスタンスにアクセスできる場合
- この一意のインスタンスをサブクラスで拡張でき、クライアントがコードを変更せずに拡張インスタンスを使用できる場合
-
サンプルデモ
class Singleton(object): def __new__(cls, *args, **kwargs): if not hasattr(cls, '_instance'): org = super(Singleton, cls) cls._instance = org.__new__(cls, *args, **kwargs) return cls._instance class SingleSpam(Singleton): def __init__(self, s): self.s = s def __str__(self): return self.s if __name__ == '__main__': spam = SingleSpam('spam') print(id(spam), spam) spa = SingleSpam('spa') print(id(spa), spa) print(id(spam), spam)
構造タイプ
6.アダプタクラス/オブジェクト(アダプタ)
-
効果
あるクラスのインターフェースを、クライアントが望む別のインターフェースに変換します。アダプターモードでは、互換性のないインターフェイスが原因で一緒に機能しなかったクラスが再び一緒に機能することができます。
-
該当するシーン
- インターフェースがニーズを満たさない既存のクラスを使用しようとしています
- 他の無関係または予測不可能なクラスと連携できる再利用可能なクラスを作成しようとしています
- 既存のサブクラスを使用しようとしていますが、それぞれをサブクラス化してそれらのインターフェースに一致させることはできません。
-
サンプルデモ
import os class Dog(object): def __init__(self): self.name = "Dog" def bark(self): return "woof" class Cat(object): def __init__(self): self.name = "Cat" def meow(self): return "meow" class Human(object): def __init__(self): self.name = "Human" def speak(self): return "hello" class Car(object): def __init__(self): self.name = "Car" def make_noise(self, octane_level): return "vroom" % ("!" * octane_level) class Adapter(object): def __init__(self, obj, adapted_methods): self.obj = obj self.__dict__.update(adapted_methods) def __getattr__(self, item): return getattr(self.obj, attr) def main(): objects = [] dog = Dog() objects.append(Adapter(dog, dict(make_noise=dog.bark))) cat = Cat() objects.append(Adapter(cat, dict(make_noise=cat.meow))) human = Human() objects.append(Adapter(human, dict(make_noise=human.speak))) car = Car() car_noise = lambda: car.make_noise(3) objects.append(Adapter(car, dict(make_noise=car_noise))) for obj in objects: print("{0} said {1}".format(obj.name, obj.make_noise())) if __name__ == "__main__": main()
7.ブリッジ
-
効果
抽象部分とその実現部分を分離して、独立して変更できるようにする
-
該当するシーン
- 抽象化と実現化の部分を分離するようにしてください。
- クラスの抽象化とその実装は、サブクラスを生成することで拡張できます。
- 抽象実装部分を変更しても、クライアントに影響はありません。つまり、クライアントを再コンパイルする必要はありません。
-
サンプルデモ
# ConcreteImplementor class DrawingApi1(object): def draw_circle(self, x, y, radius): print("Api_1.circle at {}:{} radius {}".format(x, y, radius)) # ConcreteImplementor class DrawingApi2(object): def draw_circle(self, x, y, radius): print("Api_1.circle at {}:{} radius {}".format(x, y, radius)) # Abstraction class CircleShape(object): def __init__(self, x, y, radius, drawing_api): self._x = x self._y = y self._radius = radius self._drawing_api = drawing_api # low level, i.e. implementation def draw(self): self._drawing_api.draw_circle(self._x, self._y, self._radius) # high level i.e. Abstraction def scale(self, pct): self._radius *= pct def main(): shapes = ( CircleShape(1, 2, 3, DrawingApi1()), CircleShape(5, 7, 11, DrawingApi2()) ) for shape in shapes: shape.scale(2.5) shape.draw() if __name__ == '__main__': main()
8.コンポジット
-
効果
オブジェクトをツリー構造に結合して部分全体の階層を表すと、クライアントは単一のオブジェクトと結合されたオブジェクトをより一貫して使用できます。
-
該当するシーン
- 部分全体の階層の必要性
- クライアントが結合オブジェクトと単一オブジェクトの違いを無視し、結合構造を均一に使用することを願っています。
-
サンプルデモ
# Abstraction class Component(object): def __init__(self, name): self._name = name def add(self, com): pass def display(self, depth): pass # Instance class Leaf(Component): def add(self, com): print("leaf can not add") def display(self, depth): name = "-" * depth name += self._name print(name) class Composite(Component): def __init__(self, name): super(Composite).__init__(name) self.entities = [] def add(self, entity): self.entities.append(entity) def display(self, depth): name = "-" * depth name += self._name for entity in self.entities: entity.display(depth+2) if __name__ == "__main__": p = Composite("Wong") p.add(Leaf("Lee")) p.add(Leaf("Zhao")) p1 = Composite("Wu") p1.add(Leaf("San")) p.add(p1) p.display(1)
9.デコレーター(デコレーター)
-
効果
オブジェクトにいくつかの追加機能を動的に追加します。使用法に関しては、デコレータパターンはサブクラスを生成する方法よりも柔軟性があります。
-
該当するシーン
- 他のオブジェクトに影響を与えることなく、動的で透過的な方法で個々のオブジェクトに責任を追加する
- 取消可能な義務の取り扱い
- サブクラスの生成方法を拡張に使用できない場合:
- 多数の独立した拡張機能が存在する場合があります。各組み合わせをサポートするために、多数のサブクラスが生成され、多数のサブクラスが爆発します。
- クラス定義が非表示になっているか、クラス定義を使用してサブクラスを生成できません
-
サンプルデモ
class Foo(object): def func1(self): print("original func1") def func2(self): print("original func2") class FooDecorator(object): def __init__(self, decorator): self._decorator = decorator def func1(self): print("decorated func1") self._decorator.func1() def __getattr__(self, item): return getattr(self._decorator, item) if __name__ == '__main__': foo = Foo() foo_decorator = FooDecorator(foo) foo_decorator.func1() foo_decorator.func2()
10.ファサード(外観)
-
効果
サブシステムの一連のインターフェースに一貫したインターフェースを提供するために、ファサードモードは高レベルのインターフェースを定義し、サブシステムを使いやすくします。
-
該当するシーン
- 継続的な進化によりサブシステムが複雑になった場合、複雑なサブシステムにシンプルなインターフェースを提供しようとする
- ほとんどのパターンが使用されると、サブクラスの再利用またはサブシステムのカスタマイズを試みて、生成されるクラスが小さくなります。
- クライアントと抽象クラスの実装の間には大きな依存関係があります。Facadeの導入により、サブシステムをクライアントや他のサブシステムから分離し、サブシステムの独立性と移植性を向上させることができます
- 階層サブシステムを構築する必要がある場合は、ファサードパターンを使用して、サブシステムの各レイヤーのエントリポイントを定義します。
- サブシステムが互いに依存している場合、Facadeを使用して通信できるため、サブシステム間の依存関係が簡素化されます。
-
サンプルデモ
import time SLEEP = 0.5 class TC1(object): def run(self): print("###### In Test 1 ######") time.sleep(SLEEP) print("Setting up") time.sleep(SLEEP) print("Running test") time.sleep(SLEEP) print("Tearing down") time.sleep(SLEEP) print("Test 1 Finished") class TC2(object): def run(self): print("###### In Test 2 ######") time.sleep(SLEEP) print("Setting up") time.sleep(SLEEP) print("Running test") time.sleep(SLEEP) print("Tearing down") time.sleep(SLEEP) print("Test 2 Finished") class TC3(object): def run(self): print("###### In Test 3 ######") time.sleep(SLEEP) print("Setting up") time.sleep(SLEEP) print("Running test") time.sleep(SLEEP) print("Tearing down") time.sleep(SLEEP) print("Test 3 Finished") # Facade class TestRunner(object): def __init__(self): self.tc1 = TC1() self.tc2 = TC2() self.tc3 = TC3() self.tests = [i for i in (self.tc1, self.tc2, self.tc3)] def run_all(self): for test in self.tests: test.run() # Client if __name__ == '__main__': test_runner = TestRunner() test_runner.run_all()
11. Flyweight(フライウェイト)
-
効果
共有テクノロジーを利用して、多数の細粒度オブジェクトを効果的にサポート
-
該当するシーン
- アプリケーションが多くのオブジェクトを使用するため、多くのストレージオーバーヘッドが発生する
- オブジェクトのほとんどの状態を外部状態に変更できます。オブジェクトの外部状態を削除すると、多くの思考グループを比較的少数の共有オブジェクトに置き換えることができます
- アプリケーションはオブジェクトの識別に依存しません。Flyweightオブジェクトは共有できるため、識別テストでは、概念的に異なるオブジェクトの真の値が返されます。
-
サンプルデモ
import weakref class Card(object): _CardPool = weakref.WeakValueDictionary() def __new__(cls, value, suit): obj = Card._CardPool.get(value + suit, None) if not obj: obj = object.__new__(cls) Card._CardPool[value + suit] = obj obj.value, obj.suit = value, suit return obj def __repr__(self): return "<Card: %s%s>" % (self.value, self.suit) # Client if __name__ == '__main__': c1 = Card("9", "h") c2 = Card("9", "h") print(c1, c2) print(c1 == c2) print(id(c1), id(c2))
12.プロキシ
-
効果
他のオブジェクトのプロキシを提供して、そのようなオブジェクトへのアクセスを制御する
-
該当するシーン
- 単純なポインタの代わりに、より一般的で複雑なオブジェクトポインタを使用する必要がある場合は、プロキシモードを使用します。
- リモートプロキシ:異なるアドレス空間にあるオブジェクトのローカル代表を提供します
- 仮想エージェント:必要に応じて高価なオブジェクトを作成します
- 保護エージェント:元のオブジェクトへのアクセスを制御します。オブジェクトには異なるアクセス権が必要です。
- スマートプロキシ:単純なポインタの代わりに、オブジェクトにアクセスするときにいくつかの追加操作を実行します。典型的な使用:実際のオブジェクトへの参照のカウント。
- 永続オブジェクトが初めて参照されるときに、それをメモリにロードします
- 実際のオブジェクトにアクセスする前に、他のオブジェクトが変更できないように、ロックがすでにロックされているかどうかを確認してください
- 単純なポインタの代わりに、より一般的で複雑なオブジェクトポインタを使用する必要がある場合は、プロキシモードを使用します。
-
サンプルデモ
import time class SaleManager(object): def work(self): print("Sale Manager is working...") def talk(self): print("sale Manager ready to talk.") class Proxy(object): def __init__(self): self.busy = False self.sales = None def work(self): print("Proxy checking for Sale Manager availability.") if not self.busy: self.sales = SaleManager() time.sleep(2) self.sales.talk() else: time.sleep(2) print("Sale Manager is busy.") # Client if __name__ == '__main__': proxy = Proxy() proxy.work() proxy.busy = True proxy.work()
13.通訳
-
効果
言語を指定し、その文法の式(式)を定義し、インタープリターを定義すると、インタープリターはこの式(式)を使用して、解釈された言語の文を解釈します。
-
該当するシーン
- 言語が解釈と実行を必要とし、この言語の文をASTとして表現できる場合は、インタープリターモードを使用できます(詳細に調整するため)
-
サンプルデモ
class Context(object): def __init__(self): self.input = "" self.output = "" class AbstractExpression(object): def interpret(self, context): pass class Expresssion(AbstractExpression): def interpret(self, context): print("terminal interpret") class NonterminalExpression(AbstractExpression): def interpret(self, context): print("Nonterminal interpret") # Client if __name__ == '__main__': context = "" c = [] c.append(Expresssion()) c.append(NonterminalExpression()) c.append(Expresssion()) c.append(Expresssion()) for item in c: c.interpret(context)
14.テンプレートメソッド
-
効果
操作でアルゴリズムのスケルトンを定義し、サブクラスへのいくつかのステップを遅らせます。TemplateMethodを使用すると、サブクラスはアルゴリズムの構造を変更せずに、アルゴリズムの特定のステップを再定義できます。
-
該当するシーン
- アルゴリズムの不変部分を一度に実装し、変数の動作を実装するサブクラスに任せます。
- コードの重複を避けるために、各サブクラスの共通の動作を抽出して共通の親クラスに集中させる必要があります。これは、OpdykeとJohnsonによって説明されている「一般化するためのリファクタリング」の良い例です
- サブクラス拡張の制御
-
サンプルデモ
# Skeletons
string = "apple bear cat"
def iter_elements(getter, action):
for elem in getter():
action(elem)
def rev_elements(getter, action):
for elem in getter()[::-1]:
action(elem)
# Getters
def get_list():
return string.split()
def get_lists():
return [list(x) for x in string.split()]
# Actions
def print_item(item):
print(item)
def print_rev_item(item):
print(item[::-1])
def make_templates(skeleton, getter, action):
def template():
skeleton(getter, action)
return template
# Client
if __name__ == '__main__':
templates = [make_templates(s, g, a)
for g in (get_list, get_lists)
for a in (print_item, print_rev_item)
for s in (iter_elements, rev_elements)
]
for template in templates:
template()