Interface ngầm định
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 46 trên 110.
Vì mọi lớp Dart đều tự động định nghĩa một giao diện, bạn có thể dùng bất kỳ lớp nào làm giao diện, kể cả lớp cụ thể có đầy đủ phần triển khai. Đây là điều mình gọi là giao diện ngầm định.
Xét một lớp có các thuộc tính và phương thức:
class Logger {
String prefix = '[LOG]';
void log(String message) {
print('$prefix $message');
}
void clear() {
print('Clearing logs...');
}
}Khi một lớp khác triển khai Logger, lớp đó phải tự triển khai mọi thứ, bao gồm cả thuộc tính prefix:
class FileLogger implements Logger {
@override
String prefix = '[FILE]';
@override
void log(String message) {
print('$prefix Writing to file: $message');
}
@override
void clear() {
print('Deleting log file...');
}
}Đây là điểm mấu chốt: khi dùng implements, bạn đang nói rằng “lớp (class) của mình sẽ có cùng cấu trúc”, tức là có cùng các phương thức (method) và thuộc tính (property). Bạn không kế thừa bất kỳ code, hàm khởi tạo hay giá trị mặc định nào. Bạn chỉ cam kết tuân theo cấu trúc của giao diện.
Điều này giúp các giao diện ngầm trở nên hữu ích khi tạo ra các thành phần có thể thay thế cho nhau. Code cần một Logger có thể hoạt động với bất kỳ lớp nào triển khai giao diện đó, bất kể cách triển khai hoạt động bên trong như thế nào.
Thử thách
DễHãy xây dựng một hệ thống thông báo để minh họa cách các lớp cụ thể có thể đóng vai trò như giao diện ngầm định (implicit interface). Bạn sẽ tạo một lớp thông báo cơ sở, sau đó triển khai lớp đó theo cách hoàn toàn khác, qua đó cho thấy implements yêu cầu bạn tự cung cấp tất cả thành viên.
Bạn sẽ sắp xếp code vào hai tệp:
notification.dart: Định nghĩa các lớp thông báo tại đây:- Một lớp
Notificationcó thuộc tínhString channelđược đặt thành'default', một phương thứcsend(String message)in ra[channel]: [message], và một phương thứcgetStatus()trả về chuỗi'Notification ready' - Một lớp
EmailNotificationtriển khaiNotification. Vì bạn dùngimplements, bạn phải tự cung cấp thuộc tínhchannel(đặt thành'email'), phương thứcsend()(in raSending email: [message]) và phương thứcgetStatus()(trả về'Email service active') - Một lớp
PushNotificationtriển khaiNotification. Đặtchannelthành'push', đểsend()in raPush alert: [message], và đểgetStatus()trả về'Push service connected'
- Một lớp
main.dart: Nhập tệp thông báo và minh họa cách các lớp khác nhau có thể triển khai cùng một giao diện ngầm định với hành vi hoàn toàn khác nhau:- Tạo một
EmailNotificationvà mộtPushNotification - Với mỗi thông báo, in trạng thái bằng
getStatus(), sau đó gọisend()với thông điệp'Hello World' - In một dòng trống giữa hai thông báo
- Tạo một
Hãy nhớ: khi dùng implements, bạn không kế thừa bất kỳ code nào từ Notification. Bạn chỉ cam kết rằng lớp của mình sẽ có cùng cấu trúc, gồm các thuộc tính và phương thức giống nhau. Mỗi lớp triển khai đều tự cung cấp phần triển khai hoàn chỉnh của mình.
Đầu ra dự kiến:
Email service active
Sending email: Hello World
Push service connected
Push alert: Hello WorldTự làm thử
import 'notification.dart';
void main() {
// TODO: Tạo một instance của EmailNotification
// TODO: Dùng getStatus() để in ra trạng thái của thông báo email
// TODO: Dùng thông báo email để gửi tin nhắn 'Hello World'
// TODO: In ra một dòng trống
// TODO: Tạo một instance của PushNotification
// TODO: Dùng getStatus() để in ra trạng thái của thông báo push
// TODO: Dùng thông báo push để gửi tin nhắn 'Hello World'
}
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ánTự luyện tập: Trình biên dịch Dart online