ウェブエンジニア珍道中

日々の技術的に関する経験を書いていきます。脱線もしますが助けになれば幸いです。

typescriptでデザインパターンを書く -Singleton-

github.com

今回はSingletonパターンを書いてみます。

Singletonパターンとは

アプリケーション内でインスタンスを一つだけ生成することを保証するデザインパターンです。

new Hoge()で作る度に別のインスタンスが作られるというのを制限します。

サンプル

概要

Singletonクラスを使ってインスタンスを2つ作成、同じものかどうか比べるというものです。

登場人物は以下の通りです。

  • Singleton
    • アプリケーションで唯一のインスタンスを作成して返すクラス
  • NotSingleton
    • 比較用のクラス(特に何も加工していない)
  • Main
    • 各クラスのインスタンスを2つずつ作成
    • 同じものか判定する(厳密等価演算子===を使う)

プログラム

Singletonクラス

export class Singleton {
    private static singleton: Singleton = new Singleton();
    private constructor() {
        console.log("インスタンスを作成しました。");
    }
    public static get instance(): Singleton {
        return this.singleton;
    }
}

private constructor()と書いてコンストラクタをプライベートメソッドにすることでnewで生成されるのを防いでいます。

インスタンスの生成には静的変数や静的メソッド(static変数とかクラスメソッドとか言ったりする)を使います。

静的メソッドinstanceを使うと、クラス呼び出し時に生成される静的変数singletonが返されることでインスタンス生成を実現しています。

newで毎回作る訳ではなく、クラスに紐づく変数を毎回返しているだけなので、2つ以上インスタンスが作られることがない状態を作っています。

NotSingletonクラス

export class NotSingleton {
    constructor() {
        console.log("インスタンスを作成しました。");
    }
}

比較用のクラスです、インスタンス作成時の表示の処理以外は特に実装していません。

Main

// singleton

console.log("singletonを使った場合");

import { Singleton } from "./singleton";

var obj1: Singleton = Singleton.instance;
var obj2: Singleton = Singleton.instance;

if (obj1 === obj2) {
    console.log("obj1とobj2は同じインスタンスです。");
} else {
    console.log("obj1とobj2は同じインスタンスではありません。");
}


// not_singleton

console.log("\nsingletonを使わなかった場合");

import { NotSingleton } from "./not_singleton";

var obj3: NotSingleton = new NotSingleton();
var obj4: NotSingleton = new NotSingleton();

if (obj3 === obj4) {
    console.log("obj3とobj4は同じインスタンスです。");
} else {
    console.log("obj3とobj4は同じインスタンスではありません。");
}

各クラスのインスタンスを2つずつ作って同じかどうか比べています。

読み込みのタイミングで静的変数が作られて、メッセージの出力処理がずれるため、importの位置を一番上からずらしています。

出力結果

$ ts-node main.ts
singletonを使った場合
インスタンスを作成しました。
obj1とobj2は同じインスタンスです。

singletonを使わなかった場合
インスタンスを作成しました。
インスタンスを作成しました。
obj3とobj4は同じインスタンスではありません。

singletonを使った場合同じインスタンスが作れていることが確認できました。

使用上の注意

状態を持たない

singletonを適応したクラスにメンバ変数があるとただのグローバル変数となってしまい、色々な箇所から触られた際に非常に混沌とするので状態は持たせないのが鉄則らしいです。

メリット

使い回す時の心配事をなくす

複数の箇所で使いまわされるものを毎回別々に作らずに、同じインスタンスを参照して使えるというところにメリットがあります。いちいち作るのも面倒だし、メモリも無限じゃないし、使い回せるならそれに越したことはないですね。

使い回せる状態を作るだけなら全てにstaticをつければ大丈夫なのですが、間違ってnewでインスタンスを作ってしまうのを防止したい時や、インスタンス作成の処理を制御したりしたい際にこのパターンが使えます。

まとめ

とても知名度の高いデザインパターンらしいです、自分も名前くらいは知っていました。

そして間違った使われ方をされやすく、物議をかもしているデザインパターンでもあるらしいです。シンプルで簡単なものである分、使い方には慎重になった方が良いと思います。

初めてstaticをがっつり使って便利だなーと感じたので活用法も合わせて勉強していきたいです。