Menu
CoddyTech

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.

challenge icon

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 Notification có thuộc tính String channel được đặt thành 'default', một phương thức send(String message) in ra [channel]: [message], và một phương thức getStatus() trả về chuỗi 'Notification ready'
    • Một lớp EmailNotification triển khai Notification. Vì bạn dùng implements, bạn phải tự cung cấp thuộc tính channel (đặt thành 'email'), phương thức send() (in ra Sending email: [message]) và phương thức getStatus() (trả về 'Email service active')
    • Một lớp PushNotification triển khai Notification. Đặt channel thành 'push', để send() in ra Push alert: [message], và để getStatus() trả về 'Push service connected'
  • 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 EmailNotification và một PushNotification
    • Với mỗi thông báo, in trạng thái bằng getStatus(), sau đó gọi send() với thông điệp 'Hello World'
    • In một dòng trống giữa hai thông báo

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 World

Tự 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'
  
}
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