S — Single Responsibility Principle (S.R.P)

Bài viết 2 trong 2 bài thuộc: Solid - 5 nguyên tắc vàng trong lập trình

A class should have one, and only one, reason to change.

1. Lý thuyết

Mỗi Class nên chỉ có duy nhất một lý do để thay đổi.
Responsibility trong ngữ cảnh này có thể hiểu là lý do để thay đổi. Bất cứ khi nào bạn nghĩ ra vài lý do để thay đổi code thì hãy cân nhắc đến việc tách chúng ra các Class riêng biệt.

Bạn thử nghĩ lại xem, mình đã từng viết một method nào dài khoảng 1 gang tay chưa? (thi thoảng mình cũng dùng tay để đo độ dài của code :D).
Việc đầu tiên bạn nghĩ để code trở nên đẹp hơn là gì?
+ Tách nó ra thành các hàm nhỏ hơn, mỗi hàm làm một nhiệm vụ và biết đâu ta có thể tái sử dụng lại. (Big is bad, small is good)
+ Xa hơn là tách ra thành các Class, mỗi Class làm một nhiệm vụ (theo như nguyên tắc Single Responsibility này mô tả)
+ Hơn nữa, hãy để các Class giao tiếp, phụ thuộc với nhau (Dependent) thông qua Interface.

Lý thuyết ở trên sẽ có đôi phần khó hiểu (đặc biệt là các bạn mới học lập trình), vậy nên chúng ta cùng đi vào ví dụ thực tế nhé.

2. Ví dụ

2.1 Yêu cầu

Bạn được giao nhiệm vụ, viết chức năng gửi một email maketing cho User.
– User đó phải là VIP
– Nội dung email là: hanhtrinhtuoitre.com chào VIP nhé!

2.2 Tách hàm

Phân tích đoạn code trên ta thấy có 3 nhiệm vụ chính
– Lấy ra VIP User: điều kiện là ‘role_id’ = 5, hãy tưởng tượng là sau này họ yêu cầu VIP User phải là những active user (có ‘status’ = 1 chẳng hạn). Cộng với việc lấy VIP User chắc chắn sẽ là nghiệp vụ được thực hiện nhiều lần trong Project của bạn => Tách nó ra một hàm mới.
– Chuẩn bị content cho Email: nếu tương lai content của chúng ta dài và chứa cả html code thì sao? => Tách nó ra một hàm mới
– Việc gửi Email: chúng ta đang new Email() để gửi email, việc này khiến cho UserService của chúng ta gắn chặt vào Email class. => cắm (inject) một EmailService thông qua hàm __construct của UserService.

2.3 Tách Class

Nhìn lại đoạn code ta thấy đỡ tù tội hơn, nhưng mà cái nguyên tắc này nó lại bảo là mày phải tách Class cơ? Tách sao giờ?

– Việc truy cập vào Database để lấy ra VIP User chúng ta sẽ tách ra một class mới và gọi nó là UserRepository.php, sau đó cũng giống như EmailService, ta sẽ inject nó vào UserService thông qua constructor.

– Việc tạo Email content: tạo một MarketingEmailGenerator.php để tạo ra content cho email.

– Cuối cùng, UserService sẽ như sau

2.4 cắm Interface để dùng

Woaaaa, đến đây anh em thấy code có vẻ ngon lành cành đào rồi nhỉ? Nhưng ngẫm thêm ý cuối cùng:

Hơn nữa, hãy để các Class giao tiếp, phụ thuộc với nhau (Dependent) thông qua Interface.

Ở đây mình thấy có marketingEmailGenerator, khả năng tương lai sẽ có nhiều loại như: welcomeEmailGenerator, NotificationEmailGenertator
Khi có nhiều loại như này, chúng ta nghĩ lại về kiến thức cơ bản OOP sẽ thấy cần phải tạo một lớp Inferface cho nó (hoặc có thể tạo ra một AbstractClass chung cho các loại trên)

Ở đây, anh em mình sẽ tạo ra một Interface là: EmailGenerateableInterface. Thằng EmailService sẽ giao tiếp với các loại EmailGenerator (miễn nó thuộc EmailGenerateableInterface)

3. Tổng kết

Single Responsibility nghe đơn giản mà lại có võ, nó xuất hiện ở rất rất nhiều nơi nên sẽ có nhiều cơ hội để chúng ta có thể áp dụng từ dễ đến khó. Let’s do it and improve yourself.

Cảm ơn bạn đã đọc đến dòng cuối cùng này. Làm ngụm coffee và code thôi!

Series Navigation<< SOLID là gì? có cần học không? áp dụng như nào để code tốt hơn?

Join the Conversation

2 Comments

Leave a comment

Leave a Reply