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ếSOLID
Bài 11/12
5 phút

ISP — Interface nhỏ, đúng việc

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

Interface IOrderStore có ba method: đọc, lưu, xoá. Trang báo cáo chỉ cần đọc nhưng vẫn phải implement đủ ba method, hai method thừa chỉ ném NotImplementedException (exception có sẵn báo method chưa viết). ISP tránh việc ép class nhận những thứ nó không dùng.

Khái niệm

✂️ ISP (Interface Segregation Principle): không bắt class phụ thuộc vào những method nó không dùng, nên chia interface lớn thành nhiều interface nhỏ theo từng việc.

Ví dụ

var repo = new OrderRepository();
var page = new ReportPage(repo);
page.Show();

interface IOrderReader
{
    string Get(int id);
}

interface IOrderWriter
{
    void Save(string order);
}

class OrderRepository : IOrderReader, IOrderWriter
{
    public string Get(int id) => $"Đơn #{id}";
    public void Save(string order) =>
        Console.WriteLine($"Lưu {order}");
}

class ReportPage
{
    private readonly IOrderReader _reader;

    public ReportPage(IOrderReader reader)
    {
        _reader = reader;
    }

    public void Show() =>
        Console.WriteLine(_reader.Get(1));
}
  • IOrderReader chỉ đọc, IOrderWriter chỉ ghi. Mỗi interface một việc.
  • OrderRepository làm được cả hai nên implement cả hai.
  • ReportPage chỉ cần đọc nên chỉ nhận IOrderReader. Sửa phần ghi không ảnh hưởng tới nó.
<<interface>> IOrderReader Get(id) <<interface>> IOrderWriter Save(order) OrderRepository ReportPage
Một class làm hai vai, ReportPage chỉ thấy vai đọc

Thử ngay

Chép ví dụ trên vào Program.cs, thêm hai dòng sau ngay dưới page.Show();:

IOrderWriter writer = repo;
writer.Save("Đơn #2");

Đoán trước khi chạy: cùng một object repo, vừa truyền cho ReportPage như một IOrderReader, vừa gán vào biến IOrderWriter. Có được không?

Xem kết quả
Đơn #1
Lưu Đơn #2

Được. Object implement nhiều interface thì đóng được vai của từng interface đó. Mỗi nơi chỉ thấy đúng phần nó cần.

Lỗi hay gặp

Interface quá to, class phải implement cho có. NotImplementedException là dấu hiệu rõ nhất.

// SAI — trang báo cáo bị ép implement cả ghi và xoá
interface IOrderStore
{
    string Get(int id);
    void Save(string order);
    void Delete(int id);
}

class ReportSource : IOrderStore
{
    public string Get(int id) => $"Đơn #{id}";
    public void Save(string order)
    {
        throw new NotImplementedException();
    }
    public void Delete(int id)
    {
        throw new NotImplementedException();
    }
}
// ĐÚNG — trang báo cáo chỉ implement phần đọc
class ReportSource : IOrderReader
{
    public string Get(int id) => $"Đơn #{id}";
}

Tóm tắt

  • ISP: không bắt class phụ thuộc vào method nó không dùng.
  • Chia interface lớn thành nhiều interface nhỏ theo từng việc.
  • Một class implement được nhiều interface nhỏ khi nó làm được nhiều việc.
  • NotImplementedException trong class implement là dấu hiệu interface quá to.

Tự kiểm tra

0/3 câu
Câu 1

IPrinter có Print(), Scan(), Fax(). Máy in rẻ chỉ in được, nên Scan và Fax ném NotImplementedException. Theo ISP nên làm gì?

Câu 2

Class Dashboard chỉ đọc dữ liệu đơn hàng. Constructor của nó nên nhận kiểu nào?

Câu 3

IAccountService có Login(), Logout(), ChangePassword() và được năm class implement. Team thêm ResetPassword() vào interface này. Class nào phải sửa?

LSP — Class con phải thay được class chaDIP và dependency injection

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