Menu
CoddyTech

Hàm khởi tạo bất đồng bộ

Thuộc phần Lập trình hướng đối tượng trong hành trình Dart của Coddy. Bài học 79 trên 110.

Trong Dart, bạn không thể đánh dấu hàm khởi tạo là async, vì hàm phải trả về một instance của lớp ngay lập tức, chứ không phải Future. Vậy bạn tạo các đối tượng cần khởi tạo bất đồng bộ như thế nào, chẳng hạn như khi tải dữ liệu từ cơ sở dữ liệu hoặc lấy cấu hình?

Giải pháp là dùng một phương thức khởi tạo bất đồng bộ tĩnh. Giữ hàm khởi tạo ở trạng thái riêng tư (hoặc đơn giản), rồi cung cấp một phương thức tĩnh thực hiện công việc bất đồng bộ và trả về một Future của lớp bạn:

class UserProfile {
  final String name;
  final int age;
  
  UserProfile._(this.name, this.age);  // Hàm khởi tạo riêng tư
  
  static Future<UserProfile> create(String userId) async {
    // Mô phỏng việc lấy dữ liệu
    await Future.delayed(Duration(seconds: 1));
    return UserProfile._('Alice', 25);
  }
}

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

Hàm khởi tạo riêng tư UserProfile._ xử lý việc tạo đối tượng thực tế, còn phương thức tĩnh create() xử lý toàn bộ công việc bất đồng bộ. Bên gọi dùng await để nhận đối tượng đã được khởi tạo đầy đủ.

Mẫu này giúp đối tượng sẵn sàng để sử dụng ngay khi bạn nhận được, không ở trạng thái khởi tạo dở dang hoặc thiếu dữ liệu. Mẫu này thường dùng cho các lớp cần tải tài nguyên, kết nối với dịch vụ hoặc thực hiện những bước thiết lập mất thời gian.

challenge icon

Thử thách

Dễ

Hãy xây dựng một trình tải cấu hình để minh họa cách xử lý việc khởi tạo bất đồng bộ trong các lớp! Vì hàm khởi tạo không thể chạy bất đồng bộ, bạn sẽ dùng mẫu phương thức factory tĩnh để tạo các đối tượng cần thời gian để “tải” dữ liệu.

Bạn sẽ sắp xếp code thành hai tệp:

  • config_loader.dart: Tạo một lớp ConfigLoader mô phỏng việc tải các thiết lập cấu hình bất đồng bộ. Lớp của bạn cần có:
    • Ba trường final: appName (String), version (String) và maxUsers (int)
    • Một hàm khởi tạo riêng tư khởi tạo cả ba trường
    • Một hàm factory bất đồng bộ tĩnh có tên load, nhận tham số String configId và trả về Future<ConfigLoader>. Hàm này cần:
      • Dùng Future.delayed() với 100 mili giây để mô phỏng thời gian tải
      • Tạo các giá trị cấu hình dựa trên configId:
        • appName: configId được nối thêm " App"
        • version: "1.0." theo sau là độ dài của configId
        • maxUsers: độ dài của configId nhân với 10
      • Trả về một instance ConfigLoader mới với các giá trị này
    • Một phương thức displayInfo() in cấu hình theo định dạng sau: Config: [appName] v[version] (max [maxUsers] users)
  • main.dart: Nhập trình tải cấu hình và minh họa mẫu khởi tạo bất đồng bộ:
    • In ra Loading configurations...
    • Tải cấu hình với configId "Production" rồi gọi displayInfo() của cấu hình đó
    • Tải cấu hình với configId "Dev" rồi gọi displayInfo() của cấu hình đó
    • In ra All configs loaded!

Hãy chú ý cách phương thức tĩnh load xử lý toàn bộ công việc bất đồng bộ, còn hàm khởi tạo riêng tư chỉ gán các giá trị. Bên gọi dùng await để nhận một đối tượng đã được khởi tạo hoàn chỉnh!

Đầu ra dự kiến:

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

Tự làm thử

import 'config_loader.dart';

void main() async {
  // TODO: In ra "Loading configurations..."
  
  // TODO: Tải cấu hình có configId là "Production"
  // và gọi phương thức displayInfo() của cấu hình đó
  
  // TODO: Tải cấu hình có configId là "Dev"
  // và gọi phương thức displayInfo() của cấu hình đó
  
  // TODO: In ra "All configs loaded!"
}
quiz iconTự kiểm tra

Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.

Tất cả bài học trong Lập trình hướng đối tượng

Tự luyện tập: Trình biên dịch Dart online