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.
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ớpConfigLoadermô 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 configIdvà 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 configIdmaxUsers: độ dài của configId nhân với 10
- Trả về một instance
ConfigLoadermới với các giá trị này
- Dùng
- Một phương thức
displayInfo()in cấu hình theo định dạng sau:Config: [appName] v[version] (max [maxUsers] users)
- Ba trường final:
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ọidisplayInfo()của cấu hình đó - Tải cấu hình với configId
"Dev"rồi gọidisplayInfo()của cấu hình đó - In ra
All configs loaded!
- In ra
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!"
}
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
1Nền tảng về lập trình hướng đối tượng
Tệp bên ngoàiThư viện và câu lệnh importGiới thiệu về lập trình hướng đối tượngLớp và đối tượngTừ khóa thisPhương thứcBiến thể hiệnKiến thức cơ bản về hàm khởi tạoÔn tập: Máy tính đơn giản4An toàn với giá trị null
Giới thiệu về an toàn với giá trị nullCó thể null và không thể nullToán tử ? và !Từ khóa late và an toàn với giá trị nullToán tử xử lý nullAn toàn với giá trị null trong lớpÔn tập: hệ thống hồ sơ người dùng7Lớp trừu tượng và interface
Lớp trừu tượngPhương thức trừu tượngInterface trong DartInterface ngầm địnhTriển khai và kế thừaNhiều interfaceÔn tập: Máy tính hình10Bộ sưu tập và kiểu tổng quát
Tổng quan về List, Set và MapBộ sưu tập an toàn về kiểuLớp tổng quátPhương thức tổng quátRàng buộc kiểu tổng quátIterable và IteratorÔn tập: Lưu trữ tổng quát2Hàm khởi tạo trong Dart
Hàm khởi tạo mặc địnhHàm khởi tạo có tênDanh sách khởi tạoHàm khởi tạo hằng sốHàm khởi tạo nhà máyHàm khởi tạo chuyển hướngÔn tập: Trình tạo hình dạng5Đóng gói
Thành viên công khai và riêng tưQuy ước tiền tố _Tính riêng tư ở cấp thư việnTìm hiểu sâu về getter và setterChe giấu thông tinÔn tập: hồ sơ học sinh8Mixin
Giới thiệu về MixinTạo MixinSử dụng nhiều MixinTừ khóa on trong MixinMixin và kế thừaMixin và giao diệnÔn tập: hệ thống động vật3Thuộc tính của lớp
Thành viên instance và thành viên tĩnhTrường final và constBiến latePhương thức và trường tĩnhGetter và setterÔn tập: trình quản lý tài khoản ngân hàng6Kế thừa
Kế thừa cơ bảnTừ khóa superGhi đè phương thứcChú thích @overrideTừ khóa lớp finalHàm khởi tạo và kế thừaÔn tập: phân cấp nhân viên9Đa hình
Khái niệm cơ bản về đa hìnhĐa hình qua giao diệnKiểm tra kiểu lúc chạyToán tử is và asTừ khóa hiệp biếnÔn tập: Bộ xử lý thanh toán12OOP bất đồng bộ
Future và async/awaitCơ bản về StreamBộ điều khiển StreamHàm khởi tạo bất đồng bộBất đồng bộ trong phương thức lớpÔn tập: Trình lấy dữ liệuTự luyện tập: Trình biên dịch Dart online