🦴

TypeScriptとClassとSingleton

2023/12/03に公開

この記事はごった煮 Advent Calendar 2023の3日目の記事です。

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

シングルトンパターンは、クラスのインスタンスが一つしか存在しないことを保証するデザインパターンです。これは、特定のクラスに対して一度だけインスタンスを生成し、そのインスタンスをアプリケーション全体で共有するために使用されます。
特に、アプリケーション全体の状態管理をしたい、といったユースケースで利用することがあります。
例えば、以下は自作のDiscordBotです(絶賛工事中なのでリンク先が変なブランチになっているのはご容赦ください)
参加中のボイスチャンネルの状態管理に利用しています。
https://github.com/na2na-p/jetdisc/tree/fix/exclude-testingfile

下記記事も参考になるかと思います。
https://zenn.dev/nekoniki/articles/b05c0f1297f301d3bd63

実装方法

以下のリポジトリにテスト含めて掲載しています
https://github.com/na2na-p/training-singleton

パターン1

コンストラクタをprivate化する方法

class Singleton {
    private static instance: Singleton;

    // コンストラクタをprivateにすることで、外部からのインスタンス化を防ぐ
    private constructor() {
        // 初期化処理
    }

    // インスタンスを取得するためのメソッド
    public static getInstance(): Singleton {
        // インスタンスがまだ作成されていない場合は作成する
        if (!Singleton.instance) {
            Singleton.instance = new Singleton();
        }
        // 既存のインスタンスを返す
        return Singleton.instance;
    }

    // インスタンスのビジネスロジックをここに追加
    public someMethod() {
        // ...
    }
}

// 使用例
const instance = Singleton.getInstance();
instance.someMethod();

パターン2

ジェネリックな高階関数を利用する方法
こちらのパターンの実装はあまり見かけないので紹介したいと思います

type Getter<T> = () => T;
type Factory<T> = () => T;

// singleton関数は、与えられたファクトリ関数を使ってSingletonパターンを実装します。
// この関数はGetter<T>型の関数を返し、同じインスタンスを繰り返し返すようにします。
export const singleton = <T>(factory: Factory<T>): Getter<T> =>
  ((): Getter<T> => {
    let memo: T | null = null;
    return () => (memo ? memo : (memo = factory()));
  })();

// Singletonクラスのインスタンスを生成するためのファクトリ関数です。
const createSingletonInstance = () => new Singleton();
// 生成されたSingletonインスタンスを取得するためのGetter関数です。
export const getSingletonInstance = singleton(createSingletonInstance);

class Singleton {
  public someMethod() {
    console.log('someMethod');
  }
}

// 使用例
const instance = getSingletonInstance(Singleton)
instance.someMethod()

どちらが好きか

個人的には後者の方が好きです。
前者と比較するとやや複雑になる印象を受けますが、後者の方がテスト容易性が高いです。
シンプルさと明確性を重視するなら前者を、柔軟性と再利用性を重視するならば後者を選択するといいと思います。

Discussion