Site logo
Authors
  • avatar Nguyễn Đức Xinh
    Name
    Nguyễn Đức Xinh
    Twitter
Published on
Published on

Abstract class trong Lập Trình Hướng Đối Tượng (OOP)

Lập trình hướng đối tượng (OOP) được xây dựng trên nền tảng các khái niệm như đóng gói, kế thừa, đa hình và trừu tượng. Trong số đó, lớp trừu tượng đóng vai trò quan trọng trong việc cho phép tái sử dụng mã, xác định cấu trúc, và thúc đẩy thiết kế phần mềm tốt. Trong blog này, chúng ta sẽ khám phá lớp trừu tượng là gì, tại sao chúng quan trọng và cách sử dụng chúng hiệu quả trong các dự án phần mềm của bạn.

Abstract Class là gì?

Lớp trừu tượng là lớp không thể tự khởi tạo, mà chỉ có thể được kế thừa. Lớp này đóng vai trò là bản thiết kế cho các lớp khác và có thể chứa cả phương thức trừu tượng (phương thức không có thân) và phương thức cụ thể (phương thức có thân).

Về bản chất, lớp trừu tượng định nghĩa những gì một lớp nên làm (thông qua các phương thức trừu tượng) và cũng có thể cung cấp cách thực hiện (thông qua các phương thức cụ thể). Điều này làm cho lớp trừu tượng trở thành một công cụ mạnh mẽ để tạo mã có thể tái sử dụng và mở rộng.

Các Điểm Chính:

  1. Không Thể Khởi Tạo - No Instantiation: Bạn không thể tạo một đối tượng từ Abstract Class.
  2. Phương Thức Trừu Tượng - Abstract Methods: Abstract Class có thể chứa các phương thức trừu tượng mà các lớp con phải triển khai.
  3. Phương Thức Cụ Thể - Concrete Methods: Abstract Class có thể chứa các phương thức cụ thể với triển khai sẵn.
  4. Constructor và Fields: Abstract Class có thể có constructor, các trường dữ liệu và phương thức với triển khai đầy đủ, giúp cho việc chia sẻ trạng thái và hành vi chung giữa các lớp con.
  5. Kế Thừa - Inheritance: Các lớp con kế thừa Abstract Class phải triển khai tất cả các phương thức trừu tượng.
  6. Đơn Kế Thừa - Single Inheritance: Một lớp chỉ có thể kế thừa từ một Abstract Class duy nhất, không như Interface có thể implement nhiều cái cùng lúc.

Tại Sao Nên Sử Dụng Abstract Class?

Abstract Class giúp bạn tạo ra một cơ sở chung cho các lớp con, thúc đẩy tính tái sử dụng mã và giảm sự trùng lặp. Dưới đây là một số lý do chính:

  1. Tính Trừu Tượng (Abstraction): Abstract Class cho phép bạn định nghĩa các phương thức trừu tượng mà các lớp con phải triển khai, giúp tập trung vào những gì cần làm hơn là cách làm.
  2. Tái Sử Dụng Mã (Code Reusability): Các phương thức cụ thể trong Abstract Class có thể được tái sử dụng bởi các lớp con, giảm sự trùng lặp mã.
  3. Tính Kế Thừa (Inheritance): Abstract Class cung cấp một cơ sở chung cho các lớp con, giúp tổ chức mã tốt hơn.

Abstract Class vs Interface

Mặc dù cả Abstract Class và Interface đều cung cấp tính trừu tượng, chúng có những điểm khác biệt quan trọng.

Tính Năng Abstract Class Interface
Triển Khai Phương Thức Có thể có cả phương thức trừu tượng và cụ thể Không (cho đến khi có phương thức mặc định trong một số ngôn ngữ)
Đa Kế Thừa Giới hạn (kế thừa đơn trong hầu hết các ngôn ngữ) Hỗ trợ
Trường Có thể có biến thể hiện Chỉ có hằng số
Trường Hợp Sử Dụng Khi các lớp chia sẻ một cơ sở chung với triển khai một phần Khi các lớp khác nhau chia sẻ hành vi mà không cần kế thừa
Có thể khởi tạo Có thể (trong một số ngôn ngữ) Không
Có hàm khởi tạo Có thể (trong một số ngôn ngữ) Không
Access Modifiers Có thể có các phạm vi truy cập khác nhau Mặc định là public
Ứng Dụng Chia sẻ code giữa các lớp có liên quan Định nghĩa một hợp đồng cho các lớp không liên quan

Khi Nào Nên Sử Dụng Abstract Class:

  • Khi bạn có common code cần chia sẻ giữa nhiều lớp có liên quan
  • Khi bạn muốn cung cấp một interface chung với một số hành vi mặc định
  • Khi bạn cần duy trì trạng thái giữa các phương thức
  • Khi bạn cần quyền truy cập vào việc tạo constructor

Khi Nào Nên Sử Dụng Interface:

  • Khi bạn muốn định nghĩa một hợp đồng mà không cần triển khai
  • Khi bạn cần đa kế thừa
  • Khi bạn đang định nghĩa một hợp đồng mà các lớp không liên quan có thể triển khai
  • Khi bạn muốn chỉ định hành vi có thể được triển khai bởi nhiều lớp không liên quan

Ví Dụ về Abstract Class

Dưới đây là một số ví dụ về cách sử dụng Abstract Class trong các ngôn ngữ lập trình khác nhau.

Ví Dụ Java

abstract class Animal {
    abstract void makeSound();
    
    void eat(String food) {
        System.out.println("Eating " + food);
    }
}

class Dog extends Animal {
    @Override
    void makeSound() {
        System.out.println("Woof!");
    }
}

class Cat extends Animal {
    @Override
    void makeSound() {
        System.out.println("Meow!");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal myDog = new Dog();
        Animal myCat = new Cat();

        myDog.makeSound(); // Output: Woof!
        myDog.eat("bones"); // Output: Animal is eating bones

        myCat.makeSound(); // Output: Meow!
        myCat.eat("fish"); // Output: Animal is eating fish
    }
}

Ví Dụ C#

abstract class Animal {
    public abstract void MakeSound();
    
    public void Eat(string food) {
        Console.WriteLine("Eating " + food);
    }
}

class Dog : Animal {
    public override void MakeSound() {
        Console.WriteLine("Woof!");
    }
}

class Program {
    static void Main(string[] args) {
        Animal myDog = new Dog();
        myDog.MakeSound(); // Output: Woof!
        myDog.Eat("bone"); // Output: Eating bone
    }
}

Ví Dụ TypeScript

abstract class Animal {
    abstract makeSound(): void;
    
    eat(food: string): void {
        console.log(`Eating ${food}`);
    }
}

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

const dog: Animal = new Dog();
dog.makeSound(); // Output: Woof
dog.eat("bone"); // Output: Eating bone

Ví Dụ PHP

abstract class Animal {
    abstract public function makeSound(): void;
    
    public function eat(string $food): void {
        echo "Eating $food";
    }
}

class Dog extends Animal {
    public function makeSound(): void {
        echo "Woof";
    }
}

$dog = new Dog();
$dog->makeSound(); // Output: Woof
$dog->eat("bone"); // Output: Eating bone

Các Best Practices Khi Sử Dụng Abstract Class

  1. Đặt Tên Rõ Ràng: Đặt tên Abstract Class sao cho dễ hiểu và phản ánh đúng chức năng của nó.
  2. Giữ Đơn Giản: Chỉ định nghĩa các phương thức cần thiết để tránh Abstract Class phình to.
  3. Tài Liệu Tốt: Cung cấp tài liệu rõ ràng cho Abstract Class và các phương thức của nó.

Kết Luận

Abstract Class là một công cụ mạnh mẽ trong OOP giúp bạn tạo ra các hệ thống linh hoạt, dễ bảo trì và tái sử dụng mã. Bằng cách hiểu và sử dụng đúng cách Abstract Class, bạn có thể nâng cao chất lượng mã của mình và cải thiện kỹ năng thiết kế phần mềm.

Đọc Thêm

  • Design Patterns: Elements of Reusable Object-Oriented Software của Erich Gamma và cộng sự.
  • Clean Code - Robert C. Martin
  • Effective Java - Joshua Bloch
  • Các Nguyên Tắc SOLID cho Thiết Kế Phần Mềm Tốt Hơn.
  • OOP theo từng ngôn ngữ (Java, PHP, TypeScript, C#,...)