top of page

Nguyên lý SOLID-Nguyên lý nhà phát triển đều nên biết

Kiểu lập trình hướng đối tượng là một kiểu thiết kế mới cho nhà phát triển phần mềm. Tuy vậy, kiểu lập trình này không làm giảm bớt sự khó hiểu và nhầm lẫn của các chương trình. Nguyên lý SOLID, bao gồm năm nguyên tắc được phát triển bởi Robert C.Martin ra đời nhằm khắc phục điều này, giúp chương trình trở nên dễ đọc và dễ dàng duy trì hơn.

Nguyên lý SOLID

Năm nguyên tắc đó bao gồm:

S: Single Responsibility Principle (Nguyên tắc đơn nhiệm)

O: Open-Closed Principle (Nguyên tắc đóng-mở)

L: Liskov Substitution Principle (Nguyên tắc thay thế)

I: Interface Segregation Principle (Nguyên tắc phân chia giao diện)

D: Dependency Inversion Principle (Nguyên tắc đảo ngược phụ thuộc)

Ngay sau đây, chúng ta hãy đi vào làm rõ những nguyên tắc ở trên.


S: Nguyên tắc đơn nhiệm (SRP)

Một lớp (class) nên chịu một trách nhiệm duy nhất. Nếu một lớp có nhiều hơn một trách nhiệm, chúng sẽ trở thành một kết hợp.

Lưu ý: Nguyên tắc này không chỉ áp dụng cho các lớp mà còn áp dụng cho các thành phần phần mềm và dịch vụ siêu nhỏ khác.

Ví dụ:

đoạn mã ví dụ

Ở ví dụ này, lớp Animal (class Animal) vi phạm nguyên lý đơn nhiệm (SRP).

Theo ví dụ chúng ta có thể rút ra hai trách nhiệm: Hàm tạo và getAnimalName quản lý thuộc tính Animal, trong khi saveAnimal quản lý bộ lưu trữ Animal trên cơ sở dữ liệu (tuy vậy theo SRP tuyên bố rằng mỗi lớp nên có một trách nhiệm).

Do đó, chúng ta nên tạo ra một lớp chịu trách nhiệm duy nhất là lưu trữ cơ sở dữ liệu động vật.

class animalDB

O: Nguyên tắc đóng mở (OCP)

Nguyên tắc O trong nguyên lý SOLID này phát biểu rằng: các thực thể phần mềm (lớp (class), mô-đun (modules), hàm (functions)) phải được mở để mở rộng chứ không phải để sửa đổi.

Tiếp tục với ví dụ class Animal ở trên.

O: Nguyên tắc đóng mở (OCP)

Chúng ta hãy tạo một danh sách các động vật và thêm âm thanh của chúng vào.

O: Nguyên tắc đóng mở (OCP)

Hàm AnimalSound không tuân theo nguyên tắc đóng-mở vì không thể “đóng” mỗi khi thêm các loại động vật mới vào.

Ví dụ ta thêm một động vật mới là Rắn (Snake) vào

Chúng ta phải sửa đổi hàm AnimalSound như sau:

sửa đổi hàm AnimalSound

Bạn thấy đó, cứ mỗi khi có một loại động vật được thêm vào, các câu lệnh if lại được lặp lại, và nếu ứng dụng của bạn trở nên phức tạp thì điều này thực sự không tốt chút nào.

Vậy làm cách nào để làm AnimalSound phù hợp với OCP?

làm AnimalSound phù hợp với OCP

Animal bây giờ có một phương thức ảo makeSound. Bằng cách sử dụng phương thức này, mỗi khi một động vật mới được thêm vào, AnimalSound không cần phải thay đổi.

Apply now

L: Nguyên tắc thay thế (LSP)

Một lớp con (sub-class) phải được thay thế cho siêu hạng của nó (super-class).


Mục đích của nguyên lý SOLID này là để xác định rằng một lớp sub-class có thể đảm nhận vị trí của một super-class mà không có lỗi.


Hãy cùng đi đến ví dụ động vật (Animal) của chúng ta.

Ví dụ này vi phạm nguyên tắc LSP và cả OCP. Chương trình vừa phải biết mọi loại động vật và đếm chân của chúng. Mỗi khi muốn thay đổi, chức năng phải sửa đổi để có thể thêm động vật mới.


Để thực hiện chức năng này theo đúng nguyên tắc LSP:

- Nếu super-class (Animal) có phương thức chấp nhận tham số loại super-class (Animal). Lớp sub-class của nó (Pigeon) nên chấp nhận làm đối số với loại super-class (Animal type) hoặc sub-class type(Pigeon type).

- Nếu super-class trả về loại super-class (Animal). Sub-class của nó phải trả về loại super-class (Animal type) hoặc sub-class (Pigeon).


Hàm AnimalLegCount quan tâm ít hơn vào Animal type, được gọi là phương thức LegCount. Chúng bao gồm tham số thuộc loại động vật, hoặc là lớp thú hoặc sub-class của nó.

Bây giờ hãy triển khai lớp Animal theo phương thức LegCount và các sub-class của nó


Khi được chuyển đến hàm AnimalLegCount, nó sẽ trả về số chân mà Sư tử có. Bạn thấy đó, AnimalLegCount không cần biết loại Động vật để trả về số lượng chân của nó.


I: Nguyên tắc phân chia giao diện (ISP)

Thay vì dùng một giao diện (interface) lớn, nên tách chúng thành nhiều giao diện (interface) nhỏ với nhiều mục đích cụ thể.

Hãy tham khảo giao diện IShape dưới đây.

I: Nguyên tắc phân chia giao diện (ISP)

Giao diện này vẽ hình vuông, hình tròn, hình chữ nhật . Lớp hình tròn, hình vuông, hình chữ nhật được thực hiện bởi giao diện IShape phải xác định các phương thức drawCircle (), drawSapes (),drawRectangle().


Lớp Rectangle (hình chữ nhật) thực hiện phương thức (drawCircle and drawSquare), nhưng nó lại không sử dụng phương thức tương tự như Square (hình vuông) thực hiện phương thức drawCircle, drawRectangle, và class Circle.

Nếu chúng ta thêm một phương thức khác vào giao diện Ishape, như drawTriangle()


ISP cho rằng các giao diện chỉ nên thực hiện một công việc (giống như SRP). Ở đây, giao diện Ishape thực hiện các hành động nên được xử lý độc lập bởi các giao diện khác.

Để làm cho giao diện IShape tuân thủ nguyên tắc ISP, chúng ta tách biệt các hành động với các giao diện khác nhau:

làm cho giao diện IShape tuân thủ nguyên tắc ISP

Giao diện ICircle chỉ xử lý bản vẽ hình tròn và tương ứng đối với hình khác. Các lớp (Hình tròn, Hình chữ nhật, Hình vuông, Tam giác, v.v.) chỉ có thể kế thừa từ giao diện IShape và thực hiện hành vi vẽ của riêng chúng.


D: Nguyên tắc đảo ngược phụ thuộc (DIP)

Nguyên tắc trong bộ nguyên lý SOLID này phát biểu rằng:

A. Các mô-đun cấp cao không nên phụ thuộc vào các mô-đun cấp thấp. Cả hai nên phụ thuộc vào abstractions (trừu tượng).

B. Giao diện abstractions không nên phụ thuộc vào chi tiết mà hông tin chi tiết nên phụ thuộc vào abstractions.



Ở đây, HTTP là thành phần cấp cao trong khi httpService là thành phần cấp thấp. Thiết kế này vi phạm DIP A. Lớp Ths http buộc phải phụ thuộc vào lớp XMLHttpService. Nếu thay đổi dịch vụ kết nối http, có thể chúng tôi muốn kết nối với internet thông qua Nodejs hoặc thậm chí Mock service http. Chúng ta sẽ phải hết sức chuyển qua tất cả các phiên bản của HTTP để chỉnh sửa mã và điều này vi phạm nguyên tắc OCP.


Giao diện kết nối [Connection] có một phương thức yêu cầu. Với điều này, chúng ta chuyển một đối số kiểu [Connection] sang lớp [Http]


Bất kể loại dịch vụ kết nối http được truyền đến http, nó có thể dễ dàng kết nối với mạng mà không cần biết loại kết nối mạng. Bây giờ chúng ta có thể triển khai lại lớp XMLHttpService của mình để triển khai giao diện Kết nối:

triển khai lại lớp XMLHttpService

triển khai lại lớp XMLHttpService

Chúng ta có thể thấy rằng cả mô-đun cấp cao và mô-đun cấp thấp đều phụ thuộc vào abstractions. Lớp http (mô-đun mức cao) phụ thuộc vào giao diện Connection (abstraction) và các loại dịch vụ http (mô-đun mức thấp) lần lượt, phụ thuộc vào giao diện Connection (abstraction).

Công việc IT

Hi vọng với nguyên lý SOLID trên đây sẽ giúp cho mọi nhà phát triển phần mềm duy trì tốt hơn các ứng dụng trong tương lai. Ban đầu việc tuân thủ các nguyên tắc này có thể nản chí, do đó chúng ta nên thực hành và tuân thủ đều đặn để đạt được kết quả mong muốn.

Nguồn tổng hợp

---

JT1 - IT Recruitment Agency Website: https://www.jt1.vn Email: hi@jt1.vn Điện thoại: +8428 6675 6685 Xem thêm các bài viết khác tại: https://www.jt1.vn/blog Theo dõi chúng tôi tại: https://www.facebook.com/jt1asia/

Job_link_banner.gif
bottom of page