CodeGym /Các khóa học /C# SELF /Tạo hệ thống phân cấp class

Tạo hệ thống phân cấp class

C# SELF
Mức độ , Bài học
Có sẵn

1. Giới thiệu

Hãy tưởng tượng một thế giới không có hệ thống phân cấp: hàng ngàn class Person, Animal, Vehicle và tất cả đều tách biệt hoàn toàn. Không có gì lạ khi lập trình viên trong thế giới đó chắc chắn sẽ bị rối tung lên và không sống nổi đến bữa trưa! Trong các dự án thực tế, chúng ta thường cần các object có thể làm điều gì đó chung (ví dụ, tất cả động vật đều có thể di chuyển), nhưng mỗi loại lại có đặc điểm riêng (cá thì bơi, chim thì bay).

Chính hệ thống phân cấp class giúp thể hiện mối liên hệ giữa các thực thể, để lập trình trở thành công việc sáng tạo chứ không phải là cuộc chiến bất tận với copy-paste.

Xem ví dụ nhé. Giả sử ta có class cơ sở Animal. Tất cả động vật đều có thể phát ra âm thanh. Nhưng chỉ có mèo kêu meo meo, chó sủa, còn vẹt thì thậm chí kể được vài câu chuyện cười. Ta muốn thể hiện điều này trong code theo kiểu phân cấp.

Class cơ sở


public class Animal
{
    public string Name { get; set; }

    public Animal(string name)
    {
        Name = name;
    }

    // Phương thức cơ sở: có thể override ở class con
    public virtual void Speak()
    {
        Console.WriteLine("Động vật phát ra âm thanh nào đó...");
    }
}

Ở đây mình thêm từ khóa virtual vào method Speak(). Kiểu như nhắn: "Ê, class con, nếu muốn thì override method này nhé".

Tạo hệ thống phân cấp: class dẫn xuất

Giờ ta có class Cat, kế thừa từ Animal:


public class Cat : Animal
{
    public Cat(string name) : base(name) { }

    // Override Speak — mèo đâu có gầm được!
    public override void Speak()
    {
        Console.WriteLine($"{Name} nói: Meo!");
    }
}

Và class Dog:

public class Dog : Animal
{
    public Dog(string name) : base(name) { }

    public override void Speak()
    {
        Console.WriteLine($"{Name} nói: Gâu!");
    }
}

Còn nếu có động vật bình thường không biết nói? Thì dùng class cơ sở, không cần override gì cả.

Minh họa — cây hệ thống phân cấp


.               Animal
                /   \
             Cat    Dog
  • Animal — class cơ sở
  • Cat, Dog — class con (dẫn xuất)

2. Viết code sử dụng hệ thống phân cấp này

Tiếp tục làm việc với ứng dụng console của mình nhé.

Giả sử ta có một collection động vật và muốn mỗi con nói một câu đặc trưng:

Animal[] zoo = new Animal[]
{
    new Cat("Barsik"),
    new Dog("Rex"),
    new Animal("Sinh vật bí ẩn")
};

foreach (Animal animal in zoo)
{
    animal.Speak();
}

Kết quả mong đợi:

Barsik nói: Meo!
Rex nói: Gâu!
Động vật phát ra âm thanh nào đó...

Đó, nhờ hệ thống phân cấp và polymorphism (sắp tới mình sẽ giải thích kỹ, nhưng ý chính là: gọi đúng version method tùy theo kiểu thực tế của object), app của bạn sẽ linh hoạt và dễ mở rộng hơn nhiều.

3. Thêm method và field mới

Ok, phần "phát âm" xong rồi. Nhưng tất cả động vật giống nhau thì chán quá. Ví dụ, mèo có thể có chín mạng, còn chó biết nhặt gậy.

Thêm hành vi riêng biệt

Ở class con, bạn có thể thêm method và field riêng:


public class Cat : Animal
{
    public int Lives { get; private set; } = 9;

    public Cat(string name) : base(name) { }

    public override void Speak()
    {
        Console.WriteLine($"{Name} nói: Meo! Tao còn {Lives} mạng.");
    }

    public void LoseLife()
    {
        if (Lives > 0)
        {
            Lives--;
            Console.WriteLine($"{Name} mất một mạng. Còn lại: {Lives}");
        }
        else
        {
            Console.WriteLine($"{Name} đã dùng hết mạng rồi!");
        }
    }
}

Dùng trong code:

var barsik = new Cat("Barsik");
barsik.Speak();      // Barsik nói: Meo! Tao còn 9 mạng.
barsik.LoseLife();   // Barsik mất một mạng. Còn lại: 8

Thêm class mới: mở rộng "sở thú"

Bạn đã biết tạo class dẫn xuất rồi. Thêm thử vẹt nhé:

public class Parrot : Animal
{
    public Parrot(string name) : base(name) { }

    public override void Speak()
    {
        Console.WriteLine($"{Name} nói: Chào bạn!");
    }

    public void Repeat(string phrase)
    {
        Console.WriteLine($"{Name} lặp lại: {phrase}");
    }
}

Giờ bạn mở rộng hệ thống dễ dàng mà không phải sửa code cũ:

var keshka = new Parrot("Kesha");
keshka.Speak();                 // Kesha nói: Chào bạn!
keshka.Repeat("Học đi, sinh viên!"); // Kesha lặp lại: Học đi, sinh viên!

4. So sánh hành vi động vật

Loại Method Speak() Field riêng Hành vi bổ sung
Animal Có (virtual) Name
Cat Có (override) Lives LoseLife()
Dog Có (override)
Parrot Có (override) Repeat(string)

Hệ thống phân cấp class trong bộ nhớ (sơ đồ khối)

Animal (Name)
 ├── Cat (Lives)
 ├── Dog
 └── Parrot (Repeat)

5. Thực hành trong ứng dụng của mình

Hãy liên hệ ý tưởng hệ thống phân cấp với app — ví dụ, ta có các task các loại khác nhau:

  1. Task (class cơ sở): Bất kỳ task nào — đều có tên và trạng thái hoàn thành.
  2. WorkTask (công việc): Ngoài ra còn có deadline.
  3. HomeTask (việc nhà): Có thể có độ ưu tiên ("Rất quan trọng", "Bình thường").

Bắt đầu với class cơ sở:

public class Task
{
    public string Title { get; set; }
    public bool IsCompleted { get; private set; }

    public Task(string title)
    {
        Title = title;
    }

    public virtual void Complete()
    {
        IsCompleted = true;
        Console.WriteLine($"Task \"{Title}\" đã hoàn thành!");
    }
}

Giờ thêm task công việc:

public class WorkTask : Task
{
    public DateTime Deadline { get; set; }

    public WorkTask(string title, DateTime deadline)
        : base(title)
    {
        Deadline = deadline;
    }

    public override void Complete()
    {
        base.Complete();
        Console.WriteLine($"Deadline: {Deadline:d}");
    }
}

Và task việc nhà:

public class HomeTask : Task
{
    public string Priority { get; set; }

    public HomeTask(string title, string priority)
        : base(title)
    {
        Priority = priority;
    }

    // Không cần override Complete nếu hành vi của class cơ sở là đủ
}

Tạo list task trong chương trình:

List<Task> tasks = new List<Task>
{
    new WorkTask("Gửi báo cáo", DateTime.Today.AddDays(2)),
    new HomeTask("Rửa bát", "Rất quan trọng"),
    new Task("Đọc bài giảng về inheritance")
};

foreach (Task task in tasks)
{
    Console.WriteLine($"Task: {task.Title}");
    task.Complete();
}

Kết quả mong đợi:

Task: Gửi báo cáo
Task "Gửi báo cáo" đã hoàn thành!
Deadline: 13.07.2025
Task: Rửa bát
Task "Rửa bát" đã hoàn thành!
Task: Đọc bài giảng về inheritance
Task "Đọc bài giảng về inheritance" đã hoàn thành!

Thấy tiện chưa: tất cả task lưu chung, xử lý giống nhau, còn đặc thù thì thể hiện đúng chỗ cần.

6. Lỗi thường gặp khi dùng inheritance

Lỗi số 1: cố override method không khai báo virtual.
Nếu method trong class cơ sở không có virtual, thì không override được ở class con. Kết quả là mất hết sự linh hoạt của polymorphism, hệ thống phân cấp trở nên vô dụng.

Lỗi số 2: inheritance mà không có liên hệ logic giữa các thực thể.
Đừng dùng inheritance nếu các object không liên quan về ý nghĩa. Ví dụ, Hình tròn thực sự là Hình, nhưng NgựaPhương tiện giao thông thì hơi gượng ép. Trừ khi trong ngữ cảnh đặc biệt (ví dụ game thời trung cổ), thì mới hợp lý.

Lỗi số 3: hệ thống phân cấp quá sâu.
Khi cấu trúc class quá sâu (5–6 cấp trở lên), code sẽ rất khó đọc, bảo trì và test. Đó là dấu hiệu nên cân nhắc composition thay cho inheritance.

Lỗi số 4: quên gọi constructor của class cơ sở.
Khi thêm property mới ở class con, rất dễ quên gọi rõ ràng base(...) trong constructor. Điều này có thể dẫn đến object bị khởi tạo thiếu hoặc sai phần cơ sở và gây bug khó tìm.

1
Khảo sát/đố vui
, cấp độ , bài học
Không có sẵn
Khái niệm kế thừa
Kế thừa và hệ thống phân cấp class
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION