VieTopik
Tiếng HànTiếng AnhIT
  • Góc học tập
Tải app
  • Thư viện
  • Luyện thi
  • Cẩm nang
  • Góc học tập
OOP và thiết kếInterface và composition
Bài 6/12
5 phút

Interface hay abstract class

Nội dung bài · 5 mục
  1. 1.Khái niệm
  2. 2.Ví dụ
  3. 3.Thử ngay
  4. 4.Lỗi hay gặp
  5. 5.Tóm tắt

Không thể tạo object trực tiếp từ interface hay abstract class, và cả hai đều bắt class khác viết method. Vậy khi nào dùng cái nào? Bài này đưa ra cách chọn nhanh bằng một câu hỏi.

Khái niệm

🆚 Interface và abstract class: interface nói class làm được gì, còn abstract class là class cha chứa sẵn phần code chung cho các class con.

Interface Abstract class
Chứa code dùng chung thường không có
Có field thường, constructor không có
Một class dùng được mấy cái nhiều chỉ một
Diễn tả quan hệ "làm được" "là một"

Câu hỏi để chọn: các class có code chung cần viết một lần không? Có thì dùng abstract class. Chỉ cần cam kết một khả năng thì dùng interface.

Ví dụ

var card = new CardPayment { Amount = 120000m };
Console.WriteLine(card.Receipt());
card.Refund();

abstract class Payment
{
    public decimal Amount { get; set; }

    public abstract string Pay();

    public string Receipt() => $"{Pay()} - {Amount}đ";
}

interface IRefundable
{
    void Refund();
}

class CashPayment : Payment
{
    public override string Pay() => "Tiền mặt";
}

class CardPayment : Payment, IRefundable
{
    public override string Pay() => "Quẹt thẻ";

    public void Refund() =>
        Console.WriteLine($"Hoàn {Amount}đ về thẻ");
}
  • Payment là abstract class vì mọi hình thức thanh toán dùng chung Amount và Receipt().
  • IRefundable là interface vì hoàn tiền chỉ là một khả năng. Thẻ làm được, tiền mặt thì không.
  • CardPayment : Payment, IRefundable: kế thừa một class cha, đồng thời implement một interface. Class cha phải đứng đầu tiên.
<<abstract>> Payment Amount Pay() Receipt() <<interface>> IRefundable Refund() CashPayment CardPayment
Payment là class cha, IRefundable là khả năng thêm

Thử ngay

Chép ví dụ trên vào Program.cs. Thêm class dưới đây vào cuối file, rồi thay ba dòng đầu bằng đoạn gọi mới:

class WalletPayment : Payment, IRefundable
{
    public override string Pay() => "Ví điện tử";

    public void Refund() =>
        Console.WriteLine($"Hoàn {Amount}đ về ví");
}
var refunds = new List<IRefundable>
{
    new CardPayment { Amount = 120000m },
    new WalletPayment { Amount = 80000m },
};

foreach (IRefundable r in refunds)
{
    r.Refund();
}

Đoán trước khi chạy: thêm new CashPayment() vào list refunds thì có được không?

Xem kết quả
Hoàn 120000đ về thẻ
Hoàn 80000đ về ví

Không được, đó là lỗi compile. CashPayment không implement IRefundable, nên không thêm vào List<IRefundable> được. Compiler chặn luôn việc hoàn tiền mặt.

Lỗi hay gặp

Dùng abstract class cho một khả năng. Class đã có cha thì không kế thừa thêm class thứ hai được.

// SAI — lỗi compile: chỉ được một class cha
class BankPayment : Payment, Refundable
{
    public override string Pay() => "Chuyển khoản";

    public override void Refund() =>
        Console.WriteLine(
            $"Hoàn {Amount}đ về tài khoản");
}

abstract class Refundable
{
    public abstract void Refund();
}
// ĐÚNG — khả năng thì dùng interface
class BankPayment : Payment, IRefundable
{
    public override string Pay() => "Chuyển khoản";

    public void Refund() =>
        Console.WriteLine(
            $"Hoàn {Amount}đ về tài khoản");
}

Dùng interface khi có code chung. Mỗi class phải tự chép lại cùng một đoạn code.

// SAI — Receipt() bị chép giống hệt ở mọi class
interface IBill
{
    string Receipt();
}

class CashBill : IBill
{
    public decimal Amount { get; set; }
    public string Receipt() => $"Tiền mặt - {Amount}đ";
}
// ĐÚNG — phần chung viết một lần ở class cha
abstract class Bill
{
    public decimal Amount { get; set; }

    public abstract string Kind();

    public string Receipt() => $"{Kind()} - {Amount}đ";
}

class CashBill : Bill
{
    public override string Kind() => "Tiền mặt";
}

Tóm tắt

  • Có code chung, quan hệ "là một": dùng abstract class.
  • Chỉ cần cam kết một khả năng "làm được": dùng interface.
  • Một class kế thừa một class cha, nhưng implement được nhiều interface.
  • Hai thứ dùng cùng nhau được: class CardPayment : Payment, IRefundable.

Tự kiểm tra

0/3 câu
Câu 1

Class Truck đã kế thừa Vehicle. Giờ cần thêm khả năng "theo dõi GPS" cho Truck và cả Phone. Nên dùng gì?

Câu 2

Các loại báo cáo đều có chung phần header, footer và cách in. Chỉ phần thân là khác nhau. Nên dùng gì?

Câu 3

Khai báo nào hợp lệ, biết Animal là class còn ISwim và IFly là interface?

InterfaceComposition

Nội dung bài

  1. 1.Khái niệm
  2. 2.Ví dụ
  3. 3.Thử ngay
  4. 4.Lỗi hay gặp
  5. 5.Tóm tắt