Warum glaubt (/ hat) Bertrand Meyer, dass Subclassing die einzige Möglichkeit ist, ein „geschlossenes“ Modul zu erweitern?

19

In Meyers Object-Oriented Software Construction (1988) definiert er das Open / Closed-Prinzip wie folgt:

  • Ein Modul gilt als offen, wenn es noch zur Erweiterung zur Verfügung steht. Beispielsweise sollte es möglich sein, Felder zu den enthaltenen Datenstrukturen oder neue Elemente zu den von ihnen ausgeführten Funktionen hinzuzufügen.
  • Ein Modul gilt als geschlossen, wenn es anderen Modulen zur Verfügung steht. Dies setzt voraus, dass das Modul eine genau definierte, stabile Beschreibung erhalten hat (die Schnittstelle im Sinne des Versteckens von Informationen).

Er fährt fort zu sagen:

Wenn Sie ein Modul erneut öffnen, müssen Sie auch alle seine Clients erneut öffnen, um sie zu aktualisieren, da sie auf der alten Version basieren. … [Dieses Problem] tritt jedes Mal auf, wenn ein Modul um eine neue Funktion oder ein neues Datenelement erweitert werden muss, wodurch Änderungen in direkten und indirekten Clients ausgelöst werden. ... Mit klassischen Ansätzen für Design und Programmierung gibt es keine Möglichkeit, offene und geschlossene Module zu schreiben.

Meyers Lösung für dieses Dilemma lautet: Erweitern Sie niemals ein Bibliotheksmodul, indem Sie vorhandene Klassen ändern. Schreiben Sie stattdessen ein neues Modul, das die vorhandenen Klassen unterordnet, und lassen Sie neue Clients von diesem neuen Modul abhängen.

Jetzt, 1988, habe ich (prozedurale) Spielzeugprogramme in Turbo Pascal und Blankenship Basic geschrieben. Meine Berufserfahrung im 21. Jahrhundert liegt in der JVM, der CLR und in dynamischen Sprachen. Ich weiß also nicht, was Meyer meint durch "klassische Ansätze für Design und Programmierung".

Meyers einziges konkretes Beispiel dafür, warum Client-Module erneut geöffnet werden müssen (eine switch-Anweisung in einer Enumeration, die jetzt mehr Mitglieder hat und mehr Fälle erfordert), scheint vernünftig zu sein, aber er rechtfertigt die Behauptung nicht annähernd, dass jedes Mal , wenn Sie einer Bibliothek Funktionen hinzufügen Modul müssen Sie alle seine Clients aktualisieren .

Gibt es einen historischen Grund, warum diese Behauptung 1988 offensichtlich schien? Hat das Hinzufügen von Funktionen oder Datenstrukturen zu einer statischen C-Bibliothek beispielsweise das Layout dahingehend geändert, dass Clients auch bei abwärtskompatiblen APIs neu kompiliert werden mussten? Oder spricht Meyer wirklich nur von einem Mechanismus zur Durchsetzung der API-Abwärtskompatibilität?

David Moles
quelle
3
Interessante Frage! Ich habe das Gefühl, dass die Antwort in irgendeiner Weise mit dem fundamentalen Unterschied zwischen abstrakten Datentypen und objektorientierter Datenabstraktion zusammenhängt, die die beiden vorherrschenden Datenabstraktionsmechanismen in der modularen Programmierung sind (was Betrand Meyer als "klassische Ansätze" bezeichnet) ") und objektorientierte Programmierung (lest die Kommentare!).
Jörg W Mittag
Das ist komisch. Es scheint offensichtlich von der Realität (sogar im Jahr 1988) widersprochen. Auch würde sein befürworteter Ansatz zu einer nicht hilfreichen Verbreitung von Modulen führen.
@ dan1111: Eiffels Herangehensweise an die Vererbung, einschließlich, aber nicht beschränkt auf die Herangehensweise an die Mehrfachvererbung, unterscheidet sich von C ++, Java, C # usw., sodass es nicht verwunderlich ist, dass sich die Herangehensweise unterscheidet. Schließlich hat er Eiffel speziell entwickelt, um seine Ansichten zu OO zu unterstützen.
Jörg W Mittag

Antworten:

18

Soweit ich das beurteilen kann, wurde diese Frage von Bertrand Meyer selbst beantwortet, und die Antwort lautet: Diese Aussage ist nicht korrekt. Mit klassischen Ansätzen zur Gestaltung und Programmierung, es in der Tat kann ein Weg , um Schreibmodule, die sowohl offen als auch geschlossen sind.

Um dies herauszufinden, müssen Sie die zweite Auflage dieses Buches studieren (neun Jahre später, 1997). Laut Vorwort zur zweiten Auflage ist es

kein Update, sondern das Ergebnis einer gründlichen Überarbeitung. Kein Absatz der Originalfassung ist unangetastet geblieben. (Eigentlich kaum eine Zeile.)

Insbesondere die Aussage, die Sie verwirrt, ist verschwunden. Es gibt noch ein Open-Closed-Prinzip- Kapitel in "§3.3 Fünf Prinzipien", und dieses Thema wird in "§14.7 Einführung in die Vererbung" eingehender erörtert, aber die Aussage aus der ersten Ausgabe ist nicht mehr da.

Was es stattdessen gibt, konzentriert sich darauf, wie es im OO-Ansatz bequemer und idiomatischer ist als in früheren Wegen.

Dank der Vererbung können OO-Entwickler bei der Softwareentwicklung einen viel inkrementelleren Ansatz verfolgen, als dies früher mit früheren Methoden möglich war ... (§3.3)

Diese doppelte Anforderung (offen und geschlossen) scheint ein Dilemma zu sein, und klassische Modulstrukturen bieten keinen Anhaltspunkt. Aber Vererbung löst es. Eine Klasse ist geschlossen, da sie kompiliert, in einer Bibliothek gespeichert, in Baselines zusammengefasst und von Clientklassen verwendet werden kann. Es ist jedoch auch offen, da jede neue Klasse es als übergeordnetes Element verwenden kann, indem sie neue Features hinzufügt und geerbte Features erneut deklariert. In diesem Prozess besteht keine Notwendigkeit, das Original zu ändern oder seine Kunden zu stören ... (§14.7)

Da Sie sich auch zu fragen scheinen, was Meyer hier mit "klassischen Ansätzen" gemeint hat, finden Sie eine Erklärung in §4.7 Traditionelle modulare Strukturen . In diesem Abschnitt wird erklärt, dass diese "Bibliotheken von Routinen" und "Paketen" bedeuten (für letztere sagt der Autor, dass der Begriff von Ada übernommen wurde und erwähnt andere Sprachen mit dieser Funktion - Cluster in CLU und Module in Modula).

Wenn Sie darüber nachdenken, sollte keiner dieser Ansätze ursprünglich dazu beitragen, Code zu schreiben, der dem Open-Closed-Prinzip entspricht. Dies könnte den Autor zu seiner etwas verfrühten Einschätzung führen, die später in der zweiten Auflage korrigiert wurde.


Als für das, was speziell gemacht Autor Änderung auf dieser Aussage in zwischen dem ersten und zweiten Auflage ihrer Meinung, ich glaube , eine Antwort finden kann, wieder in dem Buch selbst, nämlich in Teil F: Anwendung das Verfahrens in verschiedenen Sprachen und Umgebungen“ In. In diesem Kapitel erläutert der Autor, wie objektorientierte Methoden in älteren Sprachen verwendet werden können:

Klassische Sprachen wie Fortran sind überhaupt keine OO, aber Menschen, die sie noch verwenden müssen ... möchten möglicherweise so viele OO-Ideen anwenden, wie im Rahmen dieser älteren Ansätze machbar sind.

Insbesondere erklärt Meyer in diesem Teil ausführlich, wie eine Vererbung (mit gewissen Einschränkungen und Einschränkungen) in C und sogar in Fortran implementiert werden kann.

Sie sehen, dies erfordert wirklich eine Überarbeitung dieser Aussage aus der ersten Ausgabe. Es scheint praktisch unmöglich zu erklären, wie man "mit klassischen Ansätzen ... es gibt keinen Weg" mit realistischen Beispielen in Einklang bringen kann , wie genau dies getan werden kann.

Mücke
quelle
Interessant, und ich werde auf jeden Fall versuchen müssen, die zweite Ausgabe in den Griff zu bekommen, aber es ist mir immer noch nicht klar, warum selbst eine nicht-OO "klassische" Bibliothek (zumindest bestimmte Arten von) Funktionen nicht hinzufügen konnte, ohne sie zu stören Kunden.
David Moles
@ DavidMoles Sache ist, es könnte , und der letzte Teil meiner Antwort erklärt das, und dass Meyer selbst das erkannte (als er für die 2. Auflage überarbeitete) und sogar Beispiele gab, wie es getan werden kann. "Was speziell den Autor dazu gebracht hat, seine Meinung zu ändern ..." etc
gnat
Hmm. Ich sehe "Version 2 dieser Bibliothek, die Version 1 ersetzt und mit dieser abwärtskompatibel ist, die folgenden Funktionen hinzufügt ..." nicht als "Vererbung", außer auf eine möglichst breite konzeptionelle Art und Weise.
David Moles
(Vererbung impliziert für mich, dass Version 1 noch vorhanden ist und von Version 2 aufgerufen wird.)
David Moles
@DavidMoles durch Version 2 ersetzen (wie in, Quellcode ändern und neu kompilieren ) würde nicht als "zur Änderung geschlossen" qualifizieren. Sie können dies einfach in Wikipedia-Artikel überprüfen : "Entität kann zulassen, dass ihr Verhalten erweitert wird, ohne ihren Quellcode zu ändern ... "
Mücke