Warum verletzen viele Softwareentwickler das Open / Closed-Prinzip, indem sie viele Dinge wie das Umbenennen von Funktionen ändern, die die Anwendung nach dem Upgrade beschädigen?
Diese Frage springt mir nach der schnellen und der kontinuierlichen Version in der React- Bibliothek in den Kopf .
In kurzen Abständen bemerke ich viele Änderungen in der Syntax, den Komponentennamen, ... usw
Beispiel in der kommenden Version von React :
Neue Verfallswarnungen
Die größte Änderung ist, dass wir React.PropTypes und React.createClass in ihre eigenen Pakete extrahiert haben. Auf beide kann weiterhin über das Hauptobjekt "React" zugegriffen werden. Wenn Sie jedoch eines der beiden Objekte verwenden, wird im Entwicklungsmodus eine Warnung zur einmaligen Verfallserklärung in der Konsole protokolliert. Dies wird zukünftige Optimierungen der Codegröße ermöglichen.
Diese Warnungen wirken sich nicht auf das Verhalten Ihrer Anwendung aus. Wir stellen jedoch fest, dass sie möglicherweise zu Frustrationen führen, insbesondere wenn Sie ein Testframework verwenden, das console.error als Fehler behandelt.
- Werden diese Änderungen als Verstoß gegen diesen Grundsatz angesehen?
- Wie lerne ich es als Anfänger von etwas wie Reagieren mit diesen schnellen Änderungen in der Bibliothek (es ist so frustrierend)?
quelle
Antworten:
IMHO JacquesBs Antwort, obwohl sie viel Wahrheit enthält, zeigt ein grundlegendes Missverständnis der OCP. Um fair zu sein, Ihre Frage drückt auch dieses Missverständnis bereits aus - das Umbenennen von Funktionen bricht die Abwärtskompatibilität , nicht aber die OCP. Wenn eine Unterbrechung der Kompatibilität erforderlich erscheint (oder zwei Versionen derselben Komponente beibehalten werden müssen, um die Kompatibilität nicht zu beeinträchtigen), wurde das OCP bereits zuvor unterbrochen!
Wie Jörg W Mittag bereits in seinen Kommentaren erwähnt hat, lautet das Prinzip nicht "Sie können das Verhalten einer Komponente nicht ändern" - es heißt, man sollte versuchen , Komponenten so zu gestalten, dass sie offen für Wiederverwendung (oder Erweiterung) sind. in mehrfacher Hinsicht ohne die Notwendigkeit einer Änderung. Dies kann durch Angabe der richtigen "Erweiterungspunkte" oder, wie von @AntP erwähnt, "durch Zerlegen einer Klassen- / Funktionsstruktur bis zu dem Punkt geschehen, an dem standardmäßig jeder natürliche Erweiterungspunkt vorhanden ist". IMHO, das dem OCP folgt, hat nichts mit "die alte Version aus Gründen der Abwärtskompatibilität unverändert lassen" zu tun ! Oder zitieren Sie @ DerekElkins Kommentar unten:
Gute Programmierer nutzen ihre Erfahrung, um Komponenten unter Berücksichtigung der "richtigen" Erweiterungspunkte zu entwerfen (oder - noch besser - so, dass keine künstlichen Erweiterungspunkte erforderlich sind). Um dies jedoch korrekt und ohne unnötige Überarbeitung durchführen zu können, müssen Sie im Voraus wissen, wie zukünftige Anwendungsfälle Ihrer Komponente aussehen könnten. Selbst erfahrene Programmierer können nicht in die Zukunft schauen und kennen alle anstehenden Anforderungen im Voraus. Und deshalb muss manchmal die Abwärtskompatibilität verletzt werden - unabhängig davon, wie viele Erweiterungspunkte Ihre Komponente hat oder wie gut sie dem OCP in Bezug auf bestimmte Arten von Anforderungen entspricht, wird es immer eine Anforderung geben, die nicht einfach ohne Änderung implementiert werden kann die Komponente.
quelle
Das Open / Closed-Prinzip hat Vorteile, aber auch einige gravierende Nachteile.
Theoretisch löst das Prinzip das Problem der Abwärtskompatibilität, indem Code erstellt wird, der "offen für Erweiterungen, aber geschlossen für Änderungen" ist. Wenn für eine Klasse neue Anforderungen gelten, ändern Sie niemals den Quellcode der Klasse selbst, sondern erstellen stattdessen eine Unterklasse, die nur die zum Ändern des Verhaltens erforderlichen Elemente überschreibt. Der gesamte Code, der gegen die Originalversion der Klasse geschrieben wurde, ist daher nicht betroffen. Sie können also sicher sein, dass Ihre Änderung den vorhandenen Code nicht beschädigt hat.
In der Realität kommt es leicht zu einer Aufblähung des Codes und zu einem verwirrenden Durcheinander veralteter Klassen. Wenn es nicht möglich ist, ein Verhalten einer Komponente durch Erweiterung zu ändern, müssen Sie eine neue Variante der Komponente mit dem gewünschten Verhalten bereitstellen und die alte Version aus Gründen der Abwärtskompatibilität unverändert lassen.
Angenommen, Sie entdecken einen grundlegenden Konstruktionsfehler in einer Basisklasse, von der viele Klassen erben. Angenommen, der Fehler ist darauf zurückzuführen, dass ein privates Feld vom falschen Typ ist. Sie können dies nicht beheben, indem Sie ein Mitglied überschreiben. Grundsätzlich müssen Sie die gesamte Klasse überschreiben, was bedeutet, dass Sie letztendlich erweitern
Object
, um eine alternative Basisklasse bereitzustellen - und jetzt müssen Sie auch Alternativen für alle Unterklassen bereitstellen, wodurch eine doppelte Objekthierarchie entsteht, eine Hierarchie fehlerhaft, eine verbesserte . Sie können die fehlerhafte Hierarchie jedoch nicht entfernen (da das Löschen des Codes eine Änderung darstellt). Alle zukünftigen Clients sind beiden Hierarchien ausgesetzt.Die theoretische Antwort auf dieses Problem lautet nun "Entwerfen Sie es einfach beim ersten Mal richtig". Wenn der Code perfekt zerlegt ist, keine Fehler aufweist und mit Erweiterungspunkten versehen ist, die für alle möglichen zukünftigen Änderungen der Anforderungen vorbereitet sind, vermeiden Sie das Durcheinander. Aber in Wirklichkeit macht jeder Fehler und niemand kann die Zukunft perfekt vorhersagen.
Nehmen Sie etwas wie das .NET-Framework - es enthält immer noch eine Reihe von Auflistungsklassen, die entworfen wurden, bevor Generika vor mehr als einem Jahrzehnt eingeführt wurden. Dies ist sicherlich ein Segen für die Abwärtskompatibilität (Sie können das Framework aktualisieren, ohne etwas neu schreiben zu müssen), aber es erschwert auch das Framework und bietet Entwicklern zahlreiche Optionen, bei denen viele einfach veraltet sind.
Anscheinend haben die Entwickler von React das Gefühl, dass es den Aufwand an Komplexität und Code-Bloat nicht wert war, sich strikt an das Open / Closed-Prinzip zu halten.
Die pragmatische Alternative zum Öffnen / Schließen ist die kontrollierte Abwertung. Anstatt die Abwärtskompatibilität in einem einzelnen Release zu unterbrechen, werden alte Komponenten für einen Release-Zyklus beibehalten, aber Clients werden über Compiler-Warnungen darüber informiert, dass der alte Ansatz in einem späteren Release entfernt wird. Dies gibt den Clients Zeit, den Code zu ändern. Dies scheint in diesem Fall der Ansatz von React zu sein.
(Meine Interpretation des Prinzips basiert auf dem Open-Closed-Prinzip von Robert C. Martin)
quelle
Ich würde das offene / geschlossene Prinzip als Ideal bezeichnen. Wie bei allen Idealen wird die Realität der Softwareentwicklung kaum berücksichtigt. Ebenso wie alle Ideale ist es unmöglich, es in der Praxis tatsächlich zu erreichen - man bemüht sich lediglich, sich diesem Ideal so gut wie möglich anzunähern.
Die andere Seite der Geschichte ist als goldene Handschellen bekannt. Goldene Handschellen sind das, was Sie bekommen, wenn Sie sich zu sehr dem offenen / geschlossenen Prinzip unterwerfen. Goldene Handschellen treten auf, wenn Ihr Produkt, das niemals die Rückwärtskompatibilität bricht, nicht wachsen kann, weil in der Vergangenheit zu viele Fehler gemacht wurden.
Ein bekanntes Beispiel hierfür ist der Windows 95-Speichermanager. Im Rahmen des Marketings für Windows 95 wurde angegeben, dass alle Windows 3.1-Anwendungen unter Windows 95 funktionieren würden. Microsoft erwarb Lizenzen für Tausende von Programmen, um sie unter Windows 95 zu testen. Einer der Problemfälle war Sim City. Sim City hatte tatsächlich einen Fehler, der dazu führte, dass in den nicht zugewiesenen Speicher geschrieben wurde. In Windows 3.1 ohne einen "richtigen" Speichermanager handelte es sich hierbei um einen geringfügigen Fauxpas. In Windows 95 würde der Speichermanager dies jedoch abfangen und einen Segmentierungsfehler verursachen. Die Lösung? Wenn Ihr Anwendungsname unter Windows 95 lautet
simcity.exe
, lockert das Betriebssystem die Einschränkungen des Speichermanagers, um den Segmentierungsfehler zu vermeiden!Das eigentliche Problem hinter diesem Ideal sind die reduzierten Konzepte von Produkten und Dienstleistungen. Niemand tut wirklich das eine oder andere. Alles reiht sich irgendwo im grauen Bereich zwischen den beiden aneinander. Wenn Sie von einem produktorientierten Ansatz ausgehen, klingt Öffnen / Schließen nach einem großartigen Ideal. Ihre Produkte sind zuverlässig. Wenn es um Dienstleistungen geht, ändert sich die Geschichte. Es ist leicht zu zeigen, dass mit dem Open / Closed-Prinzip die Menge an Funktionen, die Ihr Team unterstützen muss, sich asymptotisch der Unendlichkeit annähern muss , da Sie niemals alte Funktionen bereinigen können. Dies bedeutet, dass Ihr Entwicklungsteam jedes Jahr mehr Code unterstützen muss. Schließlich erreichen Sie eine Sollbruchstelle.
Die meiste Software, insbesondere Open Source, folgt heute einer gelockerten Version des Open / Closed-Prinzips. Es ist sehr verbreitet, dass Open / Closed für kleinere Releases sklavisch gefolgt, für größere Releases jedoch aufgegeben wird. Zum Beispiel enthält Python 2.7 viele "schlechte Entscheidungen" aus Python 2.0 und 2.1, aber Python 3.0 hat sie alle beseitigt. (Auch die Umstellung von der Windows 95-Codebasis auf die Windows NT-Codebasis bei der Veröffentlichung von Windows 2000 brachte viele Dinge zum Erliegen, aber es bedeutete, dass wir uns nie mit einem Speichermanager befassen mussten, der den Anwendungsnamen überprüfte, um über das Verhalten zu entscheiden!)
quelle
Die Antwort von Doc Brown ist am ehesten zutreffend, die anderen Antworten veranschaulichen Missverständnisse des Open-Closed-Prinzips.
Um ausdrücklich das Missverständnis zu artikulieren, so scheint es ein Glaube zu sein , dass das OCP bedeutet , dass Sie nicht rückwärts inkompatible Änderungen vornehmen (oder sogar alle Änderungen oder etwas in diese Richtung.) Die OCP - Komponenten über die Gestaltung so , dass Sie nicht brauchen , um Nehmen Sie Änderungen vor, um die Funktionalität zu erweitern, unabhängig davon, ob diese Änderungen abwärtskompatibel sind oder nicht. Neben der Hinzufügung von Funktionen gibt es viele andere Gründe, aus denen Sie Änderungen an einer Komponente vornehmen können, unabhängig davon, ob diese abwärtskompatibel (z. B. Refactoring oder Optimierung) oder abwärtskompatibel (z. B. Ablehnen und Entfernen von Funktionen) ist. Dass Sie diese Änderungen vornehmen, bedeutet nicht, dass Ihre Komponente gegen das OCP verstoßen hat (und bedeutet definitiv nicht, dass Sie dies tun gegen die OCP verstoßen).
Wirklich, es geht überhaupt nicht um Quellcode. Eine abstraktere und relevantere Aussage der OCP lautet: "Eine Komponente sollte eine Erweiterung ermöglichen, ohne dass dabei ihre Abstraktionsgrenzen verletzt werden müssen." Ich würde noch weiter gehen und sagen , eine modernere Interpretation ist: „eine Komponente sollte erzwingen seine Abstraktion Grenzen aber für die Erweiterung ermöglichen“. Sogar in dem Artikel über das OCP von Bob Martin, in dem er "für Modifikationen verschlossen" beschreibt, dass "der Quellcode unverletzt ist", spricht er später von Kapselung, die nichts mit Modifikationen des Quellcodes und allem mit Abstraktion zu tun hat Grenzen.
Die fehlerhafte Prämisse in der Frage ist also, dass das OCP eine Richtlinie über die Entwicklung einer Codebasis ist (beabsichtigt ist). Das OCP wird in der Regel als Slogan bezeichnet: "Eine Komponente sollte für Erweiterungen offen und für Änderungen durch Verbraucher geschlossen sein." Grundsätzlich sollten Verbraucher einer Komponente, die der Komponente Funktionen hinzufügen möchten, in der Lage sein, die alte Komponente durch die zusätzlichen Funktionen zu erweitern, die alte Komponente jedoch nicht zu ändern .
Das OCP sagt nichts über den Schöpfer einer Komponente aus , die Funktionen ändert oder entfernt . Die OCP plädiert nicht dafür, die Fehlerkompatibilität für immer aufrechtzuerhalten . Sie als Urheber verletzen das OCP nicht, indem Sie eine Komponente ändern oder sogar entfernen. Sie, oder besser gesagt die Komponenten, die Sie geschrieben haben, verletzen das OCP, wenn die Konsumenten Ihre Komponenten nur durch Mutation, z. B. durch Affen-Patching , funktionalisieren könnenoder Zugriff auf den Quellcode und erneutes Kompilieren. In vielen Fällen handelt es sich nicht um Optionen für den Verbraucher, dh wenn Ihre Komponente nicht "erweiterungsfähig" ist, hat er kein Glück. Sie können Ihre Komponente einfach nicht für ihre Bedürfnisse verwenden. Die OCP plädiert dafür, die Konsumenten Ihrer Bibliothek nicht in diese Position zu versetzen, zumindest in Bezug auf eine erkennbare Klasse von "Erweiterungen". Selbst wenn Änderungen können auf den Quellcode oder sogar die primäre Kopie des Quellcodes gemacht werden, ist es am besten zu „vorgeben“ , dass Sie es nicht ändern können , da es viele möglichen negativen Folgen sind so zu tun.
Um Ihre Fragen zu beantworten: Nein, dies sind keine Verstöße gegen die OCP. Keine Änderung, die ein Autor vornimmt, kann einen Verstoß gegen das OCP darstellen, da das OCP kein Verhältnis von Änderungen darstellt. Die Änderungen können jedoch erstellen Verletzungen des OCP, und sie können durch Ausfälle der OCP in früheren Versionen der Code - Basis motiviert werden. Das OCP ist eine Eigenschaft eines bestimmten Codeteils, nicht die Evolutionsgeschichte einer Codebasis.
Für dagegen die Abwärtskompatibilität ist eine Eigenschaft einer Änderung des Codes. Es macht keinen Sinn zu sagen, dass ein Teil des Codes abwärtskompatibel ist oder nicht. Es ist nur sinnvoll, über die Abwärtskompatibilität eines Codes mit einem älteren Code zu sprechen . Daher ist es nie sinnvoll, davon zu sprechen, dass der erste Teil eines Codes abwärtskompatibel ist oder nicht. Der erste Codeschnitt kann das OCP erfüllen oder nicht erfüllen, und im Allgemeinen können wir feststellen, ob ein Code das OCP erfüllt, ohne auf historische Versionen des Codes Bezug zu nehmen.
Was Ihre letzte Frage anbelangt, so ist StackExchange im Allgemeinen wohl eher als meinungsbasiert eingestuft, aber kurz gesagt, es ist willkommen, JavaScript zu verwenden, wobei das von Ihnen beschriebene Phänomen in den letzten Jahren als JavaScript-Müdigkeit bezeichnet wurde . (Fühlen Sie sich frei zu googeln , um eine Vielzahl anderer Artikel zu finden, von denen einige satirisch sind und darüber aus verschiedenen Perspektiven sprechen.)
quelle
private
. Wenn ein Autor später eineprivate
Methode erstelltpublic
, bedeutet dies nicht, dass er die Zugriffskontrolle verletzt hat (1/2)private
vorher nicht wirklich war. "Das Entfernen einer veröffentlichten Komponente ist eindeutig eine entscheidende Änderung", heißt es nicht. Entweder erfüllen die Komponenten der neuen Version die Anforderungen des OCP, oder sie erfüllen diese nicht. Sie benötigen nicht den Verlauf der Codebasis, um dies zu bestimmen. Nach Ihrer Logik könnte ich niemals Code schreiben, der das OCP erfüllt. Sie verbinden die Abwärtskompatibilität, eine Eigenschaft von Codeänderungen, mit der OCP, einer Eigenschaft von Code. Ihr Kommentar ist ungefähr so sinnvoll, als würde er sagen, dass Quicksort nicht abwärtskompatibel ist. (2/2)