Method Overriding trong TypeScript là một khái niệm quan trọng trong lập trình hướng đối tượng, cho phép các lớp con (subclasses) cung cấp một cách cài đặt (implementation) khác cho các phương thức đã được định nghĩa trong lớp cha (parent class). Điều này cho phép lớp con có thể điều chỉnh hoặc mở rộng chức năng của các phương thức mà không cần thay đổi mã nguồn của lớp cha.

Trong phần này, chúng ta sẽ tìm hiểu về cách thức hoạt động của method overriding, các quy tắc cần tuân thủ, và cách thực hiện điều này trong TypeScript thông qua các ví dụ cụ thể.

Cách thức hoạt động của Method Overriding

Định nghĩa phương thức trong lớp cha

Khi bạn định nghĩa một phương thức trong lớp cha, bạn có thể cung cấp logic cho phương thức đó. Lớp con sẽ có thể kế thừa phương thức này và nếu cần, có thể cung cấp một cài đặt mới.

class Animal {
  makeSound(): void {
    console.log("Some generic animal sound");
  }
}

Trong ví dụ trên, lớp Animal có một phương thức makeSound với một cài đặt mặc định.

Kế thừa và overriding trong lớp con

Khi một lớp con kế thừa từ lớp cha, nó có thể thay thế cài đặt của phương thức đã định nghĩa trong lớp cha bằng cách định nghĩa lại phương thức đó trong lớp con.

class Dog extends Animal {
  makeSound(): void {
    console.log("Woof! Woof!");
  }
}

Trong ví dụ trên, lớp Dog đã overriding phương thức makeSound để cung cấp một cài đặt riêng cho chó, thay vì sử dụng cài đặt mặc định của lớp Animal.

Sử dụng các lớp

Bây giờ chúng ta có thể tạo một thể hiện (instance) của lớp Dog và gọi phương thức makeSound.

const myDog = new Dog();
myDog.makeSound(); // Woof! Woof!

Nếu bạn tạo một thể hiện của lớp Animal và gọi phương thức makeSound, bạn sẽ thấy kết quả khác:

const myAnimal = new Animal();
myAnimal.makeSound(); // Some generic animal sound

Quy tắc của Method Overriding

Phương thức phải có cùng tên và kiểu trả về

Khi overriding một phương thức, phương thức trong lớp con phải có cùng tên, cùng kiểu tham số và kiểu trả về với phương thức trong lớp cha. Điều này đảm bảo tính nhất quán và tránh gây nhầm lẫn.

class Cat extends Animal {
  // Đúng: phương thức này có cùng tên và kiểu trả về
  makeSound(): void {
    console.log("Meow! Meow!");
  }
}

// Sai: sẽ báo lỗi nếu kiểu trả về không khớp
class Bird extends Animal {
  makeSound(): string { // Lỗi kiểu trả về
    return "Chirp! Chirp!";
  }
}

Phương thức có thể được định nghĩa là virtual

Mặc dù TypeScript không hỗ trợ từ khóa virtual như trong một số ngôn ngữ lập trình khác, bạn có thể thực hiện một cách tương tự bằng cách sử dụng từ khóa protected hoặc public để xác định các phương thức mà có thể được overriding.

class Shape {
  draw(): void {
    console.log("Drawing a shape");
  }
}

class Circle extends Shape {
  draw(): void {
    console.log("Drawing a circle");
  }
}

Ghi đè (Overloading) và Ghi đè phương thức (Overriding)

Cần phân biệt giữa overloading (gọi là nạp chồng) và overriding (gọi là ghi đè). Overloading cho phép bạn định nghĩa nhiều phương thức có cùng tên nhưng khác tham số trong cùng một lớp, trong khi overriding cho phép bạn thay thế cài đặt của phương thức trong lớp cha bằng cài đặt mới trong lớp con.

class Calculator {
  add(a: number, b: number): number; // Phương thức nạp chồng
  add(a: string, b: string): string; // Phương thức nạp chồng
  add(a: any, b: any): any { // Phương thức cài đặt
    return a + b;
  }
}

const calc = new Calculator();
console.log(calc.add(2, 3)); // 5
console.log(calc.add("Hello, ", "World!")); // Hello, World!

Lợi ích của Method Overriding

Tính linh hoạt và khả năng mở rộng

Một trong những lợi ích lớn nhất của việc sử dụng method overriding là khả năng linh hoạt. Bạn có thể thay đổi hành vi của lớp con mà không cần thay đổi mã trong lớp cha. Điều này rất hữu ích khi bạn cần mở rộng chức năng mà không muốn làm phức tạp mã nguồn.

Đơn giản hóa mã nguồn

Method overriding giúp giữ mã nguồn sạch sẽ và dễ hiểu hơn. Thay vì sao chép mã từ lớp cha vào lớp con, bạn chỉ cần cung cấp một cài đặt mới cho phương thức đã tồn tại.

Tăng cường khả năng kiểm soát

Khi bạn override phương thức trong lớp con, bạn có thể kiểm soát chính xác cách mà các lớp con hoạt động. Điều này cho phép bạn tạo ra các cài đặt tùy chỉnh cho các lớp con mà vẫn giữ được sự nhất quán trong toàn bộ mã.

Kết luận

Method Overriding trong TypeScript là một kỹ thuật quan trọng trong lập trình hướng đối tượng, cho phép các lớp con thay thế các phương thức đã định nghĩa trong lớp cha với các cài đặt tùy chỉnh. Kỹ thuật này mang lại nhiều lợi ích, bao gồm tính linh hoạt, khả năng mở rộng, và khả năng kiểm soát tốt hơn đối với mã nguồn. Thông qua các ví dụ và quy tắc rõ ràng, bạn có thể dễ dàng áp dụng method overriding trong các ứng dụng TypeScript của mình, tạo ra mã nguồn sạch sẽ, dễ bảo trì và mạnh mẽ.