Menu
Coddy logo textTech

非同期コンストラクタ

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

Dartのコンストラクタをasyncとしてマークすることはできません。それらはFutureではなく、クラスのインスタンスを即座に返さなければならないからです。では、データベースからのデータ読み込みや設定の取得など、非同期の初期化が必要なオブジェクトをどのように作成すればよいのでしょうか?

解決策は、静的な非同期ファクトリメソッドを使用することです。コンストラクタは非公開(またはシンプル)に保ち、非同期処理を実行してクラスの Future を返す静的メソッドを提供します。

class UserProfile {
  final String name;
  final int age;
  
  UserProfile._(this.name, this.age);  // プライベートコンストラクタ
  
  static Future<UserProfile> create(String userId) async {
    // データの取得をシミュレートする
    await Future.delayed(Duration(seconds: 1));
    return UserProfile._('Alice', 25);
  }
}

Future<void> main() async {
  var user = await UserProfile.create('123');
  print(user.name);  // Alice
}

プライベートコンストラクタ UserProfile._ は実際のオブジェクト作成を処理し、静的な create() メソッドがすべての非同期処理を処理します。呼び出し側は await を使用して、完全に初期化されたオブジェクトを取得します。

このパターンは、オブジェクトを取得した時点でそれが完全に準備が整っていることを保証します。つまり、部分的に初期化された状態やデータの欠落はありません。これは、リソースの読み込み、サービスへの接続、または時間のかかるセットアップを実行する必要があるクラスで一般的によく使用されます。

challenge icon

チャレンジ

簡単

クラスでの非同期初期化の処理方法を示す設定ローダーを作成しましょう!コンストラクタは非同期(async)にできないため、データの「ロード」に時間を要するオブジェクトを作成するには、静的ファクトリメソッドパターンを使用します。

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

  • config_loader.dart: 設定を非同期でロードすることをシミュレートする ConfigLoader クラスを作成します。クラスには以下を含める必要があります:
    • 3つの final フィールド:appName (String)、version (String)、maxUsers (int)
    • これら3つのフィールドを初期化するプライベートコンストラクタ
    • String configId パラメータを受け取り、Future<ConfigLoader> を返す load という名前の静的非同期ファクトリメソッド。このメソッドは以下を行う必要があります:
      • Future.delayed() を100ミリ秒で使用し、ロード時間をシミュレートする
      • configId に基づいて設定値を生成する:
        • appName: configId に " App" を追加したもの
        • version: "1.0." の後に configId の長さを続けたもの
        • maxUsers: configId の長さに10を掛けたもの
      • これらの値を使用して新しい ConfigLoader インスタンスを返す
    • 設定を Config: [appName] v[version] (max [maxUsers] users) という形式で出力する displayInfo() メソッド
  • main.dart: 設定ローダーをインポートし、非同期初期化パターンを実演します:
    • Loading configurations... と出力する
    • configId "Production" で設定をロードし、その displayInfo() を呼び出す
    • configId "Dev" で設定をロードし、その displayInfo() を呼び出す
    • All configs loaded! と出力する

静的な load メソッドがすべての非同期処理を処理し、プライベートコンストラクタは単に値を代入していることに注目してください。呼び出し側は await を使用して、完全に初期化されたオブジェクトを取得します!

期待される出力:

Loading configurations...
Config: Production App v1.0.10 (max 100 users)
Config: Dev App v1.0.3 (max 30 users)
All configs loaded!

チートシート

Dartのコンストラクタは、Futureではなく、直ちにクラスのインスタンスを返さなければならないため、asyncとしてマークすることはできません。

非同期の初期化が必要なオブジェクトを作成するには、static async ファクトリメソッドを使用します。

class UserProfile {
  final String name;
  final int age;
  
  UserProfile._(this.name, this.age);  // Private constructor
  
  static Future<UserProfile> create(String userId) async {
    // Perform async work here
    await Future.delayed(Duration(seconds: 1));
    return UserProfile._('Alice', 25);
  }
}

使用方法:

Future<void> main() async {
  var user = await UserProfile.create('123');
  print(user.name);  // Alice
}

プライベートコンストラクタ(_でマークされたもの)がオブジェクトの作成を処理し、静的メソッドが非同期処理を処理します。これにより、オブジェクトが使用前に完全に初期化されることが保証されます。

自分で試してみよう

import 'config_loader.dart';

void main() async {
  // TODO: "Loading configurations..." を表示する
  
  // TODO: configId が "Production" の設定を読み込む
  // そしてその displayInfo() メソッドを呼び出す
  
  // TODO: configId が "Dev" の設定を読み込む
  // そしてその displayInfo() メソッドを呼び出す
  
  // TODO: "All configs loaded!" を表示する
}
quiz icon腕試し

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

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