シングルトンパターン
CoddyのCジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 54/61。
プログラム全体を通して、オブジェクトのインスタンスがちょうど1つだけ必要になることがあります。たとえば、設定マネージャー、ロギングシステム、データベース接続などです。Singleton パターンは、インスタンスが1つだけ存在することを保証し、それへのグローバルなアクセス ポイントを提供します。
Cでは、ソースファイル内のstatic変数を使ってこれを実装します。ファイルスコープでのstaticは、その変数がそのファイルに対して private であり、プログラムの存続期間中保持されることを意味する点を覚えておいてください。getter function と組み合わせることで、これにより Singleton が作成されます。
// config.c
#include "config.h"
#include <stdlib.h>
struct Config {
int max_connections;
int timeout;
};
static Config* instance = NULL;
Config* get_config(void) {
if (instance == NULL) {
instance = malloc(sizeof(Config));
instance->max_connections = 100;
instance->timeout = 30;
}
return instance;
}魔法はget_config()で起こります。first callでinstanceが作成および初期化されます。その後のすべてのcallは同じpointerをreturnsします。
staticキーワードにより、instanceは関数呼び出しの間もその値を保持し、他のファイルから隠されたままになります。
ヘッダーから、ユーザーにはgetter関数だけが見えます。static変数に直接アクセスしたり、変更したりすることはできません。get_config()を何度、またどこからcallしても、常に同じオブジェクトを取得します。
チャレンジ
簡単Singletonパターンを使用してアプリケーション全体の設定を管理するSettingsモジュールを構築しましょう。コードが設定を何回要求しても、常にまったく同じinstanceを受け取るようにします。
コードを3つのファイルに分けて整理します。
settings.h:forward declarationを使用して不透明なSettings型をDeclareします(structの本体は隠されたままです)。Singletoninstanceへのpointerをreturnsするget_settingsfunctionに加えて、2つの設定値であるvolume(整数)とbrightness(整数)用のgetterおよびsetterfunctionsをDeclareします。ここではincludeガードが不可欠です。settings.c:ここで処理の中心部分をImplementします。volumeとbrightnessのfieldsを持つ実際のSettingsstructをDefineします。単一のinstanceを保持するstaticpointer variableをCreateし、NULLで初期化します。firstのCallでinstanceを割り当て、デフォルト値(volume:50、brightness:75)で初期化し、それ以降のCallではexistinginstanceを単純にreturnするようにget_settingsをImplementします。getterとsetterをImplementして、隠されたfieldsにアクセスし、変更できるようにします。main.c:Singletonが正しく機能することを実証します。新しいvolume値とbrightness値を表す2つの整数を読み取ります。get_settingsを2回Callし、結果を2つのdifferentなpointer variablesに格納します。firstのpointerを使用して、input値でvolumeとbrightnessを更新します。次にsecondのpointerを使用して、両方の値をretrieveしてprintします。Singletonが機能していれば、1つのpointerを通じて行った変更がもう一方のpointerからも見えるようになります。
プログラムは2つのinputを受け取ります。
- 新しいvolume値
- 新しいbrightness値
1つのpointerを通じて値を変更し、もう一方のpointerから読み取った後、volumeとbrightnessをそれぞれ別の行にprintします。
inputが80と100の場合の出力例:
80
100inputが25と60の場合の出力例:
25
60inputが0と50の場合の出力例:
0
50重要な点は、bothのpointersがメモリ上のsame objectをpointしていることです。これがSingletonパターンの本質です。source file内のstatic variableにより、only 1つのinstanceが存在することが保証され、そのinstanceはプログラムの存続期間全体にわたって保持されます。
自分で試してみよう
#include <stdio.h>
#include "settings.h"
int main() {
int new_volume, new_brightness;
scanf("%d", &new_volume);
scanf("%d", &new_brightness);
// TODO: get_settings を2回呼び出し、2つの異なるポインタ変数に格納する
// TODO: 最初のポインタを使って、入力値で volume と brightness を更新する
// TODO: 2番目のポインタを使って volume と brightness を取得して表示する
// (これは両方のポインタが同じシングルトンインスタンスを指していることを示しています)
return 0;
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
自分で練習してみよう: Cオンラインコンパイラ