# TypeScript - Lớp trừu tượng

Xin chào các bạn đang theo đuổi lập trình! Hôm nay, chúng ta sẽ bắt đầu một hành trình đầy thú vị vào thế giới của TypeScript và khám phá một trong những tính năng mạnh mẽ của nó: Lớp trừu tượng. Đừng lo lắng nếu bạn là người mới bắt đầu lập trình; tôi sẽ hướng dẫn bạn từng bước qua khái niệm này, giống như tôi đã làm cho hàng trăm học sinh trong những năm dạy học của mình. Vậy, hãy cùng lặn sâu vào!

## Lớp trừu tượng là gì?

Trước khi chúng ta đi vào chi tiết về các lớp trừu tượng, hãy bắt đầu với một ví dụ đơn giản. Hãy tưởng tượng bạn đang ở một showroom ô tô và bạn thấy một biển hiệu写着 "Phương tiện". Bây giờ, bạn không thể mua một "phương tiện" vì nó quá chung chung. Bạn cần chọn một loại phương tiện cụ thể, như xe hơi, xe tải hoặc xe máy. Trong lập trình, một lớp trừu tượng giống như khái niệm chung "phương tiện" đó - nó là một bản đồ cho các lớp khác, nhưng bạn không thể tạo một đối tượng trực tiếp từ nó.

Các lớp trừu tượng trong TypeScript đóng vai trò là các lớp cơ sở từ đó các lớp khác có thể kế thừa. Chúng có thể chứa các phương thức trừu tượng (các phương thức không có thân) và các phương thức cụ thể (các phương thức có thân). Điều quan trọng cần nhớ là bạn không thể tạo một thể hiện của một lớp trừu tượng trực tiếp.

## Tạo các lớp trừu tượng

Bây giờ, hãy xem cách chúng ta tạo một lớp trừu tượng trong TypeScript. Chúng ta sử dụng từ khóa `abstract` trước từ khóa `class`. Dưới đây là cấu trúc cơ bản:

```typescript
abstract class ClassName {
    // Các thuộc tính và phương thức ở đây
}

Phương thức trừu tượng

Các lớp trừu tượng có thể có các phương thức trừu tượng. Đây là các phương thức được khai báo nhưng không có việc triển khai trong lớp trừu tượng. Bất kỳ lớp nào kế thừa lớp trừu tượng này phải cung cấp việc triển khai cho các phương thức này.

TypeScript - Abstract Classes

Phương thức cụ thể

Các lớp trừu tượng cũng có thể có các phương thức cụ thể, tức là các phương thức đã được triển khai hoàn chỉnh và có thể kế thừa bởi các lớp con.

Ví dụ 1: Lớp trừu tượng Shape

Hãy tạo một ví dụ đơn giản để minh họa cách các lớp trừu tượng hoạt động. Chúng ta sẽ tạo một lớp trừu tượng Shape với một phương thức trừu tượng calculateArea().

abstract class Shape {
    color: string;

    constructor(color: string) {
        this.color = color;
    }

    abstract calculateArea(): number;

    displayColor(): void {
        console.log(`This shape is ${this.color}`);
    }
}

Trong ví dụ này:

  • Shape là lớp trừu tượng của chúng ta.
  • color là thuộc tính mà tất cả các hình sẽ có.
  • calculateArea() là phương thức trừu tượng. Lưu ý rằng nó không có thân, chỉ có khai báo.
  • displayColor() là phương thức cụ thể mà tất cả các hình có thể sử dụng như-is.

Bây giờ, hãy tạo một số hình cụ thể kế thừa lớp Shape của chúng ta:

class Circle extends Shape {
    radius: number;

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

    calculateArea(): number {
        return Math.PI * this.radius * this.radius;
    }
}

class Rectangle extends Shape {
    width: number;
    height: number;

    constructor(color: string, width: number, height: number) {
        super(color);
        this.width = width;
        this.height = height;
    }

    calculateArea(): number {
        return this.width * this.height;
    }
}

Bây giờ chúng ta có thể sử dụng các lớp này:

const circle = new Circle("red", 5);
console.log(circle.calculateArea()); // Output: 78.53981633974483
circle.displayColor(); // Output: This shape is red

const rectangle = new Rectangle("blue", 4, 6);
console.log(rectangle.calculateArea()); // Output: 24
rectangle.displayColor(); // Output: This shape is blue

Trong ví dụ này, cả CircleRectangle kế thừa lớp Shape và cung cấp việc triển khai riêng của họ cho phương thức calculateArea(). Họ cũng kế thừa phương thức displayColor() từ lớp Shape.

Ví dụ 2: Lớp trừu tượng Animal

Hãy tạo một ví dụ khác để củng cố sự hiểu biết của chúng ta. Lần này, chúng ta sẽ tạo một lớp trừu tượng Animal:

abstract class Animal {
    name: string;

    constructor(name: string) {
        this.name = name;
    }

    abstract makeSound(): void;

    move(distance: number = 0): void {
        console.log(`${this.name} moved ${distance} meters.`);
    }
}

class Dog extends Animal {
    constructor(name: string) {
        super(name);
    }

    makeSound(): void {
        console.log("Woof! Woof!");
    }
}

class Cat extends Animal {
    constructor(name: string) {
        super(name);
    }

    makeSound(): void {
        console.log("Meow!");
    }
}

Hãy sử dụng các lớp này:

const dog = new Dog("Buddy");
dog.makeSound(); // Output: Woof! Woof!
dog.move(10); // Output: Buddy moved 10 meters.

const cat = new Cat("Whiskers");
cat.makeSound(); // Output: Meow!
cat.move(5); // Output: Whiskers moved 5 meters.

Trong ví dụ này, Animal là lớp trừu tượng với phương thức trừu tượng makeSound() và phương thức cụ thể move(). DogCat kế thừa Animal và cung cấp việc triển khai riêng của họ cho phương thức makeSound().

Tại sao sử dụng các lớp trừu tượng?

Bạn có thể tự hỏi, "Tại sao phải phiền phức như vậy? Tại sao không chỉ sử dụng các lớp thường?" Well, các lớp trừu tượng vô cùng hữu ích khi bạn muốn xác định một giao diện chung cho một tập hợp các lớp liên quan. Chúng cho phép bạn:

  1. Xác định một cấu trúc chung cho một nhóm các lớp liên quan.
  2. Buộc các lớp con phải triển khai một số phương thức nhất định.
  3. Cung cấp một số chức năng chung mà tất cả các lớp con có thể sử dụng.

Hãy nghĩ về nó như đang tạo một mẫu hoặc hợp đồng mà các lớp khác phải tuân theo. Đây là cách đảm bảo sự nhất quán giữa các lớp liên quan trong khi vẫn cho phép tùy chỉnh khi cần thiết.

Các phương thức trong các lớp trừu tượng

Dưới đây là bảng tóm tắt các loại phương thức bạn có thể có trong các lớp trừu tượng:

Loại phương thức Mô tả Có thể gọi trên lớp trừu tượng? Phải triển khai bởi lớp con?
Phương thức trừu tượng Được khai báo mà không có việc triển khai Không
Phương thức cụ thể Phương thức đã triển khai hoàn chỉnh Không (có thể bị ghi đè)

Kết luận

Và đây là, các bạn! Chúng ta đã cùng nhau hành trình qua thế giới của các lớp trừu tượng trong TypeScript. Từ việc hiểu chúng là gì, đến việc tạo chúng,再到 việc thấy chúng trong hành động với các ví dụ thực tế, bạn现在已经 có một nền tảng vững chắc về tính năng mạnh mẽ này của TypeScript.

Nhớ rằng, các lớp trừu tượng giống như bản đồ cho một công trình. Chúng cung cấp cấu trúc và một số chi tiết, nhưng các lớp kế thừa chúng phải điền vào các chi tiết cụ thể và mang chúng đến cuộc sống.

Khi bạn tiếp tục hành trình lập trình của mình, bạn sẽ thấy rằng các lớp trừu tượng là công cụ vô cùng hữu ích trong bộ công cụ TypeScript của bạn. Chúng giúp bạn viết mã sạch sẽ, tổ chức tốt hơn và dễ bảo trì hơn. Vậy, hãy tiếp tục抽象 hóa và viết mã một cách vui vẻ!

Chúc các bạn lập trình vui vẻ, và cho đến lần gặp lại, hãy tiếp tục khám phá và học hỏi!

Credits: Image by storyset