Ist Überlastung ein Beispiel für das Open / Closed-Prinzip?

12

Wikipedia sagt

"Software-Entitäten (Klassen, Module, Funktionen usw.) sollten zur Erweiterung geöffnet, zur Änderung jedoch geschlossen sein"

Das Wort Funktionen ist mir aufgefallen und ich frage mich jetzt, ob wir davon ausgehen können, dass das Erzeugen einer Überladung für eine Methode als Beispiel für das Open / Closed-Prinzip angesehen werden kann oder nicht.

Lassen Sie mich ein Beispiel erklären. Bedenken Sie, dass Ihre Service-Schicht eine Methode enthält, die an fast 1000 Stellen verwendet wird. Die Methode ruft userId ab und bestimmt, ob der Benutzer admin ist oder nicht:

bool IsAdmin(userId)

Bedenken Sie nun, dass es irgendwo erforderlich ist, zu bestimmen, ob der Benutzer admin ist oder nicht, basierend auf dem Benutzernamen und nicht der Benutzer-ID. Wenn wir die Signatur der oben genannten Methode ändern, ist der Code an 1000 Stellen fehlerhaft (Funktionen sollten für Änderungen gesperrt sein). Auf diese Weise können wir eine Überladung erstellen, um den Benutzernamen abzurufen, die Benutzer-ID basierend auf dem Benutzernamen und der ursprünglichen Methode zu ermitteln:

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

Auf diese Weise haben wir unsere Funktion erweitert, indem wir eine Überladung dafür erstellt haben (Funktionen sollten für Erweiterungen offen sein).

Ist es ein Beispiel für ein offenes / geschlossenes Prinzip?

Saeed Neamati
quelle

Antworten:

5

Ich würde die Wiki-Aussage persönlich so interpretieren:

  • für eine Klasse: fühlen Sie sich frei , die Klasse und überschreiben oder erben erweitern die Funktionalität, aber die ursprüngliche Klasse dadurch nicht hacken zu ändern , was es tut.
  • für ein Modul (vielleicht eine Bibliothek zum Beispiel): fühlen sich frei , eine neues Modul / Bibliothek zu schreiben , die die ursprünglichen, verschmilzt Funktionen in einfacher hüllen Versionen zu verwenden oder sie das Original mit zusätzlichen Funktionen, aber nicht ändert das ursprüngliche Modul.
  • für eine Funktion (dh eine statische Funktion, keine Klassenmethode): Ihr Beispiel ist für mich vernünftig; Verwenden Sie die ursprüngliche IsAdmin (int) -Funktion in der neuen IsAdmin (Zeichenfolge) erneut. das original ändert sich nicht, die neue func erweitert ihre funktionalität.

Der Schuss in den Fuß ist jedoch, dass, wenn Ihr Code die 'cUserInfo'-Klasse verwendet, in der sich IsAdmin (int) befindet, Sie im Wesentlichen die Regel brechen und die Klasse ändern. Die Regel würde leider nur beibehalten, wenn Sie eine neue Klasse cUserWithNameInfo: public cUserInfo-Klasse erstellen und Ihre IsAdmin (Zeichenfolge) dort überschreiben. Wenn ich die Codebasis besäße, würde ich die Regel niemals befolgen. Ich würde Blödsinn sagen und nur die Änderung vornehmen, die Sie vorschlagen.

Sassafras_wot
quelle
3

Zunächst einmal ist Ihr Beispiel leider erfunden. Das würde man in der realen Welt niemals tun, weil es einen unnötigen Double-Get verursacht. Oder noch schlimmer, weil Benutzer-ID und Benutzername irgendwann den gleichen Typ annehmen können. Eher, als

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);
}

Sie sollten diese Klasse wirklich erweitern .

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();
}

Sie können den ersten Methodennamen aus Konsistenzgründen weiter umgestalten und in IsUserIdAdmin ändern, dies ist jedoch nicht erforderlich. Der Punkt ist, dass Sie beim Hinzufügen der neuen Methode und beim Gewährleisten, dass kein Aufrufcode unterbrochen wird, festgestellt haben, dass Ihre Klasse erweiterbar ist. Oder mit anderen Worten, offen für Erweiterungen .

Und hier ist die Sache mit dem Open-Closed-Prinzip. Sie wissen erst dann wirklich , wie gut Ihr Code konform ist, wenn Sie versuchen, einen Teil davon zu erweitern und stattdessen Änderungen vornehmen müssen (mit dem damit verbundenen erhöhten Risiko). Aber mit der Erfahrung lernst du vorherzusagen.

Wie Onkel Bob sagt (Hervorhebung von mir):

Da die Schließung nicht vollständig sein kann, muss sie strategisch sein. Das heißt, der Designer muss die Art der Änderungen auswählen, gegen die er sein Design schließen möchte. Dies erfordert ein gewisses Maß an Voraussicht, das sich aus der Erfahrung ergibt . Der erfahrene Designer kennt die Benutzer und die Branche gut genug, um die Wahrscheinlichkeit verschiedener Arten von Änderungen einzuschätzen. Er stellt dann sicher, dass das Open-Closed-Prinzip für die wahrscheinlichsten Änderungen angewendet wird.

pdr
quelle
Danke für deine gute Erklärung. Aber 1000 Hin- und Rückfahrten oder eine Hin- und Rückfahrt zu haben, ist hier nicht der wichtigste Punkt. Vergessen wir also die Performance im Moment. Was ich jedoch getan habe, war auch, die Klasse zu erweitern, da ich ihr eine andere Methode hinzugefügt habe, allerdings mit demselben Namen, aber mit unterschiedlichen Eingabeparametern. Aber ich habe Ihr Zitat von Onkel Bob sehr geschätzt . +1. ;)
Saeed Neamati
@ SaeedNeamati: Der Punkt, den ich angesprochen habe, ist, dass es keine Rolle spielt, ob Sie eine Methode hinzufügen, die eine vorhandene Methode verwendet, oder eine Methode, die völlig unabhängig ist, so oder so, wie Ihre Klasse für Erweiterungen offen war. Wenn Sie jedoch die vorhandene Methodenschnittstelle ändern mussten, um dies zu erreichen, sind Sie nicht für Änderungen gesperrt.
pdr
2

Module, die dem Open-Closed-Prinzip entsprechen, weisen zwei primäre Attribute auf.

  1. Sie sind "Open For Extension". Dies bedeutet, dass das Verhalten des Moduls erweitert werden kann. Dass wir das Modul auf neue und unterschiedliche Weise dazu bringen können, dass sich die Anforderungen der Anwendung ändern oder die Anforderungen neuer Anwendungen erfüllt werden.
  2. Sie sind „wegen Änderungen geschlossen“. Der Quellcode eines solchen Moduls ist unantastbar. Niemand darf Änderungen am Quellcode vornehmen.
  1. Indem Sie Ihre Methode überladen, erweitern Sie die Funktionalität des vorhandenen Moduls und erfüllen so die neuen Anforderungen Ihrer Anwendung

  2. Sie nehmen keine Änderungen an einer vorhandenen Methode vor, verstoßen also nicht gegen die zweite Regel.

Es würde mich interessieren, was andere dazu sagen, die ursprüngliche Methode nicht ändern zu dürfen. Ja, Sie können geeignete Zugriffsmodifikatoren festlegen und die Kapselung erzwingen. Was kann jedoch noch getan werden?

Ref: http://www.objectmentor.com/resources/articles/ocp.pdf

CodeART
quelle
1

Das Open-Closed-Prinzip ist ein Ziel, ein Idealfall, nicht immer eine Realität. Insbesondere bei der Überarbeitung von altem Code ist der erste Schritt oft eine umfangreiche Änderung, um das OCP zu einer Möglichkeit zu machen. Die Wurzel des Prinzips ist, dass der Arbeitscode bereits funktioniert und das Ändern möglicherweise Fehler verursacht. Daher ist es das beste Szenario, vorhandenen Code nicht zu ändern, sondern nur neuen Code hinzuzufügen.

Angenommen, Sie hatten eine Funktion namens BigContrivedMethod(int1, int2, string1). BigContrivedMethodmacht drei Dinge: Ding1, Ding2 und Ding3. Zu diesem Zeitpunkt ist die Wiederverwendung von BCM wahrscheinlich schwierig, da dies zu viel bewirkt. Refactoring es (wenn möglich) in ContrivedFunction1(int), ContrivedFunction2(int)und ContrivedFunction3(string)gibt Ihnen drei kleinere, fokussiertere Methoden, die Sie einfacher kombinieren können.

Und das ist der Schlüssel zu OCP in Bezug auf Methoden / Funktionen: Zusammensetzung. Sie "erweitern" Funktionen, indem Sie sie von anderen Funktionen aufrufen.

Denken Sie daran, dass OCP Teil von 5 anderen Prinzipien ist, den SOLID-Richtlinien. Das erste ist der Schlüssel, Single Responsibility. Wenn alles in Ihrer Codebasis nur das tat, was es tun musste, müssten Sie niemals Code ändern. Sie müssen nur neuen Code hinzufügen oder alten Code auf neue Weise miteinander kombinieren. Da realer Code diese Richtlinie selten erfüllt, müssen Sie ihn häufig ändern, um SRP zu erhalten, bevor Sie OCP erhalten können.

CodexArcanum
quelle
Ich denke, Sie sprechen hier über Single Responsibility Principal, nicht über OCP.
Saeed Neamati
1
Ich sage, dass Sie OCP ohne SRP nicht realistisch haben können. Zu viel Code kann nicht erweitert werden, er kann nicht wiederverwendet werden. SOLID ist fast in der Reihenfolge der Wichtigkeit geschrieben, wobei sich jedes Prinzip auf die Prinzipien verlässt, die vor ihm liegen, um machbar zu sein.
CodexArcanum
Diese Antwort sollte mehr positiv bewertet werden.
Ashish Gupta
0

Sie folgen dem Open-Closed-Prinzip, wenn Sie das Verhalten Ihres Programms ändern, indem Sie neuen Code schreiben, anstatt alten Code zu ändern.

Da es so gut wie unmöglich ist, Code zu schreiben, der für jede mögliche Änderung offen ist (und Sie nicht möchten, weil Sie eine Analyse-Lähmung eingeben), schreiben Sie Code, der auf alle unterschiedlichen Verhaltensweisen reagiert, an denen Sie gerade arbeiten, und nicht auf Änderungen.

Wenn Sie auf etwas stoßen, das Sie ändern müssen, anstatt einfach etwas zu ändern, um das neue Verhalten zuzulassen, finden Sie heraus, wo die Änderung stattfindet, und strukturieren Sie Ihr Programm neu, ohne das Verhalten zu ändern, sodass Sie dieses Verhalten dann durch Schreiben von NEUEM Code ändern können .

So trifft dies auf Ihren Fall zu:

Wenn Sie Ihrer Klasse neue Funktionen hinzufügen, ist Ihre Klasse nicht geöffnet / geschlossen, sondern die Clients der Funktion, die Sie überladen, sind.

Wenn Sie einfach neue Funktionen hinzufügen, die in Ihrer Klasse funktionieren, sind sowohl diese als auch die Clients Ihrer Funktion offen / geschlossen.

Edward Strange
quelle