- It costs 5 mins to read

Một trong những Design Pattern để viết code chất lượng mà các Developer giỏi thường hay áp dụng chính là SRP (The Single Responsibility Principle), Nguyên tắc trách nhiệm duy nhất, hiểu nôm na là một lớp chỉ (và nên) có một trách nhiệm duy nhất mà nó được giao - SRP cũng là chữ cái S trong bộ Nguyên tắc SOLID (Single responsibility, Open/closed, Liskov substitution, Interface segregation, Dependency inversion) nhuần nhuyễn nguyên tắc này đảm bảo bạn sẽ trở thành một lập trình viên ‘cứng’. Bất kì phần code nào không có khả năng bảo trì (maintainable) và không thể thích nghi với những yêu cầu thay đổi thì sẽ nhanh chóng bị lỗi thời, Do vậy SRP rất cần thiết và tối quan trọng để viết code tốt và dễ bảo trì.

Tản mạn ngoài lề một chút, mình kiếm cơm bằng nghề coder chưa lâu, từ lúc bắt đầu đến thời điểm hiện tại thì chỉ theo xuyên suốt có 1 dự án. Dự án vừa rồi cũng mới trãi qua đợt Refactor lớn, tách Front-End khỏi Back-End. Do vừa Refactor, cleaning code, vừa optimize lại mới thấy rằng chỉ mới vài tháng trước đây thôi: Code mình viết bừa bộn hết sức, từ cách đặt tên method, tên biến tùy tiện, tham số thì truyền vào không theo một thứ tự hay quy chuẩn nào, các lớp không được phân tách rõ ràng mà trộn lẫn vào nhau… cho đến việc cố gắng nhét một mớ nhiệm vụ vào trong 1 method, dẫn đến code cuối cùng dài ngoằng, người khác e ngại và đọc không hiểu nổi, còn bản thân thì cũng mất kha khá thời gian để đọc lại những gì mình đã từng viết. (việc một controller hay một model hơn cả ngàn dòng code không phải hiếm gặp.) - Cũng nhờ đợt Refactor này mà mình mới thấy rõ cái dở của bản thân mình trước đây và thấm thía được tầm quan trọng của việc làm thế nào để code trở nên tốt hơn và dễ bảo trì hơn. (Vâng, Tôi còn rất nhiều thứ phải học để trở thành Developer giỏi)

# When I wrote this, only God and I understood what I was doing
# Now, God only knows

What is a Responsibility?

Quay trở lại chủ đề của bài viết hôm nay, có một câu hỏi được đặt ra là trách nhiệm là gì? Và tại sao việc phân tách và chia nhỏ trách nhiệm cho từng lớp theo SRP lại quan trọng?

Vâng, có thể coi một trách nhiệm là một lý do để thay đổi. Bất kỳ thay đổi nhỏ nào của yêu cầu có thể dễ dàng dẫn đến nhiều thay đổi ở các phần khác nhau của code. SRP giúp các lập trình viên viết code đó được tách riêng biệt ra, nơi mà mỗi lớp có công việc riêng của nó. Nếu các đặc tả kỹ thuật của công việc này thay đổi, thì các lập trình viên chỉ cần thay đổi những lớp xác định. Sự thay đổi này ít có khả năng phá vỡ toàn bộ ứng dụng, và các lớp khác vẫn sẽ làm công việc của chúng như trước. Nếu một tester thông báo cho Developer biết chức năng nào bị lỗi, Developer sẽ nhanh chóng xác định được lớp nào đang đảm nhận chức năng đó và chỉ cần sửa nó, rebuild, retest, redeploy lại riêng lớp đó mà thôi.

Ví dụ, xét một module biên soạn và in một báo cáo. Một module như vậy có thể bị sửa vì hai lý do. Thứ nhất, nội dung của báo cáo có thể thay đổi. Thứ hai, định dạng của báo cáo có thể thay đổi. Hai thứ đó thay đổi vì những nguyên nhân rất khác nhau; một bên mang tính nội dung, bên kia mang tính hình thức. Nguyên tắc trách nhiệm duy nhất nói rằng hai khía cạnh đó của cùng một bài toán thực sự là hai trách nhiệm riêng biệt, và do đó nên nằm trong hai lớp hay module khác nhau. Sẽ là một thiết kế tồi nếu ghép cặp lại với nhau hai thứ có thể thay đổi vì những lý do khác nhau tại những thời điểm khác nhau, nó có thể gây ra những lỗi không mong đợi.

Như đã định nghĩa ở trên, một trách nhiệm là một lý do để thay đổi. Nếu bạn có thể thấy nhiều hơn 1 lý do để thay đổi thì lớp đó không còn là Single Responsibility nữa. Điều này đôi lúc rất khó để nhận ra, xét ví dụ về một Interface Modem như sau, bạn nghĩ rằng trông nó có thực sự hoàn hảo?

interface Modem {
  public void dial(String pno);
  public void hangup();
  public void send(char c);
  public char recv();
}

Tuy nhiên, chúng ta có thể nhận thấy 2 trách nhiệm đang được nhét vào Interface Modem này. Trách nhiệm đầu tiên là connection management, trách nhiệm thứ hai là data management. Hàm dialhangup quản lý kết nối của modem trong khi hàm sendrecv có chức năng trao đổi dữ liệu. 2 tập chức năng hầu như không có gì chung cả, vậy nên ta hoàn toàn có thể tách chúng ra thành 2 lớp con riêng biệt.

Separated Modem Interface

The SRP is one of the simplest of the principle, and one of the hardest to get right. Con-joining responsibilities is something that we do naturally. Finding and separating those responsibilities from one another is much of what software design is really about.

Việc phát triển code bằng cách sử dụng các kỹ thuật này và nguyên tắc trách nhiệm duy nhất (Single Responsibility Principle) có thể dường như là một nhiệm vụ khó khăn, nhưng những nỗ lực này chắc chắn sẽ mang lại lợi ích to lớn khi dự án ngày càng lớn lên và tiếp tục phát triển.

Đọc thêm: