Đa hình qua 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 58 trên 110.
Trong bài học trước, bạn đã tìm hiểu về tính đa hình (polymorphism) thông qua kế thừa (inheritance), tức là dùng kiểu của lớp cha để làm việc với các đối tượng thuộc lớp con. Tính đa hình cũng hoạt động thông qua giao diện (interface), và cách này thường linh hoạt hơn.
Khi các lớp triển khai cùng một giao diện, bạn có thể xử lý chúng theo cách đa hình ngay cả khi chúng không có quan hệ kế thừa:
abstract class Printable {
void printDetails();
}
class Invoice implements Printable {
@override
void printDetails() => print('Invoice #1234');
}
class Report implements Printable {
@override
void printDetails() => print('Monthly Report');
}
class Receipt implements Printable {
@override
void printDetails() => print('Receipt for purchase');
}
void printAll(List<Printable> items) {
for (var item in items) {
item.printDetails();
}
}
void main() {
var documents = [Invoice(), Report(), Receipt()];
printAll(documents);
}Invoice, Report và Receipt là các lớp hoàn toàn không liên quan, chúng không có lớp cha chung. Tuy nhiên, vì tất cả đều triển khai Printable, chúng ta có thể lưu chúng trong cùng một danh sách và gọi printDetails() trên từng lớp.
Cách này rất hữu ích vì cho phép bạn xác định các hợp đồng về hành vi mà không cần áp đặt hệ thống phân cấp kế thừa. Bất kỳ lớp nào cũng có thể triển khai một giao diện, bất kể lớp đó kế thừa lớp nào. Code của bạn trở nên mô-đun hơn vì bạn có thể thêm các kiểu có thể in mới mà không cần sửa code hiện có, chỉ cần triển khai giao diện.
Thử thách
DễHãy xây dựng một hệ thống phát đa phương tiện để minh họa tính đa hình thông qua giao diện. Bạn sẽ tạo một giao diện định nghĩa thế nào là “có thể phát”, sau đó triển khai một số loại đa phương tiện không liên quan với nhau, tất cả đều đáp ứng giao diện này. Nhờ vậy, chúng có thể được xử lý thống nhất dù không có quan hệ kế thừa.
Bạn sẽ sắp xếp code vào hai tệp:
media.dart: Định nghĩa giao diện và các lớp đa phương tiện tại đây:- Một lớp trừu tượng
Playablevới hai phương thức trừu tượng:play()vàString getMediaType() - Một lớp
Songtriển khaiPlayable. Lớp này cần cóString titlevàString artist. Phương thứcplay()in raPlaying song: [title] by [artist], còngetMediaType()trả về'Audio' - Một lớp
Videotriển khaiPlayable. Lớp này cần cóString titlevàint duration(tính bằng phút). Phương thứcplay()in raPlaying video: [title] ([duration] min), còngetMediaType()trả về'Video' - Một lớp
Podcasttriển khaiPlayable. Lớp này cần cóString titlevàint episode. Phương thứcplay()in raPlaying podcast: [title] - Episode [episode], còngetMediaType()trả về'Podcast'
playAll(List<Playable> playlist)để duyệt qua danh sách và in ra[Media Type]:cho từng phần tử, sau đó gọiplay()trên phần tử đó.- Một lớp trừu tượng
main.dart: Nhập tệp đa phương tiện và minh họa tính đa hình dựa trên giao diện:- Tạo một
List<Playable>chứa mộtSongcó tiêu đề'Imagine'và nghệ sĩ'John Lennon', mộtVideocó tiêu đề'Dart Tutorial'và thời lượng15, cùng mộtPodcastcó tiêu đề'Tech Talk'và số tập42 - Truyền danh sách này vào
playAll()
- Tạo một
Hãy chú ý rằng Song, Video và Podcast không có lớp cha chung, chúng hoàn toàn không liên quan với nhau. Tuy nhiên, vì tất cả đều triển khai Playable, bạn có thể lưu chúng trong cùng một danh sách và xử lý thống nhất thông qua giao diện.
Đầu ra dự kiến:
Audio:
Playing song: Imagine by John Lennon
Video:
Playing video: Dart Tutorial (15 min)
Podcast:
Playing podcast: Tech Talk - Episode 42Tự làm thử
import 'media.dart';
void main() {
// TODO: Tạo một List<Playable> gồm:
// - Một Song có tiêu đề 'Imagine' và nghệ sĩ 'John Lennon'
// - Một Video có tiêu đề 'Dart Tutorial' và thời lượng 15
// - Một Podcast có tiêu đề 'Tech Talk' và số tập 42
// TODO: Truyền danh sách vào playAll()
}
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