<一覧に戻る

シングルトンパターン

「アプリ全体で同じ設定をどこからでも使いたい」「ロガーを毎回つくるのはムダでは?」——そんな場面、ありませんか?

その悩みをシンプルに解決してくれるのが、デザインパターンのひとつであるシングルトンパターンです。
シングルトンパターンは、特定のクラスのインスタンスがただ一つだけ存在することを保証し、その唯一のインスタンスへグローバルにアクセスできる仕組みを提供します。

Pythonの学習初期におさえておくと、設計の幅がぐっと広がります。

シングルトンパターンとは?

シングルトンパターンの目的は唯一のインスタンスをつくる・配るを徹底することです。インスタンスが一つしかないため、アプリ全体で共通の状態を扱ったり、重い初期化を一度で済ませたりできます。
たとえば以下のようなときに役立ちます。

  • 設定情報(設定ファイルの読み込み結果)をアプリ全体で共有したい
  • 1つのロガーを全モジュールで使いたい
  • キャッシュや一時ストレージを統一管理したい
  • 外部サービスのクライアントを使い回して無駄な接続を避けたい

「何度も同じものを作らず、一回作ってそれをどこでも使い回したい」——この発想がシングルトンの基本です。

Pythonでの基本的な実装(クラス変数を使う)

まずは最もシンプルで分かりやすい実装から始めましょう。ポイントは「__new__で作成を制御し、__init__は一度だけ初期化する」ことです。

class Singleton:
    _instance = None  # クラス全体で共有する格納先

    def __new__(cls, *args, **kwargs):
        # まだインスタンスがなければ作る。あればそれを返す。
        if cls._instance is None:
            cls._instance = super(Singleton, cls).__new__(cls)
        return cls._instance

    def __init__(self, value=None):
        # __init__は何度も呼ばれ得るため、初期化済みかをフラグで判定
        if not hasattr(self, 'initialized'):
            self.value = value
            self.initialized = True


# 使ってみる
singleton1 = Singleton("First Instance")
singleton2 = Singleton("Second Instance")

print(singleton1.value)        # 出力: First Instance
print(singleton2.value)        # 出力: First Instance(同じインスタンスを共有)
print(singleton1 is singleton2)  # 出力: True

はじめに、クラス変数の_instanceがたった一つの入れ物になります。

__new__は「インスタンスを作る工場」の役割を持つ特別なメソッドで、ここで「もう作ったことがあるか?」をチェックします。なければsuper(...)で本当に作り、あれば作らずに既存のものを返します。

一方__init__は「作られた後の初期化」を担当しますが、Pythonでは同じインスタンスを返していても呼ばれる可能性があります。そこでinitializedというフラグを使い、初回だけ値をセットするようにしています。こうすることで、2回目以降に__init__が呼ばれても状態が上書きされません。

💡 豆知識:なぜ _instance という名前なの? Pythonでは、変数名の先頭にアンダースコアを付けることで「これは内部用です」というサインを示します。 _instance はクラスの内部でインスタンスを保持するための変数であり、外部から直接アクセスされることを想定していません。

挙動をもう少し確かめてみる

「本当に同じインスタンスなの?」と不安になりますよね。そんなときはid関数でメモリアドレスを見てみましょう。

#上記コードの続き

a = Singleton("A")
b = Singleton("B")
print(id(a), id(b))  # 2つの値が同じなら同一インスタンス

同じ数字が表示されれば、aとbは完全に同じオブジェクトです。だからa.valueもb.valueも同じ値を示します。

使いどころと避けたい場面

シングルトンは非常に便利ですが、どんな場面でも使えばよいわけではありません。 向いているケースと避けた方がいいケースを理解することで、設計の柔軟性と安全性を両立できます。

向いている場面(シングルトンが力を発揮する)

シングルトンは、アプリ全体で共有すべき1つのリソースを扱うときに特に有効です。 複数作る意味がない、または作りすぎると不具合や非効率になるようなオブジェクトに適しています。

  • 設定管理(Configクラス): アプリ起動時に設定ファイルを一度だけ読み込み、その内容を全体で参照したい場合。→ 何度もファイルを読む無駄を省き、どのモジュールでも同じ設定値を使えます。
  • ロガー(Logger): 全モジュールで同じログフォーマットや出力先を使いたい場合。→ ロガーを複数作るとログがバラバラになりやすいため、統一した管理にシングルトンが適しています。
  • 軽量なキャッシュ・一時ストレージ : 計算結果やデータを一時的に保持して再利用したい場合。→ どこからアクセスしても同じキャッシュを参照できるため、処理が効率化します。
  • 外部サービスのクライアント(APIクライアントなど): 接続や認証を毎回行うのは非効率な場合。→ 一度作ったクライアントを使い回すことでパフォーマンスが向上します。

つまり、「一貫性を保ちたい」「初期化コストが高い」「共有が自然」といった特徴をもつものに向いています。

避けたい場面(安易に使うと危険なケース)

シングルトンはグローバルな状態を持つため、便利さと引き換えに管理の難しさが生まれます。 一度作ったインスタンスがアプリ全体に影響を及ぼすため、誤った使い方をするとバグや性能低下につながります。

  • データベース接続を1つに固定する設計 : 一見効率的ですが、同時アクセスが増えるとボトルネックになり、1つの接続障害で全体が止まる危険もあります。
  • 状態を持つビジネスロジッククラス : ングルトン内に可変の状態を持たせると、どこからでも変更できてしまい、予期せぬ副作用を生みやすくなります。

なんでもシングルトンで解決するのは危険です。 グローバルな状態が増えるほど、テストが難しくなり、コードの見通しが悪くなることがあります。

このようにシングルトンパターンの向き、不向きを理解しておく必要もあります。

まとめ

ここまで学習した内容をまとめます。

  • シングルトンパターンは「唯一のインスタンス」と「グローバルアクセス」を提供するデザインパターンです。
  • Pythonらしさを活かすなら、モジュールを使った実質シングルトンも有力な選択肢です。
  • 便利な反面、テストや拡張性の難しさも伴います。使いどころを見極めましょう。

次のステップとして、似た目的で「生成を管理する」他のデザインパターン(ファクトリメソッドパターン、抽象ファクトリーパターン、ビルダーパターン)にも触れてみると、状況に応じた最適解が選べるようになります。どのパターンをいつ選ぶべきか、少しずつ感覚が磨かれていきますよ。

出力結果: