Nguyên tắc đóng mở (OCP) so với nguyên tắc đảo ngược phụ thuộc (DIP)


12

Tôi đã cố gắng để hiểu sự khác biệt giữa Nguyên tắc đóng mở (OCP) và Nguyên tắc đảo ngược phụ thuộc (DIP).

Dựa trên nghiên cứu tôi đã thực hiện trên internet cho đến nay, tôi đã đi đến kết luận rằng 'DIP là một lựa chọn để chúng ta có thể đạt được OCP'.

Tôi có đúng không?

Bạn có thể cho tôi một ví dụ không tuân theo DIP nhưng theo OCP không?

Câu trả lời:


16

Tôi đã viết một câu trả lời trước đây về Nguyên tắc đóng mở (OCP) so với Nguyên tắc thay thế (LSP) của Liskov và cả hai nguyên tắc này liên quan đến nhau rất nhiều, nhưng vẫn khác biệt về mặt khái niệm với một số ví dụ giả định về việc có một nhưng không phải là khác. Vì câu trả lời này, tôi sẽ chỉ chạm vào OCP một cách nhanh chóng và tìm hiểu sâu hơn về DIP và điều gì tạo nên dấu ấn đó.

Hãy thử thảo luận về cách OCP liên quan và khác biệt với Nguyên tắc đảo ngược phụ thuộc (DIP) trước tiên bằng cách giải thích các nguyên tắc khác nhau trước.

Nguyên tắc đảo ngược phụ thuộc

Đọc các nguyên tắc của chú Bob của OOD, bạn sẽ thấy rằng DIP nêu rõ như sau:

Phụ thuộc vào trừu tượng, không phụ thuộc vào.

Một sự trừu tượng hóa trong Java chỉ đơn giản là đạt được interfaceabstractcác từ khóa, nghĩa là bạn có một "hợp đồng" cho một số thực thể phần mềm mà mã phải tuân theo. Một số ngôn ngữ lập trình không có cơ sở để thiết lập rõ ràng các hành vi cho mã để tuân theo, vì vậy các tóm tắt phải được tuân theo một cách thủ công hơn là có trình biên dịch giúp bạn thực thi hợp đồng. Ví dụ, trong C ++, bạn có các lớp với các phương thức ảo và các ngôn ngữ lập trình động như Javascript, bạn phải đảm bảo bạn sử dụng các đối tượng theo cùng một cách (mặc dù trong trường hợp Javascript, điều này đã được mở rộng trong TypeScript có thêm hệ thống loại để giúp bạn thoát ra với các hợp đồng bằng văn bản được xác minh bởi trình biên dịch).

Tên này bao gồm thuật ngữ "đảo ngược" bởi vì theo truyền thống (trong thời kỳ lập trình đen tối cũ), bạn đã viết các cấu trúc phần mềm có các mô-đun cấp cao hơn tùy thuộc vào các mô-đun cấp thấp. Ví dụ, nó có ý nghĩa để có một ButtonAtKitchenđầu vào xử lý cho một KitchenLamp1KitchenLamp2. Thật không may, điều đó làm cho phần mềm trở nên cụ thể hơn rất nhiều so với mức cần thiết và biểu đồ đối tượng sẽ trông như thế này:

NútAtK Kitchen xử lý KitchenLamp1 và KitchenLamp2

Vì vậy, khi bạn làm cho phần mềm tổng quát hơn, bằng cách thêm "hợp đồng". Lưu ý cách các mũi tên trong biểu đồ đối tượng "đảo" hướng. Đó là đèn nhà bếp bây giờ phụ thuộc vào a Button. Nói cách khác, các chi tiết hiện đang phụ thuộc vào sự trừu tượng thay vì cách khác.

KitchenButton hiện có một bản tóm tắt IButton mà đèn bếp phụ thuộc vào

Do đó, chúng tôi có một định nghĩa chung hơn về DIP, cũng được nêu chi tiết trong bài viết gốc về DIP của chú Bob .

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 sự trừu tượng. B. Trừu tượng không nên phụ thuộc vào chi tiết. Chi tiết nên phụ thuộc vào trừu tượng.

Nguyên tắc đóng mở

Tiếp tục từ các nguyên tắc của chú Bob, bạn sẽ thấy rằng OCP nêu rõ như sau:

Bạn sẽ có thể mở rộng một hành vi lớp, mà không sửa đổi nó.

Một ví dụ để đạt được điều này là sử dụng mẫu Chiến lược trong đó một Contextlớp được đóng để sửa đổi (nghĩa là bạn hoàn toàn không thể thay đổi mã nội bộ) nhưng cũng mở cho phần mở rộng thông qua các phụ thuộc cộng tác (ví dụ: các lớp chiến lược).

Nói một cách tổng quát hơn, bất kỳ mô-đun nào cũng được xây dựng để có thể mở rộng thông qua các điểm mở rộng của nó.

Một mô-đun với các điểm mở rộng

OCP tương tự như DIP, phải không?

Không , không thực sự.

Mặc dù cả hai đang thảo luận về sự trừu tượng, chúng có khái niệm khác nhau. Cả hai nguyên tắc đang xem xét các bối cảnh khác nhau, OCP trên một mô-đun cụ thể và DIP trên một số mô-đun. Bạn có thể đạt được cả hai cùng một lúc như với hầu hết các mẫu thiết kế của Gang of Four, nhưng bạn vẫn có thể tránh xa con đường.

Trong ví dụ DIP được đề cập ở trên, với nút và đèn bếp, không có đèn bếp nào có thể mở rộng (hiện tại cũng không có bất kỳ yêu cầu nào giải thích chúng cần phải có). Thiết kế đang phá vỡ OCP nhưng tuân theo DIP .

Một ví dụ ngược lại (và một ví dụ) sẽ là một đèn nhà bếp có thể mở rộng (với điểm mở rộng là một cái gì đó giống như một LampShade) nhưng nút vẫn phụ thuộc vào đèn . Nó đang phá vỡ DIP nhưng tuân theo OCP .

Đừng lo lắng, nó sẽ xảy ra

Đây thực sự là điều bạn sẽ thấy xảy ra thường xuyên trong mã sản xuất, rằng một số phần của nó có thể phá vỡ một nguyên tắc. Trong các hệ thống phần mềm lớn hơn (nghĩa là bất cứ điều gì lớn hơn các ví dụ ở trên), bạn có thể phá vỡ một nguyên tắc nhưng thường giữ nguyên tắc khác vì bạn cần giữ mã đơn giản. Theo tôi, điều này là ổn đối với các mô-đun nhỏ và khép kín, vì chúng nằm trong bối cảnh liên quan đến Nguyên tắc Trách nhiệm Đơn lẻ (SRP).

Một khi một số mô-đun trở nên phức tạp mặc dù rất có thể bạn cần xem xét nó với tất cả các nguyên tắc trong tâm trí và thiết kế lại hoặc cấu trúc lại nó theo một số mô hình nổi tiếng.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.