Tại sao nhiều nhà phát triển phần mềm vi phạm nguyên tắc mở / đóng?


74

Tại sao nhiều nhà phát triển phần mềm vi phạm nguyên tắc mở / đóng bằng cách sửa đổi nhiều thứ như đổi tên các chức năng sẽ phá vỡ ứng dụng sau khi nâng cấp?

Câu hỏi này nhảy lên đầu tôi sau các phiên bản nhanh và liên tục trong thư viện React .

Mỗi khoảng thời gian ngắn tôi nhận thấy nhiều thay đổi về cú pháp, tên thành phần, ... vv

Ví dụ trong phiên bản sắp tới của React :

Cảnh báo khấu hao mới

Thay đổi lớn nhất là chúng tôi đã trích xuất React.PropTypes và React.createClass vào các gói riêng của họ. Cả hai vẫn có thể truy cập thông qua đối tượng React chính, nhưng sử dụng một trong hai sẽ ghi lại cảnh báo khấu hao một lần vào bàn điều khiển khi ở chế độ phát triển. Điều này sẽ cho phép tối ưu hóa kích thước mã trong tương lai.

Những cảnh báo này sẽ không ảnh hưởng đến hành vi của ứng dụng của bạn. Tuy nhiên, chúng tôi nhận ra rằng chúng có thể gây ra một số sự thất vọng, đặc biệt nếu bạn sử dụng khung thử nghiệm coi console.error là một thất bại.


  • Những thay đổi này được coi là vi phạm nguyên tắc đó?
  • Là người mới bắt đầu một cái gì đó như React , làm thế nào để tôi học nó với những thay đổi nhanh chóng này trong thư viện (thật là bực bội)?

6
Đây rõ ràng là một ví dụ về việc quan sát nó và yêu cầu của bạn 'rất nhiều' là không có căn cứ. Các dự án Lucene và RichFaces là những ví dụ nổi tiếng và API cổng Windows Comm, nhưng tôi không thể nghĩ ra bất kỳ ai khác. Và React có thực sự là một 'nhà phát triển phần mềm lớn' không?
dùng207421

62
Giống như bất kỳ nguyên tắc nào, OCP có giá trị của nó. Nhưng nó đòi hỏi các nhà phát triển phải có tầm nhìn xa vô hạn. Trong thế giới thực, mọi người thường nhận được thiết kế đầu tiên của họ sai. Thời gian trôi qua, một số người thích làm việc xung quanh những sai lầm cũ của họ vì mục đích tương thích, những người khác thích cuối cùng dọn sạch chúng để có một cơ sở mã hóa nhỏ gọn và không bị gánh nặng.
Theodoros Chatzigiannakis

1
Lần cuối cùng bạn nhìn thấy một ngôn ngữ hướng đối tượng "như dự định ban đầu" là khi nào? Nguyên tắc cốt lõi là một hệ thống nhắn tin có nghĩa là mọi bộ phận của hệ thống đều có thể mở rộng vô hạn bởi bất kỳ ai. Bây giờ hãy so sánh với ngôn ngữ giống như OOP điển hình của bạn - có bao nhiêu cho phép bạn mở rộng một phương thức hiện có từ bên ngoài? Có bao nhiêu làm cho nó đủ dễ dàng để hữu ích?
Luaan

Di sản hút. 30 năm kinh nghiệm đã chỉ ra rằng bạn nên hoàn toàn từ bỏ di sản và bắt đầu mới mẻ, mọi lúc. Ngày nay mọi người đều có kết nối ở mọi nơi mọi lúc, vì vậy di sản hoàn toàn không liên quan đến ngày hôm nay. ví dụ cuối cùng là "Windows so với Mac". Theo truyền thống, Microsoft đã cố gắng "hỗ trợ di sản", bạn thấy điều này theo nhiều cách. Apple luôn chỉ nói "F- - - Bạn" với người dùng cũ. (Điều này áp dụng cho mọi thứ, từ ngôn ngữ, thiết bị đến HĐH.) Trên thực tế, Apple đã hoàn toàn chính xác và MSFT hoàn toàn sai, đơn giản và đơn giản.
Fattie

4
Bởi vì chính xác là không có "nguyên tắc" và "mẫu thiết kế" nào hoạt động 100% thời gian trong cuộc sống thực.
Matti Virkkunen

Câu trả lời:


148

Câu trả lời của IMHO JacquesB, mặc dù chứa rất nhiều sự thật, cho thấy sự hiểu lầm cơ bản về OCP. Công bằng mà nói, câu hỏi của bạn cũng đã thể hiện sự hiểu lầm này - việc đổi tên các chức năng phá vỡ tính tương thích ngược , nhưng không phải là OCP. Nếu việc phá vỡ tính tương thích có vẻ cần thiết (hoặc duy trì hai phiên bản của cùng một thành phần để không phá vỡ tính tương thích), thì OCP đã bị phá vỡ trước đó!

Như Jorg W Mittag đã đề cập trong các bình luận của mình, nguyên tắc không nói rằng "bạn không thể sửa đổi hành vi của một thành phần" - nó nói, người ta nên cố gắng thiết kế các thành phần theo cách chúng được mở để sử dụng lại (hoặc mở rộng) theo nhiều cách, mà không cần phải sửa đổi. Điều này có thể được thực hiện bằng cách cung cấp đúng "điểm mở rộng", hoặc, như được đề cập bởi @AntP, "bằng cách phân tách cấu trúc lớp / chức năng đến điểm mà mọi điểm mở rộng tự nhiên đều ở đó theo mặc định." IMHO theo OCP không có gì chung với "giữ nguyên phiên bản cũ không thay đổi để tương thích ngược" ! Hoặc, trích dẫn bình luận của @ DerekElkin bên dưới:

OCP là lời khuyên về cách viết mô-đun [...], chứ không phải về việc thực hiện quy trình quản lý thay đổi không bao giờ cho phép các mô-đun thay đổi.

Các lập trình viên giỏi sử dụng kinh nghiệm của họ để thiết kế các thành phần với các điểm mở rộng "đúng" (hoặc - thậm chí tốt hơn - theo cách không cần các điểm mở rộng nhân tạo). Tuy nhiên, để làm điều này một cách chính xác và không có sự áp đảo không cần thiết, bạn cần biết trước các trường hợp sử dụng trong tương lai của thành phần của bạn có thể trông như thế nào. Ngay cả các lập trình viên có kinh nghiệm cũng không thể nhìn vào tương lai và biết trước tất cả các yêu cầu sắp tới. Và đó là lý do tại sao đôi khi tính tương thích ngược cần phải bị vi phạm - cho dù thành phần của bạn có bao nhiêu điểm mở rộng, hoặc nó tuân theo OCP như thế nào đối với một số loại yêu cầu nhất định, sẽ luôn có một yêu cầu không thể thực hiện dễ dàng mà không sửa đổi thanh phân.


14
IMO lý do lớn nhất để "vi phạm" OCP là phải mất rất nhiều nỗ lực để tuân thủ đúng. Eric Lippert có một bài đăng blog tuyệt vời về lý do tại sao nhiều lớp .NET framework dường như vi phạm OCP.
BJ Myers

2
@BJMyer: cảm ơn vì đường link. Jon Skeet có một bài viết tuyệt vời về OCP vì việc nuôi ong rất giống với ý tưởng về biến thể được bảo vệ.
Doc Brown

8
ĐIỀU NÀY! OCP nói rằng bạn nên viết mã có thể thay đổi mà không cần chạm vào! Tại sao? Vì vậy, bạn chỉ phải kiểm tra, xem xét và biên dịch nó một lần. Hành vi mới nên đến từ mã mới. Không phải bằng cách bắt vít với mã đã được chứng minh cũ. Còn tái cấu trúc thì sao? Tái cấu trúc tốt là một vi phạm rõ ràng của OCP! Đó là lý do tại sao việc viết mã nghĩ rằng bạn sẽ chỉ cần cấu trúc lại nó nếu các giả định của bạn thay đổi. Không! Đặt từng giả định trong hộp nhỏ của riêng nó. Khi nó sai không sửa hộp. Viết một cái mới. Tại sao? Bởi vì bạn có thể cần phải quay lại cái cũ. Khi bạn làm điều đó, thật tuyệt nếu nó vẫn hoạt động.
candied_orange

7
@CandiedOrange: cảm ơn bình luận của bạn. Tôi không thấy tái cấu trúc và OCP trái ngược như bạn mô tả. Để viết các thành phần tuân theo OCP thường đòi hỏi một số chu kỳ tái cấu trúc. Mục tiêu phải là một thành phần không cần sửa đổi để giải quyết toàn bộ "gia đình" các yêu cầu. Tuy nhiên, người ta không nên thêm các điểm mở rộng tùy ý vào một thành phần "chỉ trong trường hợp", điều đó dẫn đến quá dễ dàng để áp đảo. Dựa vào khả năng tái cấu trúc có thể là sự thay thế tốt hơn cho điều này trong rất nhiều trường hợp.
Doc Brown

4
Câu trả lời này thực hiện tốt việc gọi ra các lỗi trong câu trả lời hàng đầu (hiện tại) - Tôi nghĩ rằng điều quan trọng nhất để thành công với mở / đóng là dừng suy nghĩ về "điểm mở rộng" và bắt đầu suy nghĩ về việc phân tách cấu trúc lớp / hàm đến điểm mà mọi điểm mở rộng tự nhiên đều ở đó theo mặc định. Lập trình "bên ngoài" là một cách rất tốt để đạt được điều này, trong đó mọi kịch bản mà phương thức / chức năng hiện tại của bạn phục vụ được đẩy ra một giao diện bên ngoài, tạo thành một điểm mở rộng tự nhiên cho các nhà trang trí, bộ điều hợp, v.v.
Ant P

67

Nguyên tắc mở / đóng có lợi ích, nhưng nó cũng có một số nhược điểm nghiêm trọng.

Về lý thuyết , nguyên tắc giải quyết vấn đề tương thích ngược bằng cách tạo mã "mở để mở rộng nhưng đóng để sửa đổi". Nếu một lớp có một số yêu cầu mới, bạn không bao giờ sửa đổi mã nguồn của chính lớp đó mà thay vào đó tạo ra một lớp con ghi đè lên các thành viên thích hợp cần thiết để thay đổi hành vi. Do đó, tất cả các mã được viết so với phiên bản gốc của lớp đều không bị ảnh hưởng, vì vậy bạn có thể tin rằng thay đổi của bạn không phá vỡ mã hiện có.

Trong thực tế, bạn dễ dàng kết thúc với sự phình to mã và một mớ hỗn độn của các lớp lỗi thời. Nếu không thể sửa đổi một số hành vi của một thành phần thông qua tiện ích mở rộng, thì bạn phải cung cấp một biến thể mới của thành phần với hành vi mong muốn và giữ phiên bản cũ không thay đổi để tương thích ngược.

Giả sử bạn khám phá một lỗ hổng thiết kế cơ bản trong một lớp cơ sở mà rất nhiều lớp kế thừa từ đó. Nói lỗi là do một trường riêng thuộc loại sai. Bạn không thể sửa lỗi này bằng cách ghi đè thành viên. Về cơ bản, bạn phải ghi đè toàn bộ lớp, có nghĩa là cuối cùng bạn sẽ mở rộng Objectđể cung cấp một lớp cơ sở thay thế - và bây giờ bạn cũng phải cung cấp các lựa chọn thay thế cho tất cả các lớp con, do đó kết thúc với hệ thống phân cấp đối tượng trùng lặp, một hệ thống phân cấp bị lỗi, một thứ bậc được cải thiện . Nhưng bạn không thể xóa hệ thống phân cấp thiếu sót (vì việc xóa mã là sửa đổi), tất cả các máy khách trong tương lai sẽ được hiển thị cho cả hai cấu trúc phân cấp.

Bây giờ câu trả lời lý thuyết cho vấn đề này là "chỉ cần thiết kế chính xác ngay lần đầu tiên". Nếu mã được phân tách hoàn hảo, không có bất kỳ sai sót hoặc sai sót nào và được thiết kế với các điểm mở rộng được chuẩn bị cho tất cả các thay đổi yêu cầu có thể trong tương lai, thì bạn sẽ tránh được sự lộn xộn. Nhưng trong thực tế, mọi người đều phạm sai lầm, và không ai có thể dự đoán tương lai một cách hoàn hảo.

Lấy một cái gì đó giống như .NET framework - nó vẫn mang theo tập hợp các lớp bộ sưu tập được thiết kế trước khi thuốc generic được giới thiệu hơn một thập kỷ trước. Đây chắc chắn là một lợi ích cho khả năng tương thích ngược (bạn có thể nâng cấp khung mà không phải viết lại bất cứ thứ gì), nhưng nó cũng làm mờ khung và cung cấp cho các nhà phát triển một tập hợp lớn các tùy chọn trong đó nhiều tùy chọn đã lỗi thời.

Rõ ràng các nhà phát triển của React đã cảm thấy rằng nó không đáng giá về sự phức tạp và phình to mã theo đúng nguyên tắc mở / đóng.

Sự thay thế thực dụng cho mở / đóng được khấu hao có kiểm soát. Thay vì phá vỡ tính tương thích ngược trong một bản phát hành, các thành phần cũ được giữ xung quanh cho một chu kỳ phát hành, nhưng khách hàng được thông báo qua các cảnh báo của trình biên dịch rằng cách tiếp cận cũ sẽ bị xóa trong bản phát hành sau. Điều này cho khách hàng thời gian để sửa đổi mã. Đây dường như là cách tiếp cận của React trong trường hợp này.

(Cách giải thích của tôi về nguyên tắc này dựa trên Nguyên tắc đóng mở của Robert C. Martin)


37
"Về cơ bản, nguyên tắc nói rằng bạn không thể sửa đổi hành vi của một thành phần. Thay vào đó, bạn phải cung cấp một biến thể mới của thành phần với hành vi mong muốn và giữ phiên bản cũ không thay đổi để tương thích ngược." - Tôi không đồng ý với điều này. Nguyên tắc nói rằng bạn nên thiết kế các thành phần theo cách không cần thiết phải thay đổi hành vi của nó vì bạn có thể mở rộng nó để làm những gì bạn muốn. Vấn đề là chúng tôi chưa tìm ra cách để làm điều đó, đặc biệt là với các ngôn ngữ hiện đang được sử dụng rộng rãi. Vấn đề biểu hiện là một phần của trò chơi
Jörg W Mittag

8
Đó là ví dụ. Cả Java và C♯ đều không có giải pháp cho Biểu thức. Haskell và Scala làm, nhưng cơ sở người dùng của họ nhỏ hơn nhiều.
Jörg W Mittag

1
@Giorgio: Trong Haskell, giải pháp là các lớp loại. Trong Scala, giải pháp là ẩn ý và đối tượng. Xin lỗi, hiện tại tôi không có liên kết. Đúng, đa phương thức (thực ra, chúng thậm chí không cần phải là "đa", đúng hơn là bản chất "mở" của các phương pháp của Lisp được yêu cầu) cũng là một giải pháp khả thi. Lưu ý rằng có nhiều cụm từ của vấn đề biểu thức, bởi vì thông thường các bài báo được viết theo cách mà tác giả thêm một hạn chế cho vấn đề biểu hiện dẫn đến thực tế là tất cả các giải pháp hiện tại trở nên không hợp lệ, sau đó cho thấy cách của chính mình
Jörg W Mittag

1
Ngôn ngữ thậm chí có thể giải quyết phiên bản "khó hơn" này. Ví dụ, Wadler ban đầu đặt vấn đề về biểu thức không chỉ là về phần mở rộng mô-đun, mà là phần mở rộng mô-đun an toàn tĩnh . Đa phương thức Lisp thông thường tuy nhiên không an toàn về mặt tĩnh, chúng chỉ an toàn về mặt động lực. Oderky sau đó đã tăng cường điều này hơn nữa bằng cách nói rằng nó phải là mô-đun an toàn tĩnh, tức là an toàn phải được kiểm tra tĩnh mà không cần nhìn vào toàn bộ chương trình, chỉ bằng cách nhìn vào mô-đun mở rộng. Điều này thực sự không thể được thực hiện với các lớp loại Haskell, nhưng nó có thể được thực hiện với Scala. Và trong trận đấu
Jörg W Mittag

2
@Giorgio: Chính xác. Điều làm cho đa phương thức chung Lisp giải quyết EP thực sự không phải là nhiều công văn. Đó là thực tế là các phương pháp được mở. Trong FP điển hình (hoặc lập trình thủ tục), phân biệt kiểu được gắn với các hàm. Trong OO điển hình, các phương thức được gắn với các loại. Các phương thức Lisp thông thường được mở , chúng có thể được thêm vào các lớp sau khi thực tế và trong một mô-đun khác. Đó là tính năng giúp chúng có thể sử dụng để giải quyết EP. Ví dụ: các giao thức của Clojure là một công văn, nhưng cũng giải quyết được EP (miễn là bạn không khăng khăng về an toàn tĩnh).
Jörg W Mittag

20

Tôi sẽ gọi nguyên tắc mở / đóng là một lý tưởng. Giống như tất cả các lý tưởng, nó đưa ra ít xem xét đến thực tế phát triển phần mềm. Cũng giống như tất cả các lý tưởng, không thể thực sự đạt được nó trong thực tế - người ta chỉ cố gắng tiếp cận lý tưởng đó tốt nhất có thể.

Mặt khác của câu chuyện được gọi là Còng tay vàng. Còng tay vàng là những gì bạn nhận được khi bạn nô lệ cho nguyên tắc mở / đóng quá nhiều. Còng tay vàng là những gì xảy ra khi sản phẩm của bạn không bao giờ phá vỡ tính tương thích ngược không thể phát triển do có quá nhiều sai lầm trong quá khứ.

Một ví dụ nổi tiếng về điều này được tìm thấy trong trình quản lý bộ nhớ Windows 95. Là một phần của tiếp thị cho Windows 95, có tuyên bố rằng tất cả các ứng dụng Windows 3.1 sẽ hoạt động trong Windows 95. Microsoft thực sự đã mua giấy phép cho hàng ngàn chương trình để kiểm tra chúng trong Windows 95. Một trong những trường hợp vấn đề là Sim City. Sim City thực sự có một lỗi khiến nó ghi vào bộ nhớ chưa được phân bổ. Trong Windows 3.1, không có trình quản lý bộ nhớ "phù hợp", đây là một lỗi nhỏ. Tuy nhiên, trong Windows 95, trình quản lý bộ nhớ sẽ bắt lỗi này và gây ra lỗi phân đoạn. Giải pháp? Trong Windows 95, nếu tên ứng dụng của bạn là simcity.exe, HĐH sẽ thực sự nới lỏng các ràng buộc của trình quản lý bộ nhớ để ngăn chặn lỗi phân đoạn!

Vấn đề thực sự đằng sau lý tưởng này là các khái niệm ngang hàng của sản phẩm và dịch vụ. Không ai thực sự làm cái này hay cái khác. Mọi thứ xếp hàng ở đâu đó trong vùng xám giữa hai người. Nếu bạn nghĩ từ một cách tiếp cận theo định hướng sản phẩm, âm thanh mở / đóng giống như một lý tưởng tuyệt vời. Sản phẩm của bạn đáng tin cậy. Tuy nhiên, khi nói đến dịch vụ, câu chuyện thay đổi. Thật dễ dàng để chỉ ra rằng với nguyên tắc mở / đóng, số lượng chức năng mà nhóm của bạn phải hỗ trợ phải tiếp cận vô hạn theo cách vô tận, bởi vì bạn không bao giờ có thể dọn sạch chức năng cũ. Điều này có nghĩa là nhóm phát triển của bạn phải hỗ trợ nhiều mã hơn mỗi năm. Cuối cùng bạn đạt đến một điểm phá vỡ.

Hầu hết các phần mềm ngày nay, đặc biệt là nguồn mở, tuân theo một phiên bản thoải mái chung của nguyên tắc mở / đóng. Rất phổ biến để xem mở / đóng theo sau một cách mù quáng cho các bản phát hành nhỏ, nhưng bị bỏ qua cho các bản phát hành chính. Ví dụ: Python 2.7 chứa nhiều "lựa chọn xấu" từ Python 2.0 và 2.1 ngày, nhưng Python 3.0 đã quét sạch tất cả chúng. (Ngoài ra, sự chuyển đổi từ Windows 95 codebase đến codebase Windows NT khi họ phát hành Windows 2000 đã phá vỡ tất cả các loại của sự vật, nhưng nó đã có nghĩa là chúng ta không bao giờ phải đối phó với một người quản lý bộ nhớ kiểm tra tên ứng dụng để quyết định hành vi!)


Đó là một câu chuyện khá hay về SimCity. Bạn có một nguồn?
BJ Myers

5
@BJMyer Đó là một câu chuyện cũ, Joel Spoleky đề cập đến nó ở gần cuối bài viết này . Ban đầu tôi đọc nó như một phần của cuốn sách phát triển trò chơi điện tử nhiều năm trước.
Cort Ammon

1
@BJMyer: Tôi khá chắc chắn rằng họ có "hack" tương thích tương tự cho hàng tá ứng dụng phổ biến.
Doc Brown

3
@BJMyer có rất nhiều thứ như thế này, nếu bạn muốn đọc tốt hãy truy cập blog The Old New Thing của Raymond Chen , duyệt qua thẻ Lịch sử hoặc tìm kiếm "khả năng tương thích". Có một hồi ức về rất nhiều câu chuyện, bao gồm một cái gì đó rõ ràng gần với trường hợp SimCity đã nói ở trên - Addentum: Chen không muốn gọi tên để đổ lỗi.
Theraot

2
Rất ít thứ bị phá vỡ ngay cả trong quá trình chuyển đổi 95-> NT. SimCity gốc cho Windows vẫn hoạt động tốt trên Windows 10 (32-bit). Ngay cả các trò chơi DOS vẫn hoạt động hoàn hảo, miễn là bạn vô hiệu hóa âm thanh hoặc sử dụng một cái gì đó như VDMSound để cho phép hệ thống con giao diện điều khiển xử lý âm thanh đúng cách. Microsoft rất coi trọng khả năng tương thích ngược và họ cũng không sử dụng bất kỳ phím tắt "hãy đặt nó vào máy ảo". Đôi khi nó cần một cách giải quyết, nhưng điều đó vẫn khá ấn tượng, đặc biệt là về mặt tương đối.
Luaan

11

Câu trả lời của Doc Brown gần nhất với chính xác, các câu trả lời khác minh họa cho sự hiểu lầm về Nguyên tắc Đóng mở.

Để nói rõ sự hiểu lầm, dường như có một niềm tin rằng OCP có nghĩa là bạn không nên thực hiện các thay đổi không tương thích ngược (hoặc thậm chí bất kỳ thay đổi hoặc điều gì đó dọc theo các dòng này.) OCP là về thiết kế các thành phần để bạn không cần phải thay đổi chúng để mở rộng chức năng của chúng, bất kể những thay đổi đó có tương thích ngược hay không. Có nhiều lý do khác ngoài việc thêm chức năng mà bạn có thể thay đổi thành phần cho dù chúng có tương thích ngược (ví dụ: tái cấu trúc hoặc tối ưu hóa) hoặc không tương thích ngược (ví dụ: không dùng và loại bỏ chức năng). Rằng bạn có thể thực hiện những thay đổi này không có nghĩa là thành phần của bạn đã vi phạm OCP (và chắc chắn không có nghĩa là bạn đang vi phạm OCP).

Thực sự, đó không phải là về mã nguồn. Một tuyên bố trừu tượng và phù hợp hơn của OCP là: "một thành phần sẽ cho phép mở rộng mà không cần vi phạm các ranh giới trừu tượng của nó". Tôi sẽ đi xa hơn và nói một biểu hiện hiện đại hơn là: "một thành phần nên thực thi các ranh giới trừu tượng của nó nhưng cho phép mở rộng". Ngay cả trong bài viết về OCP của Bob Martin trong khi ông "mô tả" "đóng để sửa đổi" là "mã nguồn là bất khả xâm phạm", sau đó ông bắt đầu nói về việc đóng gói không liên quan gì đến việc sửa đổi mã nguồn và mọi thứ phải làm với sự trừu tượng hóa ranh giới.

Vì vậy, tiền đề bị lỗi trong câu hỏi là OCP (dự định là) một hướng dẫn về sự phát triển của một cơ sở mã. OCP thường được khẩu hiệu là "một thành phần nên được mở cho các tiện ích mở rộng và đóng cho sửa đổi bởi người tiêu dùng". Về cơ bản, nếu người tiêu dùng của một thành phần muốn thêm chức năng cho thành phần đó thì họ có thể mở rộng thành phần cũ thành một thành phần mới với chức năng bổ sung, nhưng họ không thể thay đổi thành phần cũ.

OCP không nói gì về người tạo ra một thành phần thay đổi hoặc loại bỏ chức năng. OCP không ủng hộ việc duy trì khả năng tương thích lỗi mãi mãi. Bạn, với tư cách là người tạo, không vi phạm OCP bằng cách thay đổi hoặc thậm chí xóa một thành phần. Bạn, hay đúng hơn là các thành phần bạn đã viết, đang vi phạm OCP nếu cách duy nhất người tiêu dùng có thể thêm chức năng cho các thành phần của bạn là bằng cách thay đổi nó, ví dụ như bằng cách vá khỉhoặc có quyền truy cập vào mã nguồn và biên dịch lại. Trong nhiều trường hợp, cả hai đều không phải là tùy chọn cho người tiêu dùng, điều đó có nghĩa là nếu thành phần của bạn không "mở rộng" thì họ không gặp may. Họ chỉ đơn giản là không thể sử dụng thành phần của bạn cho nhu cầu của họ. OCP lập luận rằng không đưa người tiêu dùng của thư viện của bạn vào vị trí này, ít nhất là đối với một số loại "tiện ích mở rộng" có thể nhận dạng được. Ngay cả khi sửa đổi có thể được thực hiện đối với mã nguồn hoặc thậm chí là bản sao chính của mã nguồn, tốt nhất bạn nên "giả vờ" rằng bạn không thể sửa đổi nó vì có nhiều hậu quả tiêu cực tiềm ẩn khi làm như vậy.

Vì vậy, để trả lời câu hỏi của bạn: Không, đây không phải là vi phạm OCP. Không có thay đổi nào mà tác giả thực hiện có thể là vi phạm OCP vì OCP không phải là tỷ lệ thay đổi. Tuy nhiên, những thay đổi có thể tạo ra sự vi phạm OCP và chúng có thể được thúc đẩy bởi những thất bại của OCP trong các phiên bản trước của codebase. OCP là một thuộc tính của một đoạn mã cụ thể, không phải là lịch sử tiến hóa của một cơ sở mã.

Ngược lại, khả năng tương thích ngược là một thuộc tính của sự thay đổi mã. Thật vô nghĩa khi nói một số đoạn mã là hoặc không tương thích ngược. Nó chỉ có ý nghĩa để nói về khả năng tương thích ngược của một số mã đối với một số mã cũ hơn. Do đó, không bao giờ có ý nghĩa để nói về lần cắt đầu tiên của một số mã có tương thích ngược hay không. Lần cắt mã đầu tiên có thể thỏa mãn hoặc không thỏa mãn OCP và nói chung, chúng tôi có thể xác định liệu một số mã có thỏa mãn OCP hay không mà không đề cập đến bất kỳ phiên bản lịch sử nào của mã.

Đối với câu hỏi cuối cùng của bạn, có thể nói là lạc đề đối với StackExchange nói chung là chủ yếu dựa trên quan điểm, nhưng thiếu sót trong công nghệ và đặc biệt là JavaScript, trong vài năm qua, hiện tượng mà bạn mô tả được gọi là mệt mỏi JavaScript . (Hãy thoải mái google để tìm một loạt các bài viết khác, một số châm biếm, nói về điều này từ nhiều quan điểm.)


3
"Bạn, với tư cách là người tạo, không vi phạm OCP bằng cách thay đổi hoặc thậm chí xóa một thành phần." - bạn có thể cung cấp một tài liệu tham khảo cho điều này? Không có định nghĩa nào về nguyên tắc mà tôi đã thấy nói rằng "người tạo ra" (bất kể điều đó có nghĩa là gì) được miễn trừ khỏi nguyên tắc này. Loại bỏ một thành phần được công bố rõ ràng là một thay đổi đột phá.
JacquesB

1
@JacquesB Mọi người và thậm chí thay đổi mã không vi phạm OCP, các thành phần (tức là các đoạn mã thực tế) làm. (Và, để hoàn toàn rõ ràng, điều đó có nghĩa là thành phần không sống theo chính OCP, không phải nó vi phạm OCP của một số thành phần khác.) Toàn bộ câu trả lời của tôi là OCP không nói về thay đổi mã , phá vỡ hoặc cách khác. Một thành phần được mở để mở rộng và đóng để sửa đổi, hoặc không, giống như một phương thức có thể privatehoặc không. Nếu một tác giả thực hiện một privatephương pháp publicsau này, điều đó không có nghĩa là họ đã vi phạm quyền kiểm soát truy cập, (1/2)
Derek Elkins

2
... cũng không có nghĩa là phương pháp này không thực sự privatetrước đây. "Loại bỏ một thành phần được công bố rõ ràng là một thay đổi đột phá", là một trình tự không theo thứ tự. Các thành phần của phiên bản mới đáp ứng OCP hoặc chúng không, bạn không cần lịch sử của cơ sở mã để xác định điều này. Theo logic của bạn, tôi không bao giờ có thể viết mã thỏa mãn OCP. Bạn đang tương thích ngược, một thuộc tính thay đổi mã, với OCP, thuộc tính của mã. Nhận xét của bạn có ý nghĩa nhiều như việc nói quicksort không tương thích ngược. (2/2)
Derek Elkins

3
@JacquesB Trước tiên, xin lưu ý rằng đây là nói về một mô-đun tuân thủ OCP. OCP là lời khuyên về cách viết mô-đun sao cho hạn chế rằng mã nguồn không thể thay đổi, tuy nhiên mô-đun có thể được mở rộng. Trước đó, trong bài báo, ông nói về việc thiết kế các mô-đun không bao giờ thay đổi, không phải về việc thực hiện quy trình quản lý thay đổi không bao giờ cho phép các mô-đun thay đổi. Đề cập đến chỉnh sửa câu trả lời của bạn, bạn không "phá vỡ OCP" bằng cách sửa đổi mã của mô-đun. Thay vào đó, nếu "mở rộng" mô-đun yêu cầu bạn sửa đổi mã nguồn, (1/3)
Derek Elkins

2
"OCP là một thuộc tính của một đoạn mã cụ thể, không phải là lịch sử tiến hóa của một cơ sở mã." - Xuất sắc!
Doc Brown
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.