Phương thức trừu tượng
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 44 trên 110.
Phương thức trừu tượng là phương thức được khai báo bên trong một lớp trừu tượng nhưng không có phần thân. Phương thức này định nghĩa chữ ký phương thức nhưng để mỗi lớp con tự triển khai. Bất kỳ lớp nào kế thừa lớp trừu tượng đều phải ghi đè tất cả các phương thức trừu tượng, nếu không lớp đó cũng sẽ là lớp trừu tượng.
abstract class Animal {
// Phương thức trừu tượng (abstract method): không có thân, các lớp con phải triển khai
String makeSound();
// Phương thức cụ thể (concrete method): có thân, dùng chung cho mọi lớp con
void breathe() {
print('Breathing...');
}
}
class Dog extends Animal {
@override
String makeSound() {
return 'Woof!'; // Phải triển khai phương thức này
}
}
class Cat extends Animal {
@override
String makeSound() {
return 'Meow!'; // Cách triển khai khác nhau, cùng chữ ký
}
}Nếu một lớp con quên triển khai phương thức trừu tượng, Dart sẽ báo lỗi biên dịch. Đây chính là mục đích: phương thức trừu tượng đảm bảo tuân thủ một giao ước. Mọi lớp con đều chắc chắn có phương thức đó, vì vậy bạn có thể an tâm gọi phương thức này trên bất kỳ đối tượng nào thuộc kiểu trừu tượng.
// Bạn có thể dùng kiểu trừu tượng làm kiểu dữ liệu cho biến.
Animal a = Dog();
print(a.makeSound()); // Gâu!
Animal b = Cat();
print(b.makeSound()); // Meo!Phương thức trừu tượng khác với phương thức cụ thể trong lớp trừu tượng. Phương thức cụ thể có phần thân và được kế thừa nguyên vẹn. Phương thức trừu tượng không có phần thân và phải được ghi đè.
Bạn có thể kết hợp cả hai trong cùng một lớp trừu tượng để chia sẻ một số hành vi, đồng thời buộc các lớp con tự định nghĩa hành vi cho những phần còn lại.
| Đặc điểm | Phương thức trừu tượng | Phương thức cụ thể |
|---|---|---|
| Có thân hàm | Không | Có |
| Bắt buộc ghi đè | Có | Không |
| Mục đích | Đảm bảo hợp đồng | Chia sẻ hành vi |
| Phạm vi | Chỉ trong lớp trừu tượng | Bất kỳ lớp nào |
Thử thách
DễHãy xây dựng một chương trình tính toán hình dạng để minh họa cách các phương thức trừu tượng hoạt động. Bạn sẽ định nghĩa một lớp trừu tượng với các phương thức trừu tượng mà mỗi hình dạng phải tự triển khai theo cách riêng, rồi sử dụng các hình dạng đó thông qua một giao diện chung.
Bạn sẽ tổ chức code thành bốn tệp:
shape.dart: Định nghĩa lớp trừu tượngShapevới trườngfinalcolor(String) và hàm khởi tạo để khởi tạo trường này. Khai báo hai phương thức trừu tượng:double area()vàdouble perimeter(). Thêm một phương thức cụ thểdescribe()để in raA [color] shape with area: [area] and perimeter: [perimeter]. Vìarea()vàperimeter()là các phương thức trừu tượng, mỗi lớp con sẽ cung cấp cách tính riêng, nhưngdescribe()dùng được cho tất cả mà không cần ghi đè.circle.dart: Tạo lớpCirclekế thừaShape. Lớp này có trườngfinalradius(double). Hàm khởi tạo nhậncolorvàradius, rồi truyềncolorchosuper. Triển khaiarea()để trả về3.14 * radius * radiusvàperimeter()để trả về2 * 3.14 * radius.rectangle.dart: Tạo lớpRectanglekế thừaShape. Lớp này có các trườngfinalwidthvàheight(đều là double). Hàm khởi tạo nhậncolor,widthvàheight, rồi truyềncolorchosuper. Triển khaiarea()để trả vềwidth * heightvàperimeter()để trả về2 * (width + height).main.dart: Nhập tất cả các tệp. Tạo mộtCirclecó màu'red'và bán kính5.0, cùng mộtRectanglecó màu'blue', chiều rộng4.0và chiều cao6.0. Gọidescribe()cho mỗi hình dạng. Sau đó, tạo mộtList<Shape>chứa cả hai hình dạng, duyệt qua danh sách và in raArea: [area]cho mỗi hình dạng.
Đầu ra dự kiến:
A red shape with area: 78.5 and perimeter: 31.400000000000002
A blue shape with area: 24.0 and perimeter: 20.0
Area: 78.5
Area: 24.0Tự làm thử
import 'shape.dart';
import 'circle.dart';
import 'rectangle.dart';
void main() {
// TODO: Tạo một Circle có màu 'red' và bán kính 5.0
// TODO: Tạo một Rectangle có màu 'blue', chiều rộng 4.0 và chiều cao 6.0
// TODO: Gọi describe() trên hình tròn
// TODO: Gọi describe() trên hình chữ nhật
// TODO: Tạo một List<Shape> chứa cả hai hình
// TODO: Duyệt qua danh sách và in ra "Area: [area]" cho mỗi hình
}
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