Pythonで生成に関するデザインパターンを作成してみた

## 目次
[● Factory Method パターン](#-factory-method-パターン)
[● Abstract Factory パターン](#-abstract-factory-パターン)
[● Builder パターン](#-builder-パターン)
[● Singleton パターン](#-singleton-パターン)
[● Prototype パターン](#-prototype-パターン)
[その他のデザインパターン](#その他のデザインパターン)
## <font color="DarkGray">●</font><font color="DeepSkyBlue"> Factory Method パターン</font>
```python:FactoryMethod.py
# ---------------------------------------------------------------------------------------
#                　　　　　　　　　　Factory Method パターン
# ---------------------------------------------------------------------------------------
# オブジェクトを作成するためのインターフェースを定義し、サブクラスが作成する具象クラスを決定します。
from abc import ABCMeta, abstractmethod


# 抽象的な製品
class Payment(metaclass=ABCMeta):
    @abstractmethod
    def pay(self, money):
        pass


# 抽象的な工場
class PaymentFactory(metaclass=ABCMeta):
    @abstractmethod
    def create_payment(self):
        pass


# 具象クラス（製品）
class VisaPay(Payment):
    def pay(self, money):
        print("Visa支払い：{0}円".format(money))


# 具象クラス（製品）
class MasterPay(Payment):
    def pay(self, money):
        print("Master支払い：{0}円".format(money))


# 具象クラス（工場）
class VisaFactory(PaymentFactory):
    def create_payment(self):
        return VisaPay()


# 具象クラス（工場）
class MasterFactory(PaymentFactory):
    def create_payment(self):
        return MasterPay()


if __name__ == '__main__':
    # Client(利用側)
    # 利用側は利用されるVisaPayなどに依存しません。
    py = VisaFactory()
    p = py.create_payment()
    p.pay(1000)

    py = MasterFactory()
    p = py.create_payment()
    p.pay(2000)
```
実行結果：
```
Visa支払い：1000円
Master支払い：2000円
```

## <font color="DarkGray">●</font><font color="DeepSkyBlue"> Abstract Factory パターン</font>

```python:AbstractFactory.py
# ---------------------------------------------------------------------------------------
#                　　　　　　　　　　Abstract Factory パターン
# ---------------------------------------------------------------------------------------
# ファクトリクラスインターフェイスを定義し、サブファクトリクラスで互いに関連する一連オブジェクト群を間違いなく作成します。

from abc import ABCMeta, abstractmethod


# 抽象的なCPU
class CPU(metaclass=ABCMeta):
    @abstractmethod
    def show_cpu(self):
        pass


# 抽象的なOS
class OS(metaclass=ABCMeta):
    @abstractmethod
    def show_os(self):
        pass


# 抽象的な工場
class SmartphoneBase(metaclass=ABCMeta):
    @abstractmethod
    def make_cpu(self):
        pass

    @abstractmethod
    def make_os(self):
        pass


# ----------具象CPU--------
class AppleCPU(CPU):
    def show_cpu(self):
        print("Apple  CPU")


class SnapdragonCPU(CPU):
    def show_cpu(self):
        print("Snapdragon CPU")


# ----------具象OS--------
class IOS(OS):
    def show_os(self):
        print("IOS モバイルオペレーティングシステム")


class Android(OS):
    def show_os(self):
        print("Android モバイルオペレーティングシステム")


# ----------具象工場--------
class AppleFactory(SmartphoneBase):
    def make_cpu(self):
        return AppleCPU()

    def make_os(self):
        return IOS()


class SamsungFactory(SmartphoneBase):
    def make_cpu(self):
        return SnapdragonCPU()

    def make_os(self):
        return Android()


# ------------ Client(利用側) ------------
class Smartphone(object):
    def __init__(self, cpu, os):
        self.cpu = cpu
        self.os = os

    def show_info(self):
        print("スマートフォン情報：")
        self.cpu.show_cpu()
        self.os.show_os()


def make_smartphone(factory):
    cpu = factory.make_cpu()
    os = factory.make_os()
    return Smartphone(cpu, os)


if __name__ == '__main__':
    p1 = make_smartphone(AppleFactory())
    p1.show_info()

    p2 = make_smartphone(SamsungFactory())
    p2.show_info()
```
実行結果：
```
スマートフォン情報：
Apple  CPU
IOS モバイルオペレーティングシステム
スマートフォン情報：
Snapdragon CPU
Android モバイルオペレーティングシステム
```
## <font color="DarkGray">●</font><font color="DeepSkyBlue"> Builder パターン</font>

```python:Builder.py
# ---------------------------------------------------------------------------------------
#                　　　　　　　　　　Builder パターン
# ---------------------------------------------------------------------------------------
# 連鎖呼び出しで、複雑なオブジェクトを段階的に構築します


def return_this(func):
    def wrapper(self, *args, **kwargs):
        func(self, *args, **kwargs)
        return self

    return wrapper


class Computer:

    def __init__(self):
        self.cpu = ''
        self.usbCount = 0
        self.display = ''
        self.builder = Computer.Builder(self)

    def show_info(self):
        print("cpu: {0}, usbCount: {1}, display: {2}".format(self.cpu, str(self.usbCount), self.display))

    # 内部クラス
    class Builder:
        def __init__(self, computer):
            self.cpu = computer.cpu
            self.usbCount = computer.usbCount
            self.display = computer.display

        @return_this
        def set_cpu(self, cpu):
            self.cpu = cpu

        @return_this
        def set_usbCount(self, usbCount):
            self.usbCount = usbCount

        @return_this
        def set_display(self, display):
            self.display = display

        def build(self):
            newComputer = Computer()
            newComputer.cpu = self.cpu
            newComputer.usbCount = self.usbCount
            newComputer.display = self.display
            newComputer.builder = self
            return newComputer


# ------------ Client(利用側) ------------

class Computer001Builder:
    def __init__(self):
        self.computer = Computer().builder \
            .set_cpu("Core i7-6700") \
            .set_usbCount(2) \
            .set_display("ASUS VZ249HE") \
            .build()

    def get_computer(self):
        return self.computer


if __name__ == '__main__':
    print("-----PC製品-----")
    c001 = Computer001Builder().get_computer()
    c001.show_info()

    print("-----自作PC-----")
    # デフォルト構成
    # 連鎖呼び出しで、PC製品というオブジェクトを段階的に構築します
    selfMakeComputer = Computer().builder \
        .set_cpu("Core i5-6500") \
        .set_usbCount(1) \
        .set_display("Dell U2720QM") \
        .build()
    selfMakeComputer.show_info()

    # 構成変更
    print("-----自作PCにUSB増設-----")
    selfMakeComputer = selfMakeComputer.builder.set_usbCount(5).build()
    selfMakeComputer.show_info()
```
実行結果：
```
-----PC製品-----
cpu: Core i7-6700, usbCount: 2, display: ASUS VZ249HE
-----自作PC-----
cpu: Core i5-6500, usbCount: 1, display: Dell U2720QM
-----自作PCにUSB増設-----
cpu: Core i5-6500, usbCount: 5, display: Dell U2720QM
```
## <font color="DarkGray">●</font><font color="DeepSkyBlue"> Singleton パターン</font>

```python:Singleton.py
# ---------------------------------------------------------------------------------------
#                　　　　　　　　　　Singleton パターン
# ---------------------------------------------------------------------------------------
# インスタンスを1つしか生成できないクラスを作成します。

class SingletonBase:
    def __new__(cls, *args, **kwargs):
        if not hasattr(cls, "_instance"):
            cls._instance = super(SingletonBase, cls).__new__(cls)
        return cls._instance


class SingletonA(SingletonBase):
    def __init__(self, x):
        self.x = x


if __name__ == '__main__':
    s1 = SingletonA(100)
    s2 = SingletonA(300)
    print(s1.x)
    print(s2.x)
    print("s1のアドレス：{0}, s2のアドレス：{1}".format(id(s1),  id(s2)))
```
実行結果：
```
300
300
s1のアドレス：2060572639088, s2のアドレス：2060572639088
```
## <font color="DarkGray">●</font><font color="DeepSkyBlue"> Prototype パターン</font>

```python:Prototype.py
# ---------------------------------------------------------------------------------------
#                　　　　　　　　　　Prototype パターン
# ---------------------------------------------------------------------------------------
# オブジェクトを複製します
class ProtoType:
    def __init__(self, id, name):
        super().__init__()
        self.id = id
        self.name = name

    def clone(self):
        import copy
        return copy.deepcopy(self)


if __name__ == '__main__':
    original = ProtoType(100, "テスト名称")
    o_copy = original.clone()
    print('original.id: {}  original.name: {}'.format(original.id, original.name))
    print('o_copy.id: {}  o_copy.name: {}'.format(o_copy.id, o_copy.name))
    print('オブジェクト(original)のID: {} '.format(id(original)))
    print('オブジェクト(o_copy)のID: {} '.format(id(o_copy)))
```
実行結果：
```
original.id: 100  original.name: テスト名称
o_copy.id: 100  o_copy.name: テスト名称
オブジェクト(original)のID: 1828390518688 
オブジェクト(o_copy)のID: 1828390518208 
```
## その他のデザインパターン
[Pythonで構造に関するデザインパターンを作成してみた](https://qiita.com/lxk/items/939300b9ff328a79c5b6)
[Pythonで振る舞いに関するデザインパターンを作成してみた](https://qiita.com/lxk/items/9ff63940ace1a68205fe)
***