23のデザインパターン

3つの一般的な基本設計パターン

  1. 作成モード

    適切な状況に合わせて、インスタンス化メソッドと対応する作成メソッドを提供します。

  2. 構造化モデル

    エンティティ間の関係を処理するために使用され、これらのエンティティがよりうまく連携できるようにします。

  3. 行動モード

    異なるエンティティ間の通信に使用され、エンティティ間の通信のためのより簡単で柔軟な通信方法を提供します。

詳細モード紹介

作成タイプ

1.ファクトリーメソッド

ファクトリーメソッド

  1. 効果

    オブジェクトを作成するためのインターフェースを定義し、インスタンス化するクラスをサブクラスに決定させます。ファクトリメソッドは、クラスのインスタンス化をそのサブクラスに遅らせます。

  2. 該当するシーン

    • クラスが作成する必要があるオブジェクトのクラスを知らない場合。
    • クラスがそのサブクラスで作成するオブジェクトを指定する必要がある場合。
  3. サンプルデモ

    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. 効果

    特定のクラスを指定せずに、関連オブジェクトまたは相互依存オブジェクトを作成するためのインターフェースを提供します。

  2. 該当するシーン

    • システムが製品の作成、組み合わせ、および表示から独立している場合
    • システムを構成するために複数の製品ファミリーの1つが必要な場合
    • 共同で使用する一連の関連製品オブジェクトのデザインを強調する場合
    • 製品クラスライブラリを提供するが、実装ではなくインターフェースのみを表示する場合。
  3. サンプルデモ

    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.ビルダー

ファクトリーメソッド

  1. 効果

    複雑なオブジェクトの構築をその表現から分離て、同じ構築プロセスで異なる表現を作成できるようにします。

  2. 該当するシーン

    • 複合オブジェクトの作成が、オブジェクトを構成するサブコンポーネントから独立している場合、およびサブコンポーネントがどのように組み立てられているか
    • 異なるオブジェクトを異なる構築プロセスで作成できるようにする
  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.プロトタイプ(プロトタイプ)

ファクトリーメソッド

  1. 効果

    プロトタイプインスタンスを使用してオブジェクトを作成し、これらのプロトタイプをコピーして新しいオブジェクトを作成します。

  2. 該当するシーン

    • 動的ロード、つまり、インスタンス化されるクラスが実行時に指定されるとき
    • 製品クラスレベルと並行するファクトリクラスレベルの作成を回避するには
    • クラスのインスタンスが複数の異なる状態の組み合わせしか持てない場合

    毎回適切な状態で手動でインスタンス化するよりも、対応する数のプロトタイプとそれらのクローンを作成する方が便利です。

  3. サンプルデモ

    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つだけ存在することを確認してください

  2. 該当するシーン

    • クラスが1つのインスタンスのみを許可し、クライアントがグローバル変数からインスタンスにアクセスできる場合
    • この一意のインスタンスをサブクラスで拡張でき、クライアントがコードを変更せずに拡張インスタンスを使用できる場合
  3. サンプルデモ

    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.アダプタクラス/オブジェクト(アダプタ)

ファクトリーメソッド

  1. 効果

    あるクラスのインターフェースを、クライアントが望む別のインターフェースに変換します。アダプターモードでは、互換性のないインターフェイスが原因で一緒に機能しなかったクラスが再び一緒に機能することができます。

  2. 該当するシーン

    • インターフェースがニーズを満たさない既存のクラスを使用しようとしています
    • 他の無関係または予測不可能なクラスと連携できる再利用可能なクラスを作成しようとしています
    • 既存のサブクラスを使用しようとしていますが、それぞれをサブクラス化してそれらのインターフェースに一致させることはできません。
  3. サンプルデモ

    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.ブリッジ

ファクトリーメソッド

  1. 効果

    抽象部分とその実現部分を分離して、独立して変更できるようにする

  2. 該当するシーン

    • 抽象化と実現化の部分を分離するようにしてください。
    • クラスの抽象化とその実装は、サブクラスを生成することで拡張できます。
    • 抽象実装部分を変更しても、クライアントに影響はありません。つまり、クライアントを再コンパイルする必要はありません。
  3. サンプルデモ

    # 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.コンポジット

複合

  1. 効果

    オブジェクトをツリー構造に結合し部分全体の階層を表すと、クライアントは単一のオブジェクトと結合されたオブジェクトをより一貫して使用できます。

  2. 該当するシーン

    • 部分全体の階層の必要性
    • クライアントが結合オブジェクトと単一オブジェクトの違いを無視し、結合構造を均一に使用することを願っています。
  3. サンプルデモ

    # 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.デコレーター(デコレーター)

デコレータ

  1. 効果

    オブジェクトにいくつかの追加機能を動的に追加します。使用法に関しては、デコレータパターンはサブクラスを生成する方法よりも柔軟性があります。

  2. 該当するシーン

    • 他のオブジェクトに影響を与えることなく、動的で透過的な方法で個々のオブジェクトに責任を追加する
    • 取消可能な義務の取り扱い
    • サブクラスの生成方法を拡張に使用できない場合:
      • 多数の独立した拡張機能が存在する場合があります。各組み合わせをサポートするために、多数のサブクラスが生成され、多数のサブクラスが爆発します。
      • クラス定義が非表示になっているか、クラス定義を使用してサブクラスを生成できません
  3. サンプルデモ

    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.ファサード(外観)

ファサード

  1. 効果

    サブシステムの一連のインターフェースに一貫したインターフェースを提供するために、ファサードモードは高レベルのインターフェースを定義し、サブシステムを使いやすくします。

  2. 該当するシーン

    • 継続的な進化によりサブシステムが複雑になった場合、複雑なサブシステムにシンプルなインターフェースを提供しようとする
    • ほとんどのパターンが使用されると、サブクラスの再利用またはサブシステムのカスタマイズを試みて、生成されるクラスが小さくなります。
    • クライアントと抽象クラスの実装の間には大きな依存関係があります。Facadeの導入により、サブシステムをクライアントや他のサブシステムから分離し、サブシステムの独立性と移植性を向上させることができます
    • 階層サブシステムを構築する必要がある場合は、ファサードパターンを使用して、サブシステムの各レイヤーのエントリポイントを定義します。
      • サブシステムが互いに依存している場合、Facadeを使用して通信できるため、サブシステム間の依存関係が簡素化されます。
  3. サンプルデモ

    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(フライウェイト)

フライ級

  1. 効果

    共有テクノロジーを利用して、多数の細粒度オブジェクトを効果的にサポート

  2. 該当するシーン

    • アプリケーションが多くのオブジェクトを使用するため、多くのストレージオーバーヘッドが発生する
    • オブジェクトのほとんどの状態を外部状態に変更できます。オブジェクトの外部状態を削除すると、多くの思考グループを比較的少数の共有オブジェクトに置き換えることができます
    • アプリケーションはオブジェクトの識別に依存しません。Flyweightオブジェクトは共有できるため、識別テストでは、概念的に異なるオブジェクトの真の値が返されます。
  3. サンプルデモ

    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.プロキシ

代理

  1. 効果

    他のオブジェクトのプロキシを提供して、そのようなオブジェクトへのアクセスを制御する

  2. 該当するシーン

    • 単純なポインタの代わりに、より一般的で複雑なオブジェクトポインタを使用する必要がある場合は、プロキシモードを使用します。
      • リモートプロキシ:異なるアドレス空間にあるオブジェクトのローカル代表を提供します
      • 仮想エージェント:必要に応じて高価なオブジェクトを作成します
      • 保護エージェント:元のオブジェクトへのアクセスを制御します。オブジェクトには異なるアクセス権が必要です。
      • スマートプロキシ:単純なポインタの代わりに、オブジェクトにアクセスするときにいくつかの追加操作を実行します。典型的な使用:実際のオブジェクトへの参照のカウント。
    • 永続オブジェクトが初めて参照されるときに、それをメモリにロードします
    • 実際のオブジェクトにアクセスする前に、他のオブジェクトが変更できないように、ロックがすでにロックされているかどうかを確認してください
  3. サンプルデモ

    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.通訳

通訳

  1. 効果

    言語を指定し、その文法の式(式)を定義し、インタープリターを定義すると、インタープリターはこの式(式)を使用して、解釈された言語の文を解釈します。

  2. 該当するシーン

    • 言語が解釈と実行を必要とし、この言語の文をASTとして表現できる場合は、インタープリターモードを使用できます(詳細に調整するため)
  3. サンプルデモ

    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.テンプレートメソッド

テンプレートメソッド

  1. 効果

    操作でアルゴリズムのスケルトンを定義し、サブクラスへのいくつかのステップを遅らせます。TemplateMethodを使用すると、サブクラスはアルゴリズムの構造を変更せずに、アルゴリズムの特定のステップを再定義できます。

  2. 該当するシーン

    • アルゴリズムの不変部分を一度に実装し、変数の動作を実装するサブクラスに任せます。
    • コードの重複を避けるために、各サブクラスの共通の動作を抽出して共通の親クラスに集中させる必要があります。これは、OpdykeとJohnsonによって説明されている「一般化するためのリファクタリング」の良い例です
    • サブクラス拡張の制御
  3. サンプルデモ

# 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()

おすすめ

転載: www.cnblogs.com/CocoML/p/12726669.html