Menu
Coddy logo textTech

Singletonパターン

CoddyのDartジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 90/110。

シングルトンパターン (Singleton pattern)は、クラスのインスタンスがアプリケーション全体で1つだけであることを保証し、それに対するグローバルなアクセスポイントを提供します。これは、設定マネージャー、データベース接続、ロギングサービスのように、システム全体のアクションを調整するために、まさに1つのオブジェクトが必要な場合に役立ちます。

Dartでは、ファクトリコンストラクタとプライベートな静的インスタンスを組み合わせて、シングルトンを実装できます。

class AppConfig {
  static final AppConfig _instance = AppConfig._internal();
  
  String appName = 'MyApp';
  String version = '1.0.0';
  
  // プライベートな名前付きコンストラクタ
  AppConfig._internal();
  
  // ファクトリコンストラクタは同じインスタンスを返します
  factory AppConfig() {
    return _instance;
  }
}

void main() {
  var config1 = AppConfig();
  var config2 = AppConfig();
  
  config1.appName = 'UpdatedApp';
  
  print(config2.appName);  // UpdatedApp
  print(identical(config1, config2));  // true
}

プライベートコンストラクタ _internal() は、外部からのインスタンス化を防ぎます。静的な _instance フィールドは、クラスのロード時に一度だけ作成される単一のインスタンスを保持します。AppConfig() を呼び出すたびにこの同じインスタンスが返されるため、ある参照を介して行われた変更はどこからでも確認できます。

シングルトンの使用は控えめにしてください。シングルトンはグローバルな状態を導入するため、テストが困難になる可能性があります。これらは、アプリケーション全体で一貫した状態を必要とする、真に共有されるリソースに最適です。

challenge icon

チャレンジ

簡単

Singletonパターンを使用して、ゲーム設定マネージャーを構築しましょう!アプリケーション全体でゲーム設定のインスタンスが1つだけ存在することを保証するシステムを作成し、ゲームのすべての部分で同じ設定を共有できるようにします。

コードを2つのファイルに整理します:

  • game_settings.dart: Singletonパターンを実装する GameSettings クラスを作成します。設定マネージャーには以下のものを含める必要があります:
    • 単一の GameSettings オブジェクトを保持する private static final なインスタンス
    • 外部からのインスタンス化を防ぐためのプライベートな名前付きコンストラクタ
    • 常に同じインスタンスを返すファクトリコンストラクタ
    • 3つの設定フィールド:difficulty (String、デフォルト: "Normal")、soundEnabled (bool、デフォルト: true)、playerName (String、デフォルト: "Player1")
    • 現在の設定を次の形式で出力するメソッド displaySettings()Settings: [playerName] | Difficulty: [difficulty] | Sound: [on/off] (soundEnabled が true の場合は "on"、false の場合は "off" を出力)
  • main.dart: ゲーム設定をインポートし、Singletonが正しく機能することを実証します:
    • 設定インスタンスを取得し、それを settings1 と呼びます
    • displaySettings() を呼び出してデフォルト値を表示します
    • 設定を変更します:playerName を "Hero" に、difficulty を "Hard" に、soundEnabled を false に変更します
    • 設定への別の参照を取得し、それを settings2 と呼びます
    • settings2displaySettings() を呼び出し、settings1 を通じて行われた変更が反映されていることを証明します
    • settings1settings2 が同一であるかどうかを、次の形式で出力します:Same instance: [true/false]

これはSingletonの主な利点を示しています。インスタンスが1つしかないため、アプリケーションのどこかで行われた変更が、他のすべての場所ですぐに反映されます。

期待される出力:

Settings: Player1 | Difficulty: Normal | Sound: on
Settings: Hero | Difficulty: Hard | Sound: off
Same instance: true

チートシート

シングルトン・パターン (Singleton pattern)は、アプリケーション全体でクラスのインスタンスが1つだけであることを保証し、それに対するグローバルなアクセスポイントを提供します。

Dartでは、ファクトリコンストラクタとプライベートな静的インスタンスを組み合わせてシングルトンを実装します。

class AppConfig {
  static final AppConfig _instance = AppConfig._internal();
  
  String appName = 'MyApp';
  String version = '1.0.0';
  
  // プライベートな名前付きコンストラクタ
  AppConfig._internal();
  
  // ファクトリコンストラクタは常に同じインスタンスを返します
  factory AppConfig() {
    return _instance;
  }
}

主な構成要素:

  • static final _instance: クラスがロードされるときに一度だけ作成される、単一のインスタンスを保持します
  • _internal(): プライベートコンストラクタにより、外部からのインスタンス化を防ぎます
  • factory AppConfig(): 常に同じインスタンスを返します

使用例:

var config1 = AppConfig();
var config2 = AppConfig();

config1.appName = 'UpdatedApp';

print(config2.appName);  // UpdatedApp
print(identical(config1, config2));  // true

すべての参照が同じインスタンスを指しているため、1つの参照を通じて行われた変更はどこからでも確認できます。

自分で試してみよう

import 'game_settings.dart';

void main() {
  // TODO: settings のインスタンスを取得し、それを settings1 と呼びます
  
  // TODO: displaySettings() を呼び出してデフォルト値を表示します
  
  // TODO: 設定を変更します:
  // - playerName を "Hero" に変更します
  // - difficulty を "Hard" に変更します
  // - soundEnabled を false に変更します
  
  // TODO: settings への別の参照を取得し、それを settings2 と呼びます
  
  // TODO: settings2 で displaySettings() を呼び出し、変更が反映されていることを証明します
  
  // TODO: settings1 と settings2 が同一であるかどうかを出力します
  // フォーマット: Same instance: [true/false]
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン