10.1 Cơ bản về tính đa hình
Tính đa hình là một trong những khái niệm then chốt của lập trình hướng đối tượng (OOP). Nói chung, tính đa hình cho phép các đối tượng của các lớp khác nhau xử lý dữ liệu bằng cách sử dụng cùng một interface. Trong ngữ cảnh của JavaScript, điều này có nghĩa là các đối tượng khác nhau có thể có các phương thức cùng tên và các phương thức này có thể được gọi trên các đối tượng mà không cần biết kiểu cụ thể của chúng.
Các loại đa hình
Trong JavaScript, các loại đa hình chính là:
1. Ad-hoc đa hình (ad-hoc polymorphism):
- Đa hình phương thức, gọi các phương thức cùng tên cho các đối tượng kiểu khác nhau
- Các ví dụ bao gồm quá tải hàm và toán tử (mặc dù không được hỗ trợ trực tiếp trong JavaScript, nhưng có thể được mô phỏng)
2. Đa hình kiểu phụ (Subtype polymorphism):
- Đa hình kiểu phụ hoặc bao gồm, khi các đối tượng của các lớp khác nhau, kế thừa từ lớp cơ sở cùng, có thể được xử lý như các đối tượng của lớp cơ sở
- Đây là loại đa hình chính, được thực hiện thông qua kế thừa và các interface
Lợi ích của tính đa hình:
- Đơn giản hóa code: tính đa hình cho phép viết code linh hoạt và tổng quát hơn, có thể hoạt động với các loại đối tượng khác nhau mà không cần biết loại cụ thể của chúng.
- Mở rộng: tính đa hình dễ dàng thêm các loại mới và hành vi vào hệ thống mà không cần thay đổi code hiện có.
- Hỗ trợ: tính đa hình thúc đẩy phân chia trách nhiệm rõ ràng hơn và tăng khả năng đọc và bảo trì của code.
10.2 Ví dụ về tính đa hình trong JavaScript
Đa hình kiểu phụ thông qua kế thừa
Ví dụ 1: Xử lý các loại đối tượng khác nhau bằng một interface
Trong ví dụ này, hàm playWithAnimal nhận một đối tượng kiểu Animal và gọi phương thức makeSound. Các đối tượng Dog và Cat, kế thừa từ Animal, ghi đè phương thức makeSound, và khi gọi phương thức trên mỗi đối tượng nhận được kết quả khác nhau.
class Animal {
makeSound() {
console.log('Some generic sound');
}
}
class Dog extends Animal {
makeSound() {
console.log('Woof!');
}
}
class Cat extends Animal {
makeSound() {
console.log('Meow!');
}
}
function playWithAnimal(animal) {
animal.makeSound();
}
const dog = new Dog();
const cat = new Cat();
playWithAnimal(dog); // Sẽ in ra: Woof!
playWithAnimal(cat); // Sẽ in ra: Meow!
10.3 Đa hình qua interface (Duck Typing)
Trong JavaScript không có hỗ trợ tích hợp cho các interface, như trong các ngôn ngữ khác, như TypeScript hay Java. Thay vào đó sử dụng cách tiếp cận gọi là "kiểu gõ vịt" (Duck Typing). Điều này có nghĩa là đối tượng được coi là phù hợp với interface nếu nó có các phương thức và thuộc tính cần thiết, bất kể loại cụ thể của nó hay khuôn mẫu.
Quy tắc vịt (duck): nếu cái gì đó trông giống vịt, bơi như vịt và quạ như vịt, thì nó có thể là vịt thực sự.
Ví dụ 2: Sử dụng kiểu gõ vịt
Trong ví dụ này, hàm takeOff nhận bất kỳ đối tượng nào có phương thức fly. Các đối tượng Bird và Airplane hiện thực phương thức fly, do đó chúng có thể được truyền vào takeOff.
class Bird {
fly() {
console.log('Flying...');
}
}
class Airplane {
fly() {
console.log('Jet engine roaring...');
}
}
function takeOff(flyingObject) {
flyingObject.fly();
}
const bird = new Bird();
const airplane = new Airplane();
takeOff(bird); // Sẽ in ra: Flying...
takeOff(airplane); // Sẽ in ra: Jet engine roaring...
10.4 Đa hình qua hàm
Trong JavaScript, hàm là các đối tượng bậc nhất, và chúng có thể được truyền và sử dụng để thực hiện hành vi đa hình.
Ví dụ 3: Đa hình qua hàm
Trong ví dụ này, hàm greet nhận một hàm khác làm tham số và gọi nó. Điều này cho phép sử dụng các hàm khác nhau để thực hiện các hành động khác nhau.
function greetMorning() {
console.log('Good morning!');
}
function greetEvening() {
console.log('Good evening!');
}
function greet(greetingFunction) {
greetingFunction();
}
greet(greetMorning); // Sẽ in ra: Good morning!
greet(greetEvening); // Sẽ in ra: Good evening!
10.5 Đa hình qua quá tải phương thức (Mô phỏng)
JavaScript không hỗ trợ trực tiếp quá tải phương thức, như một số ngôn ngữ lập trình khác. Tuy nhiên, có thể mô phỏng quá tải phương thức bằng cách sử dụng các tham số hàm và kiểm tra kiểu của chúng.
Ví dụ 4: Mô phỏng quá tải phương thức
Trong ví dụ này, phương thức add nhận hai tham số và thực hiện các hành động khác nhau dựa trên các kiểu của chúng. Nếu các tham số là số, phương thức sẽ cộng chúng. Nếu các tham số là mảng, phương thức sẽ nối chúng.
class Calculator {
add(a, b) {
if (typeof a === 'number' && typeof b === 'number') {
return a + b;
} else if (Array.isArray(a) && Array.isArray(b)) {
return a.concat(b);
} else {
throw new Error('Invalid arguments');
}
}
}
const calc = new Calculator();
console.log(calc.add(1, 2)); // Sẽ in ra: 3
console.log(calc.add([1, 2], [3, 4])); // Sẽ in ra: [1, 2, 3, 4]
GO TO FULL VERSION