Mixin và giao diện
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 55 trên 110.
Bạn đã thấy mixin khác với kế thừa như thế nào. Giờ hãy so sánh mixin với interface, vì cả hai đều cho phép bạn làm việc với nhiều kiểu dữ liệu. Điểm khác biệt chính nằm ở những gì chúng cung cấp: interface xác định một hợp đồng, còn mixin cung cấp cách triển khai.
Khi triển khai một giao diện, bạn cam kết cung cấp một số phương thức nhất định, nhưng phải tự viết toàn bộ code. Khi dùng mixin, bạn có sẵn code mà không cần viết.
// Cách dùng interface: bạn phải tự triển khai mọi thứ
abstract class Printable {
void printInfo();
}
class Report implements Printable {
@override
void printInfo() {
print('Printing report...'); // Bạn viết phần này
}
}
// Cách dùng mixin: đã có sẵn phần triển khai
mixin Printable {
void printInfo() {
print('Printing...'); // Phần này đã được viết sẵn cho bạn
}
}
class Report with Printable {} // Tự động có printInfo()Vậy khi nào bạn nên dùng từng loại? Hãy dùng interface khi các lớp khác nhau cần cùng chữ ký phương thức nhưng cách triển khai khác nhau. Hãy dùng mixin khi các lớp cần chia sẻ chính xác cùng một hành vi.
| Tính năng | Giao diện | Mixin |
|---|---|---|
| Cung cấp | Chỉ hợp đồng | Hợp đồng và code |
| Cách triển khai | Bạn tự viết | Có sẵn |
| Từ khóa | implements | with |
| Phù hợp nhất với | Các hành vi khác nhau, cùng API | Hành vi dùng chung |
Bạn thậm chí có thể kết hợp cả hai cách: dùng interface để xác định lớp có thể làm gì, và dùng mixin để cung cấp các cách triển khai dùng chung khi phù hợp. Trong Dart, khi kết hợp cả hai, luôn đặt with trước implements:
class MyClass with SomeMixin implements SomeInterface {
// with đứng trước implements
}Thử thách
DễHãy xây dựng một hệ thống thông báo để minh họa khi nào nên dùng giao diện (interface) và mixin. Bạn sẽ tạo một giao diện cho các thông báo cần những cách triển khai khác nhau, cùng một mixin cho hành vi ghi nhật ký dùng chung, vốn cần giống nhau ở tất cả các loại thông báo.
Bạn sẽ sắp xếp code thành ba tệp:
notifiable.dart: Định nghĩa một lớp trừu tượngNotifiableđóng vai trò như một giao diện. Lớp này cần khai báo hai phương thức trừu tượng:send(String message)vàgetChannel(), phương thức sau trả về mộtString. Mỗi loại thông báo sẽ triển khai các phương thức này theo cách riêng, tùy vào kênh cụ thể.logging.dart: Tạo một mixinLoggingcung cấp hành vi dùng chung cho mọi thông báo. Thêm phương thứclogSent(String channel, String message)để in ra[LOG] Sent via [channel]: [message]và phương thứclogTimestamp()để in ra[LOG] Timestamp recorded. Hành vi này giống nhau ở mọi loại thông báo, nên rất phù hợp để đưa vào mixin.main.dart: Nhập cả hai tệp và tạo hai lớp thông báo kết hợp giao diện với mixin:- Một lớp
EmailNotificationsử dụng mixinLoggingvà triển khaiNotifiable. Phương thứcgetChannel()cần trả về'Email', còn phương thứcsend()cần in raSending email: [message], sau đó gọilogSent()với kênh và thông điệp, rồi gọilogTimestamp() - Một lớp
SMSNotificationsử dụng mixinLoggingvà triển khaiNotifiable. Phương thứcgetChannel()cần trả về'SMS', còn phương thứcsend()cần in raSending SMS: [message], sau đó gọilogSent()với kênh và thông điệp, rồi gọilogTimestamp()
EmailNotificationvà gọisend('Hello World'), in ra một dòng trống, tạo mộtSMSNotificationvà gọisend('Quick update')- Một lớp
Lưu ý rằng giao diện buộc mỗi lớp phải tự viết phần triển khai send() và getChannel() (hành vi khác nhau), còn mixin cung cấp các phương thức ghi nhật ký hoạt động giống nhau ở cả hai lớp (hành vi dùng chung).
Đầu ra dự kiến:
Sending email: Hello World
[LOG] Sent via Email: Hello World
[LOG] Timestamp recorded
Sending SMS: Quick update
[LOG] Sent via SMS: Quick update
[LOG] Timestamp recordedTự làm thử
import 'notifiable.dart';
import 'logging.dart';
// TODO: Tạo lớp EmailNotification có các đặc điểm sau:
// - sử dụng mixin Logging (with)
// - triển khai Notifiable (implements)
// - getChannel() trả về 'Email'
// - send() in ra 'Sending email: [message]', rồi gọi logSent(), sau đó gọi logTimestamp()
// TODO: Tạo lớp SMSNotification có các đặc điểm sau:
// - sử dụng mixin Logging (with)
// - triển khai Notifiable (implements)
// - getChannel() trả về 'SMS'
// - send() in ra 'Sending SMS: [message]', rồi gọi logSent(), sau đó gọi logTimestamp()
void main() {
// TODO: Tạo một EmailNotification và gọi send('Hello World')
// TODO: In ra một dòng trống
// TODO: Tạo một SMSNotification và gọi send('Quick update')
}
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