Nguyên Tắc Đơn Nhiệm trong SOLID
Trong bài viết này, chúng ta sẽ khám phá nguyên tắc đầu tiên trong tập hợp các nguyên tắc SOLID, đó là nguyên tắc Đơn Nhiệm (Single Responsibility Principle). Nguyên tắc này quy định rằng mỗi lớp (class) chỉ nên có một lý do duy nhất để thay đổi, giúp giảm thiểu sự phức tạp và tăng tính khả thi của việc bảo trì mã nguồn.
Tại sao Nguyên Tắc Đơn Nhiệm lại Quan Trọng?
Dù nguyên tắc này có vẻ đơn giản và dễ hiểu, nhưng thực tế cho thấy đây lại là nguyên tắc thường bị vi phạm. Việc tạo ra các lớp phức tạp, chịu trách nhiệm cho nhiều nhiệm vụ khác nhau không chỉ khiến mã nguồn trở nên khó hiểu mà còn làm tăng nguy cơ sai sót.
Hãy cùng phân tích một ví dụ từ mã nguồn của Microsoft, trong dự án mẫu xây dựng một trang thương mại điện tử sử dụng .NET.
Phân Tích Lớp ProfileService
Lớp ProfileService
trong mã nguồn này chứa nhiều phương thức có thể không tương thích với nguyên tắc Đơn Nhiệm:
csharp
namespace eShop.Identity.API.Services
{
public class ProfileService : IProfileService
{
private readonly UserManager<ApplicationUser> _userManager;
public ProfileService(UserManager<ApplicationUser> userManager)
{
_userManager = userManager;
}
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
{
// ... mã xử lý ...
}
public async Task IsActiveAsync(IsActiveContext context)
{
// ... mã xử lý ...
}
private IEnumerable<Claim> GetClaimsFromUser(ApplicationUser user)
{
// ... mã xử lý ...
}
}
}
Những Vấn Đề Cần Lưu Ý:
- IsActiveAsync có nên nằm trong
IProfileService
không? Tên phương thức IsActive có thể khiến người đọc hiểu nhầm nó nên trả về một giá trị boolean. - Tên gọi không tuân thủ quy ước: GetProfileDataAsync có thể được đổi thành LoadIdentityProfileDataContext để thể hiện rõ hơn chức năng của nó.
- Tên gọi của lớp ProfileService: Có nên đặt là IdentityProfileService để người đọc không nhầm lẫn với việc tải thông tin người dùng?
Phân Tích Lớp ILoginService
Tương tự, hãy xem xét lớp ILoginService
:
csharp
namespace eShop.Identity.API.Services
{
public interface ILoginService<T>
{
Task<bool> ValidateCredentials(T user, string password);
Task<T> FindByUsername(string user);
Task SignIn(T user);
Task SignInAsync(T user, AuthenticationProperties properties, string authenticationMethod = null);
}
}
Câu hỏi chính: Liệu các phương thức như FindByUsername có thật sự phù hợp với nhiệm vụ chính của LoginService
? Hay chúng ta có thể tách ra thành các lớp riêng biệt để đảm bảo tính đơn nhiệm và rõ ràng hơn?
Kết Luận
Những ví dụ trên cho thấy rằng không chỉ lập trình viên mới mà cả những chuyên gia dày dạn kinh nghiệm cũng có thể gặp phải những vấn đề trong thiết kế mã nguồn. Nguyên tắc Đơn Nhiệm không chỉ giúp mã nguồn dễ bảo trì mà còn nâng cao khả năng tiếp cận cho những ai sẽ làm việc trên mã nguồn đó trong tương lai.
Trong bài viết tiếp theo, chúng ta sẽ xem xét những yếu tố liên quan đến kỹ năng và kỷ luật trong lập trình, vì chúng cũng ảnh hưởng lớn đến chất lượng mã nguồn.
source: viblo