Sử dụng abstract classes để tạo ra các lớp cơ sở chung

Thumbnail Image

Abstract classes là một tính năng trong lập trình hướng đối tượng được sử dụng để tạo ra các lớp cơ sở chung cho các lớp con. Các lớp abstract là các lớp chung mà không thể tạo thực thể (tạo ra đối tượng), chỉ có thể được kế thừa và phải được triển khai bởi các lớp con.

 

1. Cách tạo một Abstract Class

Để tạo một lớp abstract, bạn cần sử dụng từ khóa abstract trước tên lớp. Các phương thức abstract cần được triển khai trong các lớp con, và có thể có hoặc không có nội dung.

abstract class Shape {
  abstract getArea(): number;
}

class Circle extends Shape {
  radius: number;

  constructor(radius: number) {
    super();
    this.radius = radius;
  }

  getArea() {
    return Math.PI * this.radius * this.radius;
  }
}

const circle = new Circle(10);
console.log(circle.getArea());

Trong ví dụ trên, lớp Shape là một lớp abstract với một phương thức abstract getArea(). Lớp con Circle kế thừa từ lớp Shape và triển khai phương thức getArea().

 

2. So sánh giữa abtract classes và interfaces.

Abstract classes và Interfaces là hai công cụ trong lập trình hướng đối tượng (OOP) trong TypeScript, mặc dù có một số tính năng tương đồng, nhưng có một số sự khác biệt quan trọng giữa chúng.

Abstract Classes:

- Abstract classes là một lớp có thể chứa mã nguồn và có thể tạo ra các phương thức hoặc thuộc tính có thể được ghi đè.

- Một lớp con có thể kế thừa một hoặc nhiều abstract classes bằng từ khóa extends.

- Abstract classes không thể tạo ra đối tượng của chúng, chỉ có thể tạo ra đối tượng từ lớp con kế thừa từ nó.

Interfaces:

- Interfaces là một tập hợp các phương thức và thuộc tính chỉ mô tả kiểu dữ liệu mà không chứa mã nguồn.

- Một lớp có thể kế thừa một hoặc nhiều interfaces bằng từ khóa implements.

- Interfaces không thể tạo ra đối tượng, chỉ được sử dụng để kiểm soát việc triển khai các phương thức và thuộc tính trong lớp.

Ví dụ minh họa: 

// Abstract Class
abstract class Animal {
    abstract makeSound(): void;

    move(): void {
        console.log("Animal is moving");
    }
}

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

// Interface
interface IMammal {
    makeSound(): void;
}

class Cat implements IMammal {
    makeSound(): void {
        console.log("Meow!");
    }
}

Trong trường hợp này, chúng ta có thể sử dụng abstract class để tạo ra một lớp cơ sở chung cho các lớp con với phương thức abstract và các phương thức thực tế. Còn với interface, chúng ta chỉ có thể định nghĩa các phương thức abstract mà không có thể viết mã nguồn cho các phương thức thực tế. Nói chung, abstract class cung cấp thêm tính đa hình (polymorphism) và tính trừu tượng (abstraction) so với interface.

 

3. Vì sao không tạo ra class thông thường để các lớp khác kế thừa mà phải tạo ra abstract class?

Một abstract class không thể được khởi tạo trực tiếp, chỉ có thể được kế thừa bởi các lớp con (đó là mục đích chúng ta dùng abstract). Tạo một abstract class để làm vậy là để tạo ra một cơ sở chung cho các lớp con, và yêu cầu các lớp con hoàn thiện một số chức năng hoặc biến mà lớp cha abstract cung cấp. Chúng ta không cần dùng lớp đó để tạo ra một đối tượng mới, chỉ cần nó là cơ sở chung cho các lớp khác.

Ví dụ, nếu bạn muốn tạo ra một lớp cho một loại phương tiện giao thông, bạn có thể tạo ra một lớp abstract cha với một phương thức startEngine() và một biến make, model, year. Các lớp con sẽ kế thừa từ lớp cha abstract và hoàn thiện phương thức startEngine() và các biến cho phù hợp với từng loại phương tiện giao thông.

Một ví dụ về lớp động vật:

Chúng ta có một class cha là "Animal" với các thuộc tính như tên, tuổi và phương thức "makeSound()". Chúng ta muốn các lớp con của "Animal" có thể sử dụng những thuộc tính và phương thức của "Animal", nhưng ta muốn sẽ định nghĩa lại phương thức "makeSound()" trong các lớp con.

Trong trường hợp này, chúng ta sẽ tạo ra một abstract class "Animal" và các lớp con sẽ kế thừa từ nó. Abstract class "Animal" sẽ chứa các thuộc tính và phương thức cơ bản của tất cả các loài động vật, và các lớp con sẽ định nghĩa lại phương thức "makeSound()" để tạo ra một âm thanh riêng cho mỗi loài động vật (Chó là gâu gâu, mèo kêu meo meo chẳng hạn).

Trong trường hợp này, tạo một abstract class cho phép chúng ta tạo ra một cấu trúc chung cho các lớp con và đảm bảo rằng các lớp con sẽ có các thuộc tính và phương thức cơ bản đã định nghĩa trong abstract class cha.

Trong khi đó, một interface cung cấp một cách tổng quát cho các lớp implement những gì họ cần thực hiện, nhưng không cung cấp bất kỳ code hoặc biến nào. Do đó, nếu bạn muốn tạo ra một cơ sở chung cho các lớp con, sử dụng abstract class sẽ là tùy chọn tốt hơn so với interfaces.