デザインパターンについて勉強してみた(個人的メモ)その2(TemplateMethodパターン、FactoryMethodパターン、Singletonパターン)


はじめに

この記事は個人的な勉強メモです。inputしたものはoutputしなくてはという強迫観念に駆られて記事を書いています。

あわよくば詳しい人に誤りの指摘やアドバイスを頂ければいいなという思いを込めてQiitaの記事にしています。

エンジニアとして社会人生活を送っていますが、デザインパターンについてちゃんと学んだことがなかったので勉強してみました。

ここに記載している内容は
https://github.com/ck-fm0211/notes_desigh_pattern
にuploadしています。

過去ログ

デザインパターンについて勉強してみた(個人的メモ)その1

TemplateMethodパターン

  • スーパークラスで処理の枠組みを定め、サブクラスでその具体的内容を実装する

実際に使ってみる

題材

  • ざっくりした業務マニュアルを作成する
# -*- coding:utf-8 -*-
from abc import ABCMeta, abstractmethod


class Manual(metaclass=ABCMeta):
    @abstractmethod
    def check_mail(self):
        pass

    @abstractmethod
    def edit_list(self):
        pass

    @abstractmethod
    def send_mail(self):
        pass


class JohnManual(Manual):
    def check_mail(self):
        print("mailアプリでメール確認")

    def edit_list(self):
        print("VBAを使ってリスト修正")

    def send_mail(self):
        print("VBAからそのままメール送信")

  • テンプレートとなる Manual クラスを作成
  • Johnはそれを継承した JohnManual を作成
  • テンプレートの流れに沿って独自のやり方を実現

TemplateMethodパターンのまとめ

FactoryMethodパターン

  • オブジェクトの生成方法に一工夫加えることで、より柔軟にオブジェクトを生成することを目的とするもの
  • インスタンスの生成をサブクラスに行わせることで、より柔軟に生成するインスタンスを選択することが可能となる
  • 通常、以下のようにインスタンスを生成する
chair_person = Mary()
  • FactoryMethodパターンではオブジェクトの生成を担うメソッド(factory method)を通して間接的にオブジェクトを生成する
class Factory(metaclass=ABCMeta):

    @abstractmethod
    def _create_product(self, owner):
        pass

    def create(self, owner):
        self.__p = self._create_product(owner)

実際に使ってみる

題材

  • メモをとるという業務を定義
  • メモを取る先の材料(紙、電子ペーパー)は個人の自由
  • Johnは電子ペーパーにメモを取りたい
# -*- coding:utf-8 -*-
from abc import ABCMeta, abstractmethod


class Editable:
    pass


class Paper(Editable):
    pass


class Manual(metaclass=ABCMeta):

    @abstractmethod
    def edit(self, material: Editable):
        pass

    @abstractmethod
    def create_material(self):
        return Paper()

    @abstractmethod
    def take_memo(self):
        material = self.create_material()
        self.edit(material)


class ElectronicPaper(Editable):
    pass


class JohnManual(Manual):
    def edit(self, material):
        print("電子ペーパーにメモをとる")

    def create_material(self):
        return ElectronicPaper()

    def take_memo(self):
        material = self.create_material()
        self.edit(material)

  • インスタンス生成のためのメソッドを用意する。そして、そのインスタンスを生成するためのメソッドを通してインスタンスの生成を行う。
  • create_material メソッドをオーバーライドするメソッドを記述し、メモをとる先の材料を自由に選択することができるようになる

FactoryMethodパターンのまとめ

Singletonパターン

  • singleton とは一枚札のこと。一枚札とはトランプの一組に唯一のカード。Singleton パターンとは、このような唯一の存在を保証するためのパターン。
  • あるクラスのインスタンスが一つしかないことを保証したい場合につかう

実際に使ってみる

題材

  • 図書館の貸出帳を考える
  • 図書の貸出に当たって、その貸出帳がいくつあるのかわからないような状態では、管理がとても難しくなる
# -*- coding:utf-8 -*-


class RegisterNote:
    __singleton = None
    __register = None

    def __new__(cls, *args, **kwargs):
        if cls.__singleton == None:
            cls.__singleton = super(RegisterNote, cls).__new__(cls)
        return cls.__singleton

    def set_register(self, register=None):
        self.__register = register

    def get_register(self):
        return self.__register

RegisterNote()  # クラス定義の直下に書いて、インスタンスを作るために必ず呼ぶ
  • 下記の通り、2つのインスタンス( rn1 rn2 )は同じインスタンスだということがわかる
>>> class RegisterNote:
...     __singleton = None
...     __register = None
...
...     def __new__(cls, *args, **kwargs):
...         if cls.__singleton == None:
...             cls.__singleton = super(RegisterNote, cls).__new__(cls)
...         return cls.__singleton
...
...     def set_register(self, register=None):
...         self.__register = register
...
...     def get_register(self):
...         return self.__register
...     
>>> RegisterNote()
<RegisterNote object at 0x10c48f9e8>
>>> rn1 = RegisterNote()
...
>>> rn2 =RegisterNote()
...
>>> rn1
<RegisterNote object at 0x10c48f9e8>
>>> rn2
<RegisterNote object at 0x10c48f9e8>
>>> rn2.set_register('book book book')
>>> rn1.get_register()
'book book book'

備考

- Javaの場合はprivateを使えば容易にsingletonを実装できる

public class RegisterNote{
    private static RegisterNote registerNote = new RegisterNote();
    private RegisterNote(){}
    public static RegisterNote getInstance(){
        return registerNote;
    }
}
  • registerNote は、RegisterNote クラスが初期化されるときに生成される。
  • クラスが初期化されるタイミングは Java の言語仕様で定義されており、 初めてインスタンス化された時や、初めて static メソッドが呼び出されたタイミングなどに行われる。
    • すなわち、このサンプルプログラムでは、staticメソッドであるgetInstance メソッドが初めて呼ばれるときにRegisterNode クラスが初期化され、そのときに一度だけRegisterNode クラスのインスタンスが生成されることになる。
  • この後、getInstance メソッドが呼ばれたときには、既に生成されている registerNote オブジェクトが返されることになる。
  • pythonにはprivateが存在しないので、上記のように __new__() メソッドを使う。
    • __init__() メソッドより前に呼ばれ、インスタンスを生成して返す

Singletonパターンのまとめ

参考