Làm rõ nguyên tắc mở / đóng


25

Như tôi đã giải thích, nguyên tắc mở / đóng nói rằng một lần viết mã không nên được sửa đổi (ngoài sửa lỗi). Nhưng nếu quy tắc kinh doanh của tôi thay đổi, tôi có nên sửa đổi mã thực hiện những thay đổi đó không? Tôi nghi ngờ tôi không hiểu gì về nguyên tắc này vì nó không có ý nghĩa với tôi.

Câu trả lời:


22

Đây có lẽ là khó nhất trong các nguyên tắc vững chắc để giải thích. Để tôi thử. Hãy tưởng tượng bạn đã viết một lớp Hóa đơn hoạt động hoàn hảo và không có lỗi. Nó tạo ra một bản PDF của một hóa đơn.

Sau đó, một người nào đó nói rằng họ muốn có một hóa đơn HTML có liên kết trong đó. Bạn không thay đổi bất kỳ mã nào trong Hóa đơn để đáp ứng yêu cầu này. Thay vào đó, bạn tạo một lớp khác, HTMLInvoice, đó là những gì họ muốn bây giờ. Bạn tận dụng sự kế thừa để bạn không phải viết nhiều mã trùng lặp trong HTMLInvoice.

Mã cũ đã sử dụng Hóa đơn cũ không bị hỏng hoặc thực sự bị ảnh hưởng theo bất kỳ cách nào. Mã mới có thể sử dụng HTMLInvoice. (Nếu bạn cũng thực hiện Thay thế Liskov , L of solid, bạn có thể cung cấp các phiên bản HTMLInvoice cho mã hiện tại đang mong đợi các phiên bản Hóa đơn.) Mọi người đều sống hạnh phúc mãi mãi.

Hóa đơn được đóng để sửa đổi, mở để mở rộng. Và bạn phải viết Hóa đơn đúng trước để làm việc này, btw.


1
Nếu quy tắc kinh doanh thay đổi, không có giả định làm việc hoàn hảo không có lỗi, vậy nguyên tắc mở / đóng không áp dụng?
JeffO

Bản thân tôi đã đấu tranh với quy tắc này và những gì Kate gợi ý về cơ bản là những gì tôi đã kết luận về nó. Trong kinh doanh, bạn cố gắng lập trình các lớp nhỏ hơn, linh hoạt hơn để bạn có thể tái sử dụng chúng tốt. Nếu bạn làm cho chúng hoạt động tốt, bạn không muốn sửa đổi chúng. Nhưng chúng hiếm khi được "thực hiện" đầy đủ nên một số sửa đổi là không thể tránh khỏi. Lưu ý rằng văn bản nói "mô-đun" mặc dù, không phải đối tượng. Tôi thường áp dụng thành công OCP ở cấp độ chức năng, với các chức năng chặt chẽ thực hiện một điều hoàn hảo và không bao giờ cần thay đổi.
CodexArcanum

1
@Jeff OI phân biệt giữa sửa lỗi (trong đó mã không đáp ứng yêu cầu ban đầu và không ai muốn nó như vậy) và thay đổi các yêu cầu. Nếu tôi yêu cầu PDF và mã tạo PDF, thì không có lỗi, mặc dù bây giờ tôi cũng muốn HTML (và thường mọi người cũng muốn HTML chứ không phải thay vì.)
Kate Gregory

2
@Winston - đây là ý của tôi khi tôi nói bạn phải viết Hóa đơn đúng cách. Lý tưởng nhất là đã có một hóa đơn khá trừu tượng và bạn đã kế thừa PDFInvoice mong đợi điều này. Nếu không, bạn phải phá vỡ quy tắc một lần để thiết lập bản thân để không phá vỡ nó trong tương lai. Dù bằng cách nào, dự đoán những thay đổi trong tương lai là một phần rất lớn trong tất cả những điều này - và đó là phần "bắt và chặt một con voi" trong công thức.
Kate Gregory

1
Tuyên bố cuối cùng của bạn là quan trọng nhất. Mở / đóng là một lý tưởng thiết kế - và bạn phải có được thiết kế ngay trước mắt để đạt được nó. Không phải tất cả mọi thứ cần phải đáp ứng mở / đóng, nhưng đó là một công cụ mạnh mẽ nếu bạn có thể đến đó.
Alex Feinman

13

Bạn đã đọc bài viết Nguyên tắc đóng mở của những người bạn của chú Bob tại ObjectMentor chưa? Tôi nghĩ đó là một trong những lời giải thích tốt hơn ngoài kia.

Có rất nhiều heuristic liên quan đến thiết kế hướng đối tượng. Ví dụ, tất cả các biến thành viên nên được sử dụng là riêng tư, hoặc các biến toàn cầu có thể tránh được, hoặc sử dụng nhận dạng loại thời gian chạy (RTTI) là nguy hiểm. Nguồn gốc của các heuristic này là gì? Điều gì làm cho chúng đúng? Họ luôn luôn đúng? Cột này nghiên cứu nguyên tắc thiết kế làm cơ sở cho các heuristic này - nguyên tắc đóng mở.

Như Ivar Jacobson đã nói: tất cả các hệ thống thay đổi trong vòng đời của chúng. Điều này phải được lưu ý khi phát triển các hệ thống dự kiến ​​sẽ tồn tại lâu hơn phiên bản đầu tiên. Làm thế nào chúng ta có thể tạo ra các thiết kế ổn định khi đối mặt với sự thay đổi và nó sẽ tồn tại lâu hơn phiên bản đầu tiên? Bertrand Meyer đã cho chúng tôi hướng dẫn từ năm 1988 khi ông đưa ra nguyên tắc đóng mở nổi tiếng hiện nay. Để diễn giải anh ta:

PHẦN MỀM PHẦN MỀM (LỚP, MÔ HÌNH, CHỨC NĂNG, ETC.) NÊN ĐƯỢC MỞ RA KHAI THÁC, NHƯNG ĐÓNG CỬA ĐỂ SỬA ĐỔI.

Khi một thay đổi duy nhất đối với chương trình dẫn đến một loạt các thay đổi đối với các mô đun phụ thuộc, chương trình đó thể hiện các thuộc tính không mong muốn mà chúng ta đã liên kết với thiết kế của Bad bad. Chương trình trở nên mong manh, cứng nhắc, không thể đoán trước và không thể sử dụng được. Nguyên tắc đóng mở tấn công điều này theo một cách rất đơn giản. Nó nói rằng bạn nên thiết kế các mô-đun không bao giờ thay đổi . Khi yêu cầu thay đổi, bạn mở rộng hành vi của các mô-đun đó bằng cách thêm mã mới, không phải bằng cách thay đổi mã cũ đã hoạt động.

Sự miêu tả

Các mô-đun tuân thủ nguyên tắc đóng mở có hai thuộc tính chính.

  1. Họ đang mở rộng để mở rộng.
    Điều này có nghĩa là hành vi của mô-đun có thể được mở rộng. Rằng chúng ta có thể làm cho mô-đun hoạt động theo những cách mới và khác nhau khi các yêu cầu của ứng dụng thay đổi hoặc để đáp ứng nhu cầu của các ứng dụng mới.
  2. Họ đang đóng cửa để sửa đổi.
    Mã nguồn của một mô-đun như vậy là bất khả xâm phạm. Không ai được phép thực hiện thay đổi mã nguồn cho nó.

Có vẻ như hai thuộc tính này là bất hòa với nhau. Cách thông thường để mở rộng hành vi của một mô-đun là thay đổi mô-đun đó. Một mô-đun không thể thay đổi thường được cho là có hành vi cố định. Làm thế nào hai thuộc tính đối lập này có thể được giải quyết?

Trừu tượng là chìa khóa ...


3
Đây là một bài viết tốt giải thích trừu tượng. Có một điểm cơ bản cần được xem xét, và đó là một thiết kế trừu tượng tốt được đặt ra ngay từ đầu? Nhiều cửa hàng có rất nhiều mã kế thừa mà cách duy nhất để thay đổi nó là "sửa đổi", không phải "mở rộng". Nếu đây là trường hợp, thì có lẽ người ta nên làm việc để thay đổi điều đó, nhưng cho đến khi nó xảy ra, bạn bị mắc kẹt sửa đổi mã.
Michael K

@Chris, tuyệt - Tôi cũng giới thiệu cuốn sách "Clean code" của chú Bob nếu bạn thích thể loại này.
Martijn Verburg

@Michael - Hoàn toàn đồng ý, nó gần giống như việc phải cấu trúc lại mã để làm cho nó trở nên lý tưởng.
Martijn Verburg

Bài viết cho thấy tầm quan trọng của sự trừu tượng rất độc đáo. Nhưng tôi không nắm bắt được mối liên hệ giữa trừu tượng và cố gắng không bao giờ sửa đổi các mô-đun sau khi tôi viết chúng. Sự trừu tượng hóa có nghĩa là tôi có thể sửa đổi mô-đun X mà không cần sửa đổi mô-đun Y. Nhưng đó không phải là điểm cần làm để tôi có thể sửa đổi mô-đun X hoặc mô-đun Y nếu tôi cần?
Winston Ewert

1
Ồ Mã là bất khả xâm phạm? Tôi chưa bao giờ là một fan hâm mộ lớn của chú Bob. Hiệu trưởng này mang tính mô phạm, cực kỳ không thực dụng và có kết nối hạn chế với thực tế.
user949300

12

Câu trả lời của Kate Gregory là rất tốt, nhưng hãy xem xét một tình huống khác trong đó một yêu cầu mới có thể được thỏa mãn bằng một thay đổi tương đối nhỏ trong Invoicelớp hiện có . Ví dụ: giả sử một trường mới phải được thêm vào Hóa đơn PDF. Theo OCP, chúng ta vẫn nên tạo một lớp con mới, ngay cả khi trường mới có thể được thêm vào trong triển khai hiện có bằng cách thay đổi một vài dòng mã.

Theo hiểu biết của tôi, OCP phản ánh thực tế của thập niên 80 và đầu thập niên 90, nơi các dự án thường không sử dụng kiểm soát phiên bản, ít có các thử nghiệm hồi quy tự động hoặc lợi ích của các công cụ tái cấu trúc tinh vi. OCP là một nỗ lực để tránh nguy cơ phá vỡ mã đã được kiểm tra thủ công và đưa vào sản xuất. Ngày nay, chúng ta có những cách tốt hơn để quản lý rủi ro phá vỡ phần mềm hoạt động (cụ thể là hệ thống kiểm soát phiên bản, TDD và kiểm tra tự động và các công cụ tái cấu trúc).


2
Có, bởi vì trong thực tế, không thể tạo một lớp có thể mở rộng để phù hợp với tất cả các tương lai có thể, trừ khi bạn thực hiện tất cả các phương thức được bảo vệ (hút và cũng vi phạm nguyên tắc YAGNI, không quan trọng hơn nhiều so với O / C dito).
Martin Wickman

"Theo OCP, chúng ta vẫn nên tạo một lớp con mới, ngay cả khi trường mới có thể được thêm vào trong triển khai hiện có bằng cách thay đổi một vài dòng mã.": Thật sao? Tại sao không thêm các lĩnh vực mới hoặc phương pháp mới? Điểm quan trọng là bạn chỉ thêm (mở rộng) và không thay đổi những gì đã có.
Giorgio

Tôi nghĩ rằng nguyên tắc này có ý nghĩa khi làm việc với các thư viện / khung tiêu chuẩn. Bạn không muốn mở và sửa đổi các đoạn mã được thiết lập tốt. Mặt khác, đó là tất cả về tái cấu trúc liên tục và kiểm tra, thử nghiệm, kiểm tra.
mastaBlasta

@Giorgio Chắc chắn, thêm các trường hoặc phương thức mới những gì tôi muốn giới thiệu, trong hầu hết các trường hợp. Nhưng đó không phải là phần mở rộng , đó là "sửa đổi"; toàn bộ quan điểm của OCP là mã phải được "đóng để sửa đổi" (nghĩa là không có thay đổi nào đối với tệp nguồn tồn tại trước đó) trong khi "mở để mở rộng"; và mở rộng trong OCP được thực hiện thông qua kế thừa thực hiện.
Rogério

@ Rogério: Tại sao bạn xác định ranh giới giữa mở rộng và sửa đổi ở cấp lớp? Có một lý do đặc biệt cho việc này? Tôi thà đặt nó ở cấp phương thức: thay đổi một phương thức sẽ thay đổi hành vi của ứng dụng của bạn, thêm phương thức (công khai) mở rộng giao diện của nó.
Giorgio

6

Cá nhân tôi nghĩ rằng nguyên tắc này nên được thực hiện với một chút muối. Mã là hữu cơ, doanh nghiệp thay đổi và mã thay đổi theo nhu cầu của một doanh nghiệp khi thời gian tiếp tục.

Tôi cảm thấy rất khó khăn để hiểu rằng sự trừu tượng là chìa khóa. Điều gì xảy ra nếu sự trừu tượng ban đầu là sai? Nếu chức năng kinh doanh đã thay đổi đáng kể thì sao?

Nguyên tắc này về cơ bản đảm bảo rằng các ý định và hành vi GỐC của thiết kế không bao giờ phải thay đổi. Điều đó có thể hoạt động cho những người có API công khai và khách hàng của họ gặp khó khăn trong việc theo kịp các bản phát hành mới và một số trường hợp khác. Tuy nhiên, nếu một công ty sở hữu TẤT CẢ mã, thì tôi thách thức nguyên tắc này.

Có phạm vi kiểm tra tốt về mã của bạn sẽ giúp việc tái cấu trúc cơ sở mã của bạn trở nên dễ dàng. Điều đó có nghĩa là không sao khi hiểu sai - các bài kiểm tra của bạn sẽ giúp hướng dẫn bạn thiết kế tốt hơn.

Nói rằng, nếu không có bất kỳ thử nghiệm nào, thì nguyên tắc này là âm thanh.

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.