Áp dụng nguyên tắc SOLID trong lập trình Android

1. SOLID là gì

    Thử tưởng tượng bạn đang ở trong một thư viện sách. Bạn muốn tìm một cuốn sách nào đó. Nếu như thư viện được sắp xếp gọn gàng, phân loại sách tốt thì bạn sẽ dễ dàng tìm được cuốn mình cần. Ngoài ra, nếu như thư viện mà được thiết kế nội thất tốt, bạn sẽ có hứng thú hơn khi đọc sách.

    Cũng giống như ví dụ trên, khi bạn xây dựng một ứng dụng, bạn phải biết cách viết code và tổ chức sao cho gọn gàng, dễ đọc. Đặc biệt với dự án đặc thù khách hàng thay đổi yêu cầu liên tục thì việc này lại càng trở nên quan trọng hơn.

    S.O.L.I.D là bộ quy tắc viết code được phát minh bởi Robert C. Martin (Uncle Bob). Khi bạn ứng dụng bộ quy tắc này vào dự án, đảm bảo mã nguồn của bạn sẽ cực kỳ “clean” luôn.

    Vậy SOLID là gì?
SOLID là một bộ quy tắc gồm 5 nguyên lý:

  • S — Single Responsibility Principle
  • O — Open Closed Principle
  • L — Liskov Substitution Principle
  • I — Interface Segregation Principle
  • D — Dependency Inversion Principle

    Bây giờ chúng ta cùng đi vào từng nguyên lý và cách sử dụng nó trong Android như thế nào nhé :3

2. Single Responsibility Principle (SRP)

    Nguyên lý đầu tiên tương ứng với chữ S có nội dung là:

    A class should have one and only one reason to change


Giống như tên gọi, mỗi một class/module chỉ thực hiện một chức năng. Nếu class của bạn có nhiều hơn 1 nhiệm vụ, hãy tách các function đó sang 1 class khác.

    Để hiểu hơn về nguyên lý này, chúng ta cùng xem xét một số ví dụ sau:


Ở đây, class Student có 2 nhiệm vụ là lưu trữ, và xuất thông tin của sinh viên
Do đó, chỉ cần ta thay đổi DB, thay đổi cách xuất kết quả, … ta sẽ phải sửa đổi class này. Càng về sau class sẽ càng phình to ra. Theo đúng nguyên lý, ta phải tách class này ra làm 2 class riêng. Tuy số lượng class nhiều hơn nhưng việc sửa chữa sẽ đơn giản hơn, class ngắn hơn nên cũng ít bug hơn.

    Best solution

    

    Một ví dụ khác về 1 class Adapter với các logic được implement trong onBindViewHolder như sau:

    

    Cách viết code này đã vi phạm nguyên lý của SOLID. Hàm onBindViewHolder() chỉ nên làm một nhiệm vụ duy nhất là thiết lập dữ liệu để hiển thị ra view thôi, không xử lý bất kì logic nào cả.

3. Open-Closed principle (OCP).

    Objects or entities should be open for extension but closed for modification


Theo nguyên lý này, mỗi khi ta muốn thêm chức năng, ... cho chương trình, chúng ta nên viết class mới mở rộng class cũ ( bằng cách kế thừa hoặc sở hữu class cũ), không khuyến khích việc sửa đổi class cũ.

    Ví dụ cho nguyên tắc OCP:
Bad code

    

    Better Solution

    

    Trong ví dụ này, chúng ta tập trung vào việc mở rộng các function bằng cách sử dụng interface.
Trong đoạn code đầu tiên, giả sử chúng ta có thêm 1 đối tượng nữa là Square thì trong class AreaManager cần tạo thêm function là getSquareArea với param truyền vào là Square. Cứ như thế class AreaManager sẽ phình to lên rất nhiều.
Thay vào đó chúng ta chỉ cần tạo class Square implement Shape và overriding function getArea().

4. Liskov Substitution Principle (LSP)

    Nguyên lý thứ ba, tương ứng với chữ L trong SOLID. Nội dung nguyên lý:

    Objects in a program should be replaceable with instances of their subtypes without altering the correctness of that program.


Trong đó nói rằng các lớp con có thể thay thế cho các lớp cha mà không làm thay đổi tính đúng đắn của chương trình. Ví dụ: nếu MySubclass là một lớp con của MyClass, bạn sẽ có thể thay thế MyClass bằng MySubclass mà không làm hỏng chương trình.

    Nghe có vẻ khó hiểu nhỉ? Mình sẽ lấy một ví dụ để làm rõ hơn về nguyên lý này nhé.

    

    Chúng ta có class Bird có hai hành vi là kêu và bay, class Eagle và Penguin kế thừa class Bird. Thoạt nhìn thì không có vấn đề gì, tuy nhiên chim cánh cụt thì lại không bay được nên việc overriding phương thức fly() trong class Penguin sẽ vi phạm nguyên lý LSP

    Better Solution

    

5. Interface Segregation Principle — ISP

    Many client-specific interfaces are better than one general-purpose interface


Thay vì dùng 1 interface lớn, ta nên tách thành nhiều interface nhỏ, với nhiều mục đích cụ thể

    Nguyên lý này khá dễ hiểu. Hãy tưởng tượng chúng ta có 1 interface lớn, khoảng 100 methods. Việc implements sẽ khá cực khổ, ngoài ra còn có thể dư thừa vì 1 class không cần dùng hết 100 method. Khi tách interface ra thành nhiều interface nhỏ, gồm các method liên quan tới nhau, việc implement và quản lý sẽ dễ hơn.

    Ví dụ cho nguyên lý này. Chúng ta có interface ComicsDataSource có các function như sau:

    

    Dễ dàng thấy, các function trong interface này chia thành 2 nhóm chức năng là xử lý dữ liêu ở Local và xử lý dữ liệu ở Remote. Nếu viết code như thế này thì dưới Local khi implementation interface ComicsDataSource, nó sẽ overriding cả các function ở Remote lẫn Local như thế này.

    

    Điều này thật không hợp lí. Giải pháp ở đây là nên tách interface ComicsDataSource thành 2 interface nhỏ tương ứng với hai nhóm chức năng như sau:

    

6. Dependency Inversion Principle — DIP

    High-level modules should not depend on low-level modules. Both should depend on abstractions. Abstractions should not depend on details. Details should depend on abstractions.


Ý nói là các module cấp cao không nên phụ thuộc vào các modules cấp thấp. Cả 2 nên phụ thuộc vào abstraction. Ngoài ra, interface(abstraction) không nên phụ thuộc vào chi tiết, mà ngược lại. (Các class giao tiếp với nhau thông qua interface, không phải thông qua implementation)

Nếu bạn đã sử dụng mô hình MVP thì có thể đã sử dụng nguyên lý này. Bạn có một Interface giúp chúng ta kết nối các class. Tức là, các UI class không cần quan tâm đến logic được implement trong Presenter như thế nào.
Ví dụ bằng code nhé.

    

    Chúng ta có interface là Presenter có các function liên quan đến việc CRUD Note. class NotePresenter implementation interface Presenter sẽ triển khai tất cả các function trong interface đó. Trong NoteFragment khi muốn thực hiện các thao tác CRUD chỉ cần gọi thông qua tên function trong interface Presenter. Vì vậy, nếu bạn có phải thay đổi logic bên trong Presenter thì UI cũng không biết, không cần phải thay đổi code vì điều đó.

7. Tổng kết

    Chúng ta vừa cùng nhau tìm hiểu qua 5 nguyên lí của SOLID. Tóm gọn lại, những nguyên tắc này sẽ giúp chúng ta: viết code trong sáng hơn, rõ ràng hơn, các module hệ thống tách bạch hơn, phần mềm sẽ dễ dàng kiểm thử, bảo trì và mở rộng hơn.

    Trên đây là những hiểu biết cơ bản của mình về SOLID. Hi vọng đã cung cấp cho các bạn những thông tin bổ ích!

8. Tài liệu tham khảo

    https://docs.microsoft.com/en-us/archive/blogs/cdndevs/the-solid-principles-explained-with-motivational-posters

Nguồn: Viblo

Bình luận
Vui lòng đăng nhập để bình luận
Một số bài viết liên quan