Là quá tải một ví dụ về nguyên tắc mở / đóng?


12

Wikipedia nói

"các thực thể phần mềm (lớp, mô-đun, chức năng, v.v.) nên được mở để mở rộng, nhưng đóng để sửa đổi"

Từ hàm lọt vào mắt tôi và bây giờ tôi tự hỏi liệu chúng ta có thể cho rằng việc tạo quá tải cho một phương thức có thể được coi là một ví dụ về nguyên tắc Mở / đóng hay không?

Hãy để tôi giải thích một ví dụ. Hãy xem xét rằng bạn có một phương thức trong lớp dịch vụ của mình, được sử dụng ở gần 1000 địa điểm. Phương thức được userId và xác định xem người dùng có phải là quản trị viên hay không:

bool IsAdmin(userId)

Bây giờ hãy xem xét rằng ở đâu đó cần xác định xem người dùng có phải là quản trị viên hay không, dựa trên tên người dùng, không phải userId. Nếu chúng tôi thay đổi chữ ký của phương thức đã đề cập ở trên, thì chúng tôi đã bị hỏng mã ở 1000 vị trí (các chức năng nên được đóng lại để sửa đổi). Do đó, chúng ta có thể tạo quá tải để lấy tên người dùng, tìm userId dựa trên tên người dùng và phương thức ban đầu:

public bool IsAdmin(string username)
{
    int userId = UserManager.GetUser(username).Id;
    return IsAdmin(userId);
}

Bằng cách này, chúng tôi đã mở rộng chức năng của mình thông qua việc tạo quá tải cho nó (các chức năng nên được mở để mở rộng).

Đây có phải là một ví dụ nguyên tắc mở / đóng?

Câu trả lời:


5

Cá nhân tôi sẽ giải thích các tuyên bố wiki như vậy:

  • đối với một lớp: thoải mái kế thừa lớp và ghi đè hoặc mở rộng chức năng của nó, nhưng đừng hack lớp gốc do đó thay đổi những gì nó làm.
  • đối với mô-đun (có thể là thư viện chẳng hạn): thoải mái viết mô-đun / thư viện mới bao bọc bản gốc, hợp nhất các chức năng thành các phiên bản dễ sử dụng hơn hoặc mở rộng bản gốc với các tính năng bổ sung, nhưng không thay đổi mô-đun gốc.
  • đối với một hàm (tức là một hàm tĩnh, không phải là một phương thức lớp): ví dụ của bạn là hợp lý với tôi; sử dụng lại hàm IsAdmin (int) ban đầu bên trong IsAdmin (chuỗi) mới. bản gốc không thay đổi, func mới mở rộng chức năng của nó.

Tuy nhiên, cú đánh vào chân là nếu mã của bạn đang sử dụng lớp 'cUserInfo' trong đó IsAdmin (int) cư trú, về cơ bản bạn đang phá vỡ quy tắc và thay đổi lớp. đáng buồn là quy tắc sẽ chỉ được giữ nếu bạn tạo một lớp cUserWithNameInfo: lớp cUserInfo công khai và đặt ghi đè IsAdmin (chuỗi) của bạn lên đó. Nếu tôi sở hữu cơ sở mã, tôi sẽ không bao giờ tuân thủ quy tắc. Tôi muốn nói bollocks và chỉ thực hiện thay đổi mà bạn đề xuất.


3

Trước hết, ví dụ của bạn không may bị chiếm đoạt. Bạn sẽ không bao giờ làm điều đó trong thế giới thực, bởi vì nó gây ra một cú đúp không cần thiết. Hoặc, tệ hơn, bởi vì userid và tên người dùng có thể trở thành cùng một loại tại một số điểm. Thay vì

public bool IsAdmin(int userid)
{
    User user = UserManager.GetUser(userid);
    return user.IsAdmin();
}

public bool IsAdmin(string username)
{
    int userId = UserManager.GetUser(username).Id;
    return IsAdmin(userId);
}

Bạn thực sự nên mở rộng lớp học đó.

public bool IsAdmin(int userid)
{
    User user = UserManager.GetUserById(userid);
    return user.IsAdmin();
}

public bool IsUsernameAdmin(string username)
{
    User userId = UserManager.GetUserByName(username);
    return user.IsAdmin();
}

Bạn có thể tái cấu trúc thêm và thay đổi tên phương thức đầu tiên thành IsUserIdAdmin, để thống nhất, nhưng không cần thiết. Vấn đề là, khi thêm phương thức mới và đảm bảo không có mã cuộc gọi nào bị hỏng, bạn đã thấy lớp của mình có thể mở rộng. Hay nói cách khác, mở để mở rộng .

Và đây là điều với nguyên tắc đóng mở. Bạn không bao giờ thực sự biết mã của mình tuân thủ tốt như thế nào cho đến khi bạn cố gắng mở rộng một phần của mã đó và thấy mình phải sửa đổi thay vào đó (với rủi ro gia tăng đi kèm với điều đó). Nhưng, với kinh nghiệm, bạn học cách dự đoán.

Như chú Bob nói (nhấn mạnh của tôi):

Vì việc đóng cửa không thể hoàn thành, nó phải mang tính chiến lược. Đó là, nhà thiết kế phải chọn các loại thay đổi để đóng thiết kế của mình. Điều này cần một lượng nhất định của kinh nghiệm bắt nguồn từ kinh nghiệm . Nhà thiết kế có kinh nghiệm biết rõ người dùng và ngành công nghiệp đủ để đánh giá xác suất của các loại thay đổi khác nhau. Sau đó, ông đảm bảo rằng nguyên tắc đóng mở được viện dẫn cho những thay đổi có thể xảy ra nhất.


Cảm ơn lời giải thích tốt của bạn. Nhưng có 1000 chuyến khứ hồi hoặc một chuyến khứ hồi không phải là điểm chính ở đây. Vì vậy, hãy quên đi hiệu suất tại thời điểm này. Tuy nhiên, những gì tôi đã làm cũng là mở rộng lớp, bởi vì tôi đã thêm một phương thức khác vào nó, mặc dù có cùng tên, nhưng với các tham số đầu vào khác nhau. Nhưng tôi thực sự đánh giá cao câu nói của bạn từ chú Bob . +1. ;)
Saeed Neamati

@SaeedNeamati: Điểm tôi đang đưa ra là không thành vấn đề nếu bạn thêm phương thức sử dụng phương thức hiện có hoặc thêm phương thức hoàn toàn độc lập, theo cách mà lớp của bạn mở để mở rộng. Nhưng, nếu bạn phải sửa đổi giao diện phương thức hiện có để đạt được điều đó, thì bạn không bị đóng để sửa đổi.
pdr

2

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ở ra cho 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 cho Modi ”cation. 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ó.
  1. Bằng cách quá tải phương thức của bạn, bạn đang mở rộng chức năng của mô-đun hiện có, do đó đáp ứng nhu cầu mới của ứng dụng của bạn

  2. Bạn không thực hiện bất kỳ thay đổi nào đối với một phương thức hiện có, vì vậy bạn không vi phạm quy tắc thứ hai.

Tôi rất muốn nghe những gì người khác nói về việc không cho phép thay đổi phương pháp ban đầu. Có, bạn có thể đặt các sửa đổi truy cập thích hợp và thực thi đóng gói, nhưng những gì khác có thể được thực hiện?

Tham chiếu: http://www.objectmentor.com/resource/articles/ocp.pdf


1

Nguyên tắc đóng mở là một mục tiêu, một trường hợp lý tưởng, không phải lúc nào cũng là thực tế. Đặc biệt khi tìm cách cấu trúc lại mã cũ, bước đầu tiên thường là sửa đổi nặng nề để biến OCP thành khả năng. Nguyên tắc cơ bản là mã làm việc đã hoạt động và việc thay đổi có thể gây ra lỗi. Do đó, kịch bản tốt nhất là không thay đổi mã hiện có, chỉ để thêm mã mới.

Nhưng hãy nói rằng bạn có một chức năng được gọi là BigContrivedMethod(int1, int2, string1). BigContrivedMethodthực hiện ba điều: thing1, thing2 và thing3. Tại thời điểm này, việc tái sử dụng BCM có lẽ khó khăn, vì nó làm quá nhiều. Refactoring nó (nếu có thể) vào ContrivedFunction1(int), ContrivedFunction2(int)ContrivedFunction3(string) cung cấp cho bạn ba phương thức nhỏ hơn, tập trung hơn mà bạn có thể kết hợp dễ dàng hơn.

Và đó là chìa khóa của OCP liên quan đến các phương thức / chức năng: thành phần. Bạn "mở rộng" các chức năng bằng cách gọi chúng từ các chức năng khác.

Hãy nhớ rằng OCP là một phần của 5 nguyên tắc khác, hướng dẫn RẮN. Đó là đầu tiên là chính, Trách nhiệm duy nhất. Nếu mọi thứ trong cơ sở mã của bạn chỉ thực hiện một điều cụ thể mà nó phải làm, bạn sẽ không bao giờ cần phải sửa đổi mã. Bạn chỉ cần thêm mã mới hoặc kết hợp mã cũ với nhau theo những cách mới. Vì mã thực tế hiếm khi đáp ứng hướng dẫn đó, bạn thường phải sửa đổi nó để có SRP trước khi bạn có thể nhận được OCP.


Tôi nghĩ bạn nói về Hiệu trưởng Trách nhiệm Đơn lẻ ở đây, không phải OCP.
Saeed Neamati

1
Tôi đang nói rằng bạn thực sự không thể có OCP mà không có SRP. Mã quá nhiều không thể được gia hạn, nó không thể được sử dụng lại. RẮN hầu như được viết theo thứ tự quan trọng, với mỗi nguyên tắc dựa trên những nguyên tắc trước khi nó khả thi.
CodexArcanum

Câu trả lời này nên được nâng cao hơn.
Ashish Gupta

0

Bạn đang tuân theo nguyên tắc đóng mở nếu bạn đang thay đổi hành vi của chương trình bằng cách viết mã mới thay vì thay đổi mã cũ.

Vì việc viết mã mở cho mọi thay đổi có thể xảy ra là khá tốt (và bạn không muốn vì bạn bị tê liệt phân tích), bạn viết mã phản ứng với tất cả các hành vi khác nhau mà bạn hiện đang thực hiện bằng cách mở rộng thay vì sửa đổi.

Khi bạn gặp phải điều gì đó cần thay đổi, thay vì chỉ thay đổi điều gì đó để cho phép hành vi mới, bạn tìm ra điểm thay đổi là gì, cơ cấu lại chương trình của bạn mà không thay đổi hành vi của nó để bạn có thể thay đổi hành vi đó bằng cách viết mã MỚI .

Vì vậy, làm thế nào điều này áp dụng cho trường hợp của bạn:

Nếu bạn đang thêm các chức năng mới vào lớp thì lớp của bạn không mở / đóng mà là các máy khách của chức năng bạn đang quá tải.

Nếu bạn chỉ đơn giản là thêm các hàm mới hoạt động TRÊN lớp của bạn, thì cả hàm và ứng dụng khách của hàm của bạn đều được mở / đó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.