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 interface
và abstract
cá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 KitchenLamp1
và KitchenLamp2
. 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:
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.
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 Context
lớ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ó.
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.