nono blog
楽しい人生を送りたい!
ITスキル

pythonプログラム デザインパターン(Singleton)

singletonは、DBアクセスするオブジェクトを一つにするなどインスタンスを一つしか作成したくない場合によく用いられる。

Singletonパターンとは

(概要)
インスタンス作成時に同じオブジェクトが作成され、インスタンスが一つしか作成されないことを保証する。

(目的)
インスタンスを作成するのはメモリが必要であり、さらに作成に若干の時間を要します。
そのため Singleton が用いられる。

主に、インスタンスが一つしか必要のない場合に用いられる。
 ・DataBaseにアクセスするインスタンス(アクセス情報、ID,パスワードやパスなど)
 ・Objectを作成するFactory
など

(イメージ)

Instance1
Instance2 -> 3つのインスタンスが全て”Singleton”を示している
Instance3

pythonサンプルコード

SingletonをPythonで作成する場合の2種類のサンプルコードをメモする。

サンプルコード1(__new__メソッドを用いる)

# Singletonの作成方法1(__new__を用いる)

class DataBase():

    _instance = None

    # インスタンス変数を設定
    def __init__(self):
        self.__database_url = None

    # singleton(インスタンスは一つだけしか生成しない)
    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

    # インスタンス変数に対するgetter
    @property
    def database_url(self):
        return self.__database_url

    # インスタンス変数に対するsetter
    @database_url.setter
    def database_url(self, database_url):
        self.__database_url = database_url

    def connect(self):
        """ DB接続オブジェクト"""
        pass


if __name__ == '__main__':
    a = DataBase()
    b = DataBase()
    # インスタンスが同じものであることが確認できる
    print(a == b)
    print(id(a), id(b))

    a.database_url = '127.1.1.1:3363'
    print(a.database_url)
    print(b.database_url)

サンプルコード2(コンストラクタのprivate化をする)

# Singletonの作成方法2
# JavaなどでSingletonをする場合は、コンストラクタをprivateにし、privateにアクセスするのを制限する
# pythonの場合は コンストラクタをprivateにする機能はない.故に、少し強引に同じようなロジックを作る

class DabaBase(object):

    __instance = None

    # コンストラクタをprivateにする代わりに、エラーを返す
    def __init__(self):
        raise RuntimeError('This class contractor cannot be called.')

    # クラスメソッドとしてインスタンス生成を定義する
    @classmethod
    def get_instance(cls, database_url=None):
        if cls.__instance is None:
            # インスタンスがない(初めて実行)された場合は、newでインスタンスを作成する
            # 引数clsで自分自身のインスタンス作成となる。
            cls.__instance = cls.__new__(cls)
        if database_url:
            cls.__instance.__database_url = database_url
        return cls.__instance

    @property
    def database_url(self):
        return self.__database_url

    @database_url.setter
    def database_url(self, database_url):
        self.__database_url = database_url

    def connect(self):
        pass


if __name__ == '__main__':

    # 検証1:DataBaseインスタンスを直接作成するとエラーとなること
    # a = DabaBase()

    # インスタンスを作成するには、urlを引数にしてget_instanceを呼び出す
    a = DabaBase.get_instance('127.1.1.1:3363')
    b = DabaBase.get_instance()

    print(a == b)
    print(id(a), id(b))
    print(a.database_url, b.database_url)