【初心者向け】シングルトンパターンをわかりやすく解説

プログラミングをする犬 プログラミング

シングルトンパターンは「あるクラスのインスタンスがプログラム全体でたった1つしか作られないようにする」デザインパターンです。

デザインパターンとは、ソフトウェア設計の「よくある問題」を解決するための方法論です。

ソフトウェア設計に取り組んだ先人たちが、ソフトウェア設計における「よくある問題」の解決策をパターンとして考案してくれており、それを「デザインパターン」と呼んでいます。

シングルトンパターンもそんなデザインパターンのひとつです。

この記事では、シングルトンパターンをわかりやすく詳しく解説します。

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

シングルトンパターンは「あるクラスのインスタンスがプログラム全体でたった1つしか作られないようにする」デザインパターンです。

シングルトン(Singleton)とは、数学の用語で単集合(たった1つしか要素がない集合)のことです。

ふつう、オブジェクト指向プログラミングでは、1つのクラスからたくさんのインスタンスを作ることができます。

クラスは「オブジェクトの設計図」で、インスタンスとはその設計図から作られるオブジェクトです。

せっかくの設計図ですから、インスタンスはいくつも生成してこそメリットがあるものです。

しかし、シングルトンパターンでは、あえて1つのクラスから1つのインスタンスしか作れないように設計します。

シングルトンパターンを使う場面

「あるクラスからインスタンスを1つしか作れないようにする」ことにどんなメリットがあるのでしょうか?

シングルトンパターンは「たった1つだけあればいいもの」や「現実世界でも1つしか存在し得ないもの」を表現するときに使います。

たとえば、プログラミング言語「Python」における「None」オブジェクトはシングルトンなオブジェクトです。

「None」は他のプログラミング言語の「null」にあたるもので「ない」ことを示すオブジェクトです。

「ない」という概念はたった1つだけあればいいものです。

「ない-A」「ない-B」や「ない-No1」「ない-No2」のように複数存在する必要はありません。

そのため、Pythonの「None」はシングルトンパターンで実装されています。

Pythonのプログラム中にNoneオブジェクトは1つしか存在しません。

このように「たった1つだけあればいいもの」や「現実世界でも1つしか存在し得ないもの」を表現するときにシングルトンパターンを使ってクラスを設計します。

また、シングルトンパターンで生成する唯一のインスタンスは、通常プログラム内のどこからでもアクセスできるようにします(グローバルにアクセスできるようにします)。

シングルトンパターンを使うメリット

シングルトンパターンを使うと以下のようなメリットがあります。

  1. インスタンスが1つしか存在しないことを保証できる
  2. メモリやCPUのリソースの消費量を抑えられる

それぞれ詳しく説明します。

1. インスタンスが1つしか存在しないことを保証できる

シングルトンのインスタンスは、プログラム内に1つしか存在しません。

そのため「インスタンスが1つしか存在しないことを保証したいとき」に便利です。

例として、ロギングを扱うオブジェクトを考えてみましょう。

アプリの実行ログやエラーログの出力を担うオブジェクトです。

ロギングのオブジェクトはプログラム全体で1つあれば十分です。複数のオブジェクトがロギングを担当すると、出力先がばらばらになったり、出力の統合を考えなければならず面倒です。

ロギングをするオブジェクトが1つしかなければ、プログラムのどこでログを出力しても共通の出力先にまとめることができます。

このように「インスタンスが1つしか存在しないことを保証したいとき」にはシングルトンパターンが使えます。

2. メモリやCPUのリソースの消費量を抑えられる

シングルトンパターンを使うとメモリやCPUなどのリソースの消費量を抑えられる場合があります。

同じクラスから何度も複数のインスタンスを生成しないため、生成・破棄にかかる計算量やインスタンスを保持するメモリ領域を抑えることができます。

シングルトンパターンを使う場面はほとんどない

「インスタンスが1つしかないことを保証したいとき」にシングルトンパターンが便利だと説明しました。

しかし、このような場面はほとんどありません。

シングルトンパターンを使えばメモリやCPUなどのリソースの消費量を抑えられる場合もありますが、その効果は副次的なものです。

「リソースの消費を抑えるためにシングルトンパターンを使おう」と考えることはなく「設計上はシングルトンパターンを使うのが望ましい。そのついでにリソースの消費も抑えられる」という程度のものです。

そのため、シングルトンパターンを無理に使おうと考える必要はありません。

デザインパターンの1つとして覚えておくだけで十分です。

ただし、プログラミング言語やフレームワーク、ライブラリの実装でシングルトンパターンが使われていることはあります。PythonのNoneオブジェクトもその1つです。

ライブラリなどのドキュメントに「〇〇はシングルトンなオブジェクトです」と書かれていたときに「このクラスのインスタンスはプログラム全体で1つしか存在しないんだなあ」と理解できるとよいです。

シングルトンパターンをPythonで実装

シングルトンパターンについて理解を深めるために、シングルなオブジェクトをPythonで実装してみましょう。

Pythonでシングルトンなオブジェクトを生成するクラスは以下のように実装できます。

class Singleton:

    _instance = None 

    def __new__(cls):
        if cls._instance is None: # インスタンスが作られていなければ作る(既にあれば何もせずあるものを返す)
            cls._instance = super().__new__(cls)
        return cls._instance

Pythonでは__new__メソッドという特別なメソッドをオーバーライドすることで、オブジェクト生成時の動作を変えることができます。

ここではオブジェクトが生成されていなければ生成して、クラス変数の_instanceに代入します。既にオブジェクトが生成されていれば、生成済みのオブジェクトを返します。

インスタンスが作られていなかったら生成して返し、既に作られていれば既存のインスタンスを返すだけです。

これによって、プログラム全体でひとつしかインスタンスが存在しない状態を実現できます。

シングルトンパターンをC#で実装

続いて、C#でもシングルトンパターンを実装してみましょう。

C#でシングルトンパターンを実装するには以下のようにします。

public class Singleton
{
    private static Singleton _instance;

    private Singleton() { }

    public static Singleton Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = new Singleton();
            }
            return _instance;
        }
    }
}

C#でシングルトンパターンを実装するときは、コンストラクタをprivateメソッドにします。

これによって、Singletonクラスのインスタンスを外部から生成できなくなります。

代わりにSingletonクラスのインスタンスを取得するためのInstanceメソッドを作ります。

Instanceメソッドは、自分自身のインスタンスを返します。インスタンスがまだ生成されていなければ、生成して返します。そして_instanceプロパティに代入します。

既にインスタンスを生成していれば、_instanceプロパティに代入されている既存のインスタンスを返します。

このようにしてプログラム全体でインスタンスがひとつしか存在しない状態を実現できます。

シングルトンパターンのまとめ

この記事では、シングルトンパターンを解説しました。

  • シングルトンパターンとは「あるクラスのインスタンスがプログラム全体でたった1つしか作られないようにする」デザインパターン
  • プログラム全体で「そのインスタンスがひとつしか存在しないこと」を保証できるように設計する
  • シングルトンパターンを使う場面はあまり多くない
  • 言語やフレームワークのドキュメントに「〇〇はシングルトンなオブジェクトです」と書かれていたときに「このオブジェクトはプログラム全体でひとつしか存在しないんだなあ」と理解できればまずは十分
  • シングルトンパターンは「インスタンスが作られていなかったら生成して返し、既に作られていれば既存のインスタンスを返す」クラスを実装して実現する

シングルトンパターンを実装する場面は多くありません。

シングルトンパターンが何を意味するかが理解できれば初心者には十分です。

この機会にしっかり理解しましょう。

タイトルとURLをコピーしました