Interface Segregation Principle (ISP): Client không nên bị ép buộc phụ thuộc vào các interface mà chúng không sử dụng. Nên tách interface lớn (fat interface) thành nhiều interface nhỏ, chuyên biệt (role interfaces).
Vi phạm ISP: Fat Interface
// TRƯỚC: Vi phạm ISP
public interface IProduct
{
int ID { get; set; }
double Weight { get; set; }
int Stock { get; set; }
int Inseam { get; set; } // Chỉ cho Quần
int WaistSize { get; set; } // Chỉ cho Quần
int HatSize { get; set; } // Chỉ cho Mũ
}
public class Jeans : IProduct
{
public int ID { get; set; }
public double Weight { get; set; }
public int Stock { get; set; }
public int Inseam { get; set; }
public int WaistSize { get; set; }
public int HatSize { get; set; } // Ép phải implement -> ném NotImplementedException
}
public class BaseballCap : IProduct
{
public int ID { get; set; }
public double Weight { get; set; }
public int Stock { get; set; }
public int Inseam { get; set; } // Không dùng
public int WaistSize { get; set; } // Không dùng
public int HatSize { get; set; }
}
Tuân thủ ISP: Tách interface nhỏ
// SAU: Tuân thủ ISP
// Interface chung cho mọi sản phẩm
public interface IProduct
{
int ID { get; set; }
double Weight { get; set; }
int Stock { get; set; }
}
// Interface chuyên biệt cho Quần
public interface IPants
{
int Inseam { get; set; }
int WaistSize { get; set; }
}
// Interface chuyên biệt cho Mũ
public interface IHat
{
int HatSize { get; set; }
}
// Triển khai: Chỉ implement những gì cần
public class Jeans : IProduct, IPants
{
public int ID { get; set; }
public double Weight { get; set; }
public int Stock { get; set; }
public int Inseam { get; set; }
public int WaistSize { get; set; }
// Không có HatSize
}
public class BaseballCap : IProduct, IHat
{
public int ID { get; set; }
public double Weight { get; set; }
public int Stock { get; set; }
public int HatSize { get; set; }
// Không có Inseam, WaistSize
}
Lợi ích
- Không implement thừa: Class không bị ép implement member không dùng.
- Giảm coupling: Client chỉ phụ thuộc interface mình cần.
- Dễ thay đổi: Sửa
IPantskhông ảnh hưởngIHat. - Dễ test: Mock interface nhỏ dễ dàng hơn.
Dấu hiệu vi phạm ISP
- Interface có nhiều method (> 5-7).
- Class implement interface nhưng ném
NotImplementedExceptioncho vài method. - Interface chứa method cho nhiều "role" khác nhau (Print, Scan, Fax...).
Ví dụ thực tế: IRepository
// Fat interface - vi phạm ISP
public interface IRepository<T>
{
T GetById(int id);
IEnumerable<T> GetAll();
void Add(T entity);
void Update(T entity);
void Delete(T entity);
void SaveChanges(); // Không phải repo nào cũng cần
IEnumerable<T> ExecuteRawSql(string sql); // Chỉ EF cần
}
// Tách nhỏ - tuân thủ ISP
public interface IReadRepository<T> { T GetById(int id); IEnumerable<T> GetAll(); }
public interface IWriteRepository<T> { void Add(T entity); void Update(T entity); void Delete(T entity); }
public interface IUnitOfWork { void SaveChanges(); }
public interface ISqlExecutor { IEnumerable<T> ExecuteRawSql<T>(string sql); }
Lưu ý: ISP thường đi kèm với SRP. Interface nhỏ thường tương ứng với một trách nhiệm duy nhất.