Klären Sie das offene / geschlossene Prinzip

25

Wie ich es erklärt habe, besagt das Open / Closed-Prinzip, dass einmal geschriebener Code nicht geändert werden sollte (abgesehen von Fehlerkorrekturen). Aber wenn sich meine Geschäftsregeln ändern, sollte ich dann nicht den Code ändern, der diese Änderungen implementiert? Ich vermute, ich verstehe etwas nicht über das Prinzip, weil es für mich keinen Sinn ergibt.

Winston Ewert
quelle

Antworten:

22

Dies ist wahrscheinlich das am schwierigsten zu erklärende solide Prinzip. Lass es mich versuchen. Stellen Sie sich vor, Sie haben eine Invoice-Klasse geschrieben, die perfekt funktioniert und keine Fehler aufweist. Es erstellt ein PDF einer Rechnung.

Dann sagt jemand, sie wollen eine HTML-Rechnung mit Links. Sie ändern keinen Code in Invoice, um diese Anforderung zu erfüllen. Stattdessen erstellen Sie eine andere Klasse, HTMLInvoice, die das tut, was sie jetzt wollen. Sie nutzen die Vererbung, damit Sie in HTMLInvoice nicht viel doppelten Code schreiben müssen.

Alter Code, der die alte Rechnung verwendet hat, ist nicht beschädigt oder in irgendeiner Weise betroffen. Der neue Code kann HTMLInvoice verwenden. (Wenn Sie auch Liskov Substitutability , das L von solid, ausführen, können Sie vorhandenen Code, der Rechnungsinstanzen erwartet, mit HTMLInvoice-Instanzen versehen.) Jeder lebt von nun an glücklich.

Die Rechnung kann nicht geändert oder erweitert werden. Und Sie müssen die Rechnung im Voraus richtig schreiben, damit dies funktioniert, übrigens.

Kate Gregory
quelle
1
Wenn sich die Geschäftsregeln ändern, kann nicht davon ausgegangen werden, dass das System fehlerfrei funktioniert, sodass das Open / Close-Prinzip nicht gilt.
JeffO
Ich habe selbst mit dieser Regel gekämpft und was Kate vorschlägt, ist im Grunde das, was ich daraus geschlossen habe. In der Wirtschaft versuchen Sie, kleinere, flexiblere Klassen zu programmieren, damit Sie sie gut wiederverwenden können. Wenn Sie sie richtig funktionieren lassen, möchten Sie sie nicht ändern. Aber sie sind selten vollständig "fertig", so dass einige Änderungen unvermeidlich sind. Beachten Sie, dass der Text "Modul" sagt, nicht Objekt. Ich wende das OCP oft erfolgreich auf Funktionsebene an, mit engen Funktionen, die eine Sache perfekt machen und nie geändert werden müssen.
CodexArcanum
1
@ Jeff O Ich unterscheide zwischen dem Beheben eines Fehlers (bei dem der Code die ursprüngliche Anforderung nicht erfüllte und niemand ihn so will, wie er ist) und dem Ändern der Anforderungen. Wenn ich PDFs benötige und der Code PDFs erstellt, gibt es keinen Fehler, obwohl ich jetzt HTML möchte (und normalerweise wollen die Leute auch HTML, nicht statt.)
Kate Gregory
2
@ Winston - das habe ich gemeint, als ich sagte, du musst die Rechnung richtig schreiben. Im Idealfall gab es bereits eine ziemlich abstrakte Rechnung, und Sie haben PDFInvoice geerbt, das erwartend. Wenn nicht, müssen Sie die Regel einmal brechen, um sich darauf einzustellen, sie in Zukunft nicht mehr zu brechen. In jedem Fall ist die Vorhersage zukünftiger Änderungen ein großer Teil all dessen - und das ist der Teil des Rezepts, der darin besteht, einen Elefanten zu fangen und zu zerschneiden.
Kate Gregory
1
Ihre letzte Aussage ist die wichtigste. Offen / Geschlossen ist ein Designideal - und man muss das Design von Anfang an haben, um es zu erreichen. Auch muss nicht alles offen / geschlossen sein, aber es ist ein mächtiges Werkzeug, wenn Sie dorthin gelangen können.
Alex Feinman
13

Haben Sie den Artikel The Open-Closed Principle von Onkel Bobs Freunden bei ObjectMentor gelesen? Ich denke, es ist eine der besseren Erklärungen da draußen.

Es gibt viele Heuristiken, die mit objektorientiertem Design verbunden sind. Beispiel: "Alle Mitgliedsvariablen sollten privat sein", "Globale Variablen sollten vermieden werden" oder "Die Verwendung der RTTI (Runtime Type Identification) ist gefährlich". Woher stammen diese Heuristiken? Was macht sie wahr? Sind sie immer wahr Diese Kolumne untersucht das Designprinzip, das diesen Heuristiken zugrunde liegt - das Open-Closed-Prinzip.

Wie Ivar Jacobson sagte: „Alle Systeme ändern sich während ihres Lebenszyklus. Dies muss berücksichtigt werden, wenn Systeme entwickelt werden, die voraussichtlich länger als die erste Version halten. “Wie können wir Designs erstellen, die angesichts von Änderungen stabil sind und die länger als die erste Version halten werden? Bertrand Meyer gab uns bereits 1988 Orientierung, als er das mittlerweile berühmte Open-Closed-Prinzip prägte. Um ihn zu umschreiben:

SOFTWARE-EINHEITEN (KLASSEN, MODULE, FUNKTIONEN usw.) SOLLTEN ZUR ERWEITERUNG GEÖFFNET, ABER ZUR ÄNDERUNG GESCHLOSSEN WERDEN.

Wenn eine einzelne Änderung an einem Programm zu einer Kaskade von Änderungen an abhängigen Modulen führt, weist dieses Programm die unerwünschten Attribute auf, die wir mit „schlechtem“ Design assoziieren. Das Programm wird zerbrechlich, starr, unvorhersehbar und nicht wiederverwendbar. Das Open-Closed-Prinzip greift dies auf sehr einfache Weise an. Es heißt, dass Sie Module entwerfen sollten, die sich nie ändern . Wenn sich die Anforderungen ändern, erweitern Sie das Verhalten solcher Module, indem Sie neuen Code hinzufügen und nicht indem Sie alten Code ändern, der bereits funktioniert.

Beschreibung

Module, die dem Open-Closed-Prinzip entsprechen, weisen zwei Hauptattribute 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 unverletzt. Niemand darf Änderungen am Quellcode vornehmen.

Es scheint, dass diese beiden Attribute im Widerspruch zueinander stehen. Die normale Art, das Verhalten eines Moduls zu erweitern, besteht darin, Änderungen an diesem Modul vorzunehmen. Es wird normalerweise angenommen, dass ein Modul, das nicht geändert werden kann, ein festes Verhalten aufweist. Wie können diese beiden gegensätzlichen Attribute gelöst werden?

Abstraktion ist der Schlüssel ...

Martijn Verburg
quelle
3
Dies ist ein guter Artikel, der die Abstraktion erklärt. Es muss jedoch ein grundlegender Punkt beachtet werden, und das ist ein gutes abstraktes Design, das überhaupt erst entworfen wurde. Viele Geschäfte haben eine Menge Legacy-Code, der nur durch "Modifikation" und nicht durch "Erweiterung" geändert werden kann. Wenn dies der Fall ist, sollte man wahrscheinlich daran arbeiten, das zu ändern, aber bis dahin müssen Sie den Code nicht mehr ändern.
Michael K
@ Chris, cool - Ich empfehle auch das Buch "Clean Code" von Onkel Bob, wenn Sie so etwas mögen.
Martijn Verburg
@Michael - Stimme vollkommen zu, es ist fast so, als müsste man den Code umgestalten, um ihn ideal testbar zu machen.
Martijn Verburg
Der Artikel zeigt sehr anschaulich, wie wichtig Abstraktion ist. Aber ich verstehe nicht den Zusammenhang zwischen Abstraktionen und versuche, Module niemals zu modifizieren, nachdem ich sie geschrieben habe. Die Abstraktion bedeutet, dass ich Modul X ändern kann, ohne Änderungen an Modul Y vornehmen zu müssen. Aber ist das nicht der Grund, warum ich Modul X oder Modul Y ändern kann, wenn ich muss?
Winston Ewert
1
Wow. Code ist unantastbar? Ich war noch nie ein großer Fan von Onkel Bob. Dieses Prinzip ist pedantisch, äußerst unpragmatisch und nur begrenzt mit der Realität verbunden.
user949300
12

Die Antwort von Kate Gregory ist sehr gut, aber betrachten Sie eine andere Situation, in der eine neue Anforderung durch eine relativ kleine Änderung in der vorhandenen InvoiceKlasse erfüllt werden kann . Angenommen, der Rechnungs-PDF muss ein neues Feld hinzugefügt werden. Laut OCP sollten wir immer noch eine neue Unterklasse erstellen, auch wenn das neue Feld in der vorhandenen Implementierung durch Ändern einiger Codezeilen hinzugefügt werden könnte.

Meines Erachtens spiegelt OCP die Realität der 80er und frühen 90er Jahre wider, in denen Projekte häufig nicht einmal die Versionskontrolle verwendeten, geschweige denn automatisierte Regressionstests oder den Vorteil ausgefeilter Refactoring-Tools hatten. OCP war ein Versuch, das Risiko zu vermeiden, dass manuell getesteter und in die Produktion gebrachter Code beschädigt wird. Heutzutage haben wir bessere Möglichkeiten, um das Risiko von Software-Brüchen zu bewältigen (Versionskontrollsysteme, TDD und automatisierte Tests sowie Refactoring-Tools).

Rogério
quelle
2
Ja, denn in der Praxis ist es nicht möglich, eine Klasse zu erstellen, die für alle möglichen Zukünfte erweitert werden kann, es sei denn, Sie machen alle Methoden geschützt (was das YAGNI-Prinzip, das viel wichtiger als das O / C ist, nervt und auch verletzt) dito).
Martin Wickman
"Laut OCP sollten wir immer noch eine neue Unterklasse erstellen, auch wenn das neue Feld in der vorhandenen Implementierung durch Ändern einiger Codezeilen hinzugefügt werden könnte.": Wirklich? Warum nicht neue Felder oder neue Methoden hinzufügen? Der wichtige Punkt ist, dass Sie nur hinzufügen (erweitern) und nicht ändern, was bereits vorhanden ist.
Giorgio
Ich halte das Prinzip für sinnvoll, wenn es um Standardbibliotheken / Frameworks geht. Sie möchten keine gängigen Codeteile öffnen und ändern. Ansonsten dreht sich alles um ständiges Refactoring und Testen, Testen, Testen.
mastaBlasta
@Giorgio Sicher, neue Felder oder Methoden hinzufügen , ist , was ich empfehlen würde, in den meisten Fällen. Aber das ist keine Erweiterung , sondern "Modifikation". Der springende Punkt von OCP ist, dass Code "für Änderungen geschlossen" (dh keine Änderungen an der vorhandenen Quelldatei) sein sollte, während er "für Erweiterungen offen" ist. Die Erweiterung in OCP wird durch die Vererbung der Implementierung erreicht.
Rogério
@ Rogério: Warum definieren Sie die Grenze zwischen Erweiterung und Änderung auf Klassenebene? Gibt es dafür einen bestimmten Grund? Ich würde es lieber auf der Methodenebene festlegen: Wenn Sie eine Methode ändern, ändert sich das Verhalten Ihrer Anwendung, wenn Sie eine (öffentliche) Methode hinzufügen, wird die Benutzeroberfläche erweitert.
Giorgio
6

Persönlich denke ich, dass dieses Prinzip mit einer Prise Salz genommen werden sollte. Code ist organisch, Unternehmen ändern sich und Code ändert sich im Laufe der Zeit entsprechend den Anforderungen eines Unternehmens.

Ich finde es sehr schwierig, mir klar zu machen, dass Abstraktion der Schlüssel ist. Was ist, wenn die Abstraktion ursprünglich falsch war? Was ist, wenn sich die Geschäftsfunktion erheblich geändert hat?

Dieses Prinzip stellt im Wesentlichen sicher, dass sich die ORIGINAL Intentionen und das Verhalten eines Designs niemals ändern dürfen. Das funktioniert wahrscheinlich für diejenigen, die öffentliche APIs haben und deren Kunden Probleme haben, mit neuen Releases und einigen anderen Randfällen Schritt zu halten. Wenn ein Unternehmen jedoch den gesamten Code besitzt, stelle ich dieses Prinzip in Frage.

Eine gute Testabdeckung Ihres Codes sollte das Refactoring Ihrer Codebasis zum Kinderspiel machen. Es bedeutet, dass es in Ordnung ist, Fehler zu machen - Ihre Tests helfen Ihnen dabei, ein besseres Design zu finden.

Wenn es keine Tests gibt, dann ist dieses Prinzip vernünftig.

user126776
quelle