CodeGym /Các khóa học /C# SELF /Sử dụng interface trong thực tế

Sử dụng interface trong thực tế

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

1. Interface trong business logic: nút bấm và hành động

Đây là bài giảng cuối cùng về interface, nên sẽ có rất nhiều ví dụ thực tế để bạn hiểu rõ hơn cách interface được dùng ngoài đời.

Interface đặc biệt giá trị trong các ứng dụng lớn. Vì vậy bên dưới là các trường hợp phức tạp, đôi khi vượt trước chương trình học. Nếu bạn thấy thú vị — tuyệt! Nếu không — cứ chuyển sang level tiếp theo :P

Ví dụ đơn giản nhất — nút bấm và click vào nó

Tiếp tục phát triển ứng dụng console học tập của mình — tưởng tượng là bạn có một hệ thống các thành phần giao diện đơn giản (ví dụ: nút bấm, ô nhập text và radio button) trong menu console.

Bạn muốn một số thành phần phản ứng khi nhấn (nút bấm), còn một số thì không (ví dụ: tiêu đề tĩnh). Interface cho phép bạn làm điều này một cách gọn gàng.

Bước 1: Tạo interface


// Thành phần có thể "click":
public interface IClickable
{
    void Click();
}

Bước 2: Áp dụng interface cho các loại thành phần khác nhau


// Lớp cơ bản cho tất cả các item trong menu
public class MenuItem
{
    public string Title { get; set; }

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

    public virtual void Display()
    {
        Console.WriteLine(Title);
    }
}

// Nút bấm: có thể "click"
public class Button : MenuItem, IClickable
{
    public Button(string title) : base(title) {}

    public void Click()
    {
        Console.WriteLine($"[Nút bấm] {Title} đã được nhấn!");
    }
}

// Chỉ là nhãn: không thể click
public class Label : MenuItem
{
    public Label(string title) : base(title) {}
}

Bước 3: Dùng tính đa hình của interface


IClickable[] clickableItems = new IClickable[]
{
    new Button("Lưu"),
    new Button("Thoát")
    // Label không thể thêm ở đây — nó không phải IClickable!
};

foreach (var item in clickableItems)
{
    item.Click();
}

Thấy chưa? Danh sách chỉ nhận những ai “biết” Click, nên bạn sẽ không bị lỗi runtime nếu cố “click” vào một object không phù hợp.

2. Interface và pattern “Strategy”: chọn thuật toán lúc chạy

Giả sử bạn viết một app mà có thể lưu báo cáo theo nhiều cách: vào file, vào database, lên “cloud”, hoặc thậm chí gửi Telegram cho sếp (đừng hỏi, chuyện gì cũng có thể xảy ra).

Bước 1: Đặt ra bài toán

Cần để class ReportGenerator làm việc với bất kỳ cách lưu nào, mà không cần biết chi tiết bên trong.

Bước 2: Mô tả interface-strategy


public interface IDataSaver
{
    void Save(string reportData);
}

Bước 3: Triển khai các phương án khác nhau


public class FileDataSaver : IDataSaver
{
    public void Save(string reportData)
    {
        Console.WriteLine("[FileDataSaver] Đang lưu vào file...\n" + reportData);
        // Ở đây có thể là code cho File.WriteAllText(...)
    }
}

public class DatabaseDataSaver : IDataSaver
{
    public void Save(string reportData)
    {
        Console.WriteLine("[DatabaseDataSaver] Đang lưu vào database...\n" + reportData);
        // Ở đây có thể là code ghi vào database
    }
}

Bước 4: Dùng interface để thay đổi strategy lưu


public class ReportGenerator
{
    private readonly IDataSaver _dataSaver;

    public ReportGenerator(IDataSaver dataSaver)
    {
        _dataSaver = dataSaver;
    }

    public void GenerateReport()
    {
        string report = "Đây là báo cáo quan trọng!";
        Console.WriteLine("Đang tạo báo cáo...");
        _dataSaver.Save(report);
    }
}

Demo sự linh hoạt


// Có thể đổi cách lưu mà không phải sửa ReportGenerator!
IDataSaver fileSaver = new FileDataSaver();
ReportGenerator fileReport = new ReportGenerator(fileSaver);
fileReport.GenerateReport();

IDataSaver dbSaver = new DatabaseDataSaver();
ReportGenerator dbReport = new ReportGenerator(dbSaver);
dbReport.GenerateReport();

Ý nghĩa thực tế: Nếu ngày mai bạn muốn ghi báo cáo vào một dịch vụ siêu hiện đại mới, chỉ cần viết class mới implement interface, rồi nhét object đó vào kiến trúc của bạn, không phải sửa dòng code nào cũ cả.

3. Interface trong .NET: IDisposable và using

Một trong những interface phổ biến nhất trong .NET là IDisposable. Tất cả các class làm việc với resource không được quản lý như file, stream, kết nối mạng đều implement nó.

Tại sao cần IDisposable?

Khi bạn làm việc với resource cần phải giải phóng (ví dụ: đóng file), bạn implement IDisposable và viết method Dispose. Nhờ vậy, bạn có thể truyền object đó vào lệnh using, đảm bảo Dispose sẽ được gọi khi ra khỏi block.

Ví dụ: Giả lập làm việc với file

public class FakeFile : IDisposable
{
    public string FileName { get; }

    public FakeFile(string fileName)
    {
        FileName = fileName;
        Console.WriteLine($"Đã mở file: {fileName}");
    }

    public void Dispose()
    {
        Console.WriteLine($"Đã đóng file: {FileName}");
    }
}

// Trong chương trình chính:
using (var file = new FakeFile("baocao.txt"))
{
    Console.WriteLine("Đang ghi vào file...");
    // file sẽ tự động "đóng" sau using
}

Kết quả:

Đã mở file: baocao.txt
Đang ghi vào file...
Đã đóng file: baocao.txt

Thực sự hữu ích: bạn sẽ không quên đóng file — interface và ngôn ngữ sẽ bảo vệ bạn.

4. Interface trong collection và LINQ

Khi bạn làm việc với list, array, dictionary, thực ra bạn đã dùng interface rồi, dù không để ý.


List<int> list = new List<int> { 1, 2, 3 };
IEnumerable<int> enumerable = list; // ok luôn!

// Giờ bạn có thể duyệt phần tử như sau:
foreach(var x in enumerable)
{
    Console.WriteLine(x);
}

Hầu hết các method LINQ làm việc với collection qua interface IEnumerable<T>. Nhờ vậy bạn có thể viết code không phụ thuộc loại collection cụ thể.

Tại sao cần thế?
Bạn có thể đổi List<T> thành T[], HashSet<T> hoặc collection tự chế — code vẫn chạy ngon!

5. Interface cho kiểm thử (Mock object)

Trong test, rất quan trọng là tách code khỏi phụ thuộc bên ngoài: không động vào database thật, không đụng file thật. Interface cho phép dùng mock object và test code mà không ảnh hưởng database hay file thật.


public class FakeDataSaver : IDataSaver
{
    public bool WasCalled { get; private set; } = false;
    public void Save(string reportData)
    {
        WasCalled = true;
        Console.WriteLine("Đã lưu dữ liệu vào mock object!");
    }
}

// Trong test
FakeDataSaver saver = new FakeDataSaver();
ReportGenerator generator = new ReportGenerator(saver);
generator.GenerateReport();

Console.WriteLine($"Save đã được gọi chưa? {saver.WasCalled}");

Kết quả: test không phụ thuộc thế giới thật, nhưng vẫn kiểm tra được method cần thiết đã được gọi!

6. Triển khai interface tường minh để giải quyết xung đột

Đôi khi một class phải implement hai interface có method giống nhau, nhưng logic lại khác nhau.


public interface IFlyable
{
    void Move();
}

public interface ISwimmable
{
    void Move();
}

public class Duck : IFlyable, ISwimmable
{
    // Triển khai tường minh
    void IFlyable.Move()
    {
        Console.WriteLine("Vịt đang bay!");
    }

    void ISwimmable.Move()
    {
        Console.WriteLine("Vịt đang bơi!");
    }
}

Duck duck = new Duck();

// duck.Move(); // Lỗi: không có method này!
((IFlyable)duck).Move(); // "Vịt đang bay!"
((ISwimmable)duck).Move(); // "Vịt đang bơi!"

Nhìn hơi cứng, nhưng đôi khi specification lại yêu cầu như vậy!

7. Interface trong mô hình sự kiện: INotifyPropertyChanged

Trong .NET có các interface chuẩn để hỗ trợ sự kiện — ví dụ khi dữ liệu model thay đổi, UI phải biết (rất hay gặp trong WPF, MAUI và các GUI khác).


using System.ComponentModel;

public class Person : INotifyPropertyChanged
{
    private string name;
    public string Name
    {
        get => name;
        set
        {
            if (name != value)
            {
                name = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Name)));
            }
        }
    }

    public event PropertyChangedEventHandler? PropertyChanged;
}

Ý nghĩa: bất kỳ framework nào hỗ trợ data binding đều mong class của bạn implement interface này — và UI sẽ tự động cập nhật khi property thay đổi.

8. Interface trong pattern “Factory”

Interface rất hợp để xây dựng factory — class tạo ra các object khác nhau nhưng có thể thay thế cho nhau.


public interface ITransport
{
    void Move();
}

public class Bicycle : ITransport
{
    public void Move() => Console.WriteLine("Đi xe đạp!");
}

public class Car : ITransport
{
    public void Move() => Console.WriteLine("Đi ô tô!");
}

public class TransportFactory
{
    public static ITransport Create(string type)
    {
        return type switch
        {
            "bike" => new Bicycle(),
            "car" => new Car(),
            _ => throw new ArgumentException("Loại phương tiện không xác định")
        };
    }
}

ITransport transport = TransportFactory.Create("bike");
transport.Move(); // "Đi xe đạp!"

9. Interface cho sự kiện: ví dụ tự tạo event

Mô tả interface “listener sự kiện”:


public interface ILoginListener
{
    void OnLogin(string userName);
}

// Class gọi event
public class LoginManager
{
    private List<ILoginListener> listeners = new();

    public void Subscribe(ILoginListener listener) => listeners.Add(listener);

    public void Login(string userName)
    {
        Console.WriteLine($"Người dùng {userName} đã đăng nhập.");
        foreach (var listener in listeners)
            listener.OnLogin(userName);
    }
}

// Class implement interface
public class WelcomeMessage : ILoginListener
{
    public void OnLogin(string userName)
    {
        Console.WriteLine($"Chào mừng, {userName}!");
    }
}

LoginManager manager = new();
manager.Subscribe(new WelcomeMessage());
manager.Login("Vasya");
// Người dùng Vasya đã đăng nhập.
// Chào mừng, Vasya!

Khi phỏng vấn: Nếu bị hỏi — “bạn sẽ tự làm hệ thống sự kiện thế nào?”, cứ mạnh dạn kể về interface-listener nhé!

10. Interface cho plugin (mở rộng ứng dụng)

Nhiều ứng dụng lớn hỗ trợ plugin. Nhờ interface, app của bạn có thể “nạp” module mới ngay lập tức mà không cần biết bên trong nó thế nào.


public interface IPlugin
{
    string Name { get; }
    void Run();
}

// Ứng dụng của bạn:
public class PluginLoader
{
    public void LoadAndRun(IEnumerable<IPlugin> plugins)
    {
        foreach (var plugin in plugins)
        {
            Console.WriteLine($"Đang chạy plugin: {plugin.Name}");
            plugin.Run();
        }
    }
}

Plugin có thể do bên thứ ba phát triển — miễn là implement đúng interface. Ứng dụng của bạn sẽ mở rộng dễ dàng!

2
Nhiệm vụ
C# SELF, mức độ, bài học
Đã khóa
Triển khai interface cho các hình học
Triển khai interface cho các hình học
2
Nhiệm vụ
C# SELF, mức độ, bài học
Đã khóa
Factory đơn giản sử dụng interface
Factory đơn giản sử dụng interface
1
Khảo sát/đố vui
, cấp độ , bài học
Không có sẵn
Sử dụng interface
Interface nâng cao
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION