Die vergessene Geschichte der OOP

Hinweis: Dies ist Teil der Serie „Composing Software“ (jetzt ein Buch!), in der es darum geht, funktionale Programmierung und kompositorische Softwaretechniken in JavaScript ES6+ von Grund auf zu lernen. Bleiben Sie dran. Es wird noch viel mehr davon kommen!
Buy the Book | Index | < Previous | Next >

Die funktionalen und imperativen Programmierparadigmen, die wir heute verwenden, wurden erstmals in den 1930er Jahren mit dem Lambda-Kalkül und der Turing-Maschine mathematisch erforscht, Es handelt sich dabei um alternative Formulierungen des universellen Rechnens (formalisierte Systeme, die allgemeine Berechnungen durchführen können). Die Church-Turing-These zeigte, dass Lambda-Kalkül und Turing-Maschinen funktional äquivalent sind – dass alles, was mit einer Turing-Maschine berechnet werden kann, auch mit Lambda-Kalkül berechnet werden kann und umgekehrt.

Anmerkung: Es ist ein weit verbreiteter Irrtum, dass Turing-Maschinen alles Berechenbare berechnen können. Es gibt Klassen von Problemen (z.B. das Halteproblem), die in einigen Fällen berechenbar sein können, aber nicht generell in allen Fällen mit Turing-Maschinen berechenbar sind. Wenn ich in diesem Text das Wort „berechenbar“ verwende, meine ich „berechenbar durch eine Turing-Maschine“.

Der Lambda-Kalkül repräsentiert einen Top-Down-Ansatz zur Berechnung von Funktionen, während die Laufschrift/Registermaschinen-Formulierung der Turing-Maschine einen Bottom-Up-Ansatz zur Berechnung imperativer (Schritt-für-Schritt-) Funktionen repräsentiert.

Low-Level-Sprachen wie Maschinencode und Assembler erschienen in den 1940er Jahren, und Ende der 1950er Jahre erschienen die ersten populären Hochsprachen. Lisp-Dialekte sind auch heute noch in Gebrauch, darunter Clojure, Scheme, AutoLISP usw. FORTRAN und COBOL erschienen beide in den 1950er Jahren und sind Beispiele für imperative Hochsprachen, die auch heute noch verwendet werden, obwohl die Sprachen der C-Familie sowohl COBOL als auch FORTRAN für die meisten Anwendungen ersetzt haben.

Beide, die imperative Programmierung und die funktionale Programmierung, haben ihre Wurzeln in der Mathematik der Rechentheorie, die den digitalen Computern vorausging. Der Begriff „objektorientierte Programmierung“ (OOP) wurde von Alan Kay um 1966 oder 1967 während seines Studiums geprägt.

Ivan Sutherlands bahnbrechende Sketchpad-Anwendung war eine frühe Inspiration für OOP. Sie wurde zwischen 1961 und 1962 entwickelt und 1963 in seiner Sketchpad Thesis veröffentlicht. Bei den Objekten handelte es sich um Datenstrukturen, die grafische Bilder auf einem Oszilloskop-Bildschirm darstellten und die Vererbung über dynamische Delegierte ermöglichten, die Ivan Sutherland in seiner Dissertation „Master“ nannte. Jedes beliebige Objekt konnte ein „Master“ werden, und weitere Instanzen der Objekte wurden „occurrences“ genannt. Die Master von Sketchpad haben viel mit der prototypischen Vererbung von JavaScript gemeinsam.

Anmerkung: Der TX-2 am MIT Lincoln Laboratory war eine der ersten Anwendungen eines grafischen Computermonitors mit direkter Bildschirminteraktion mit einem Lichtstift. Der EDSAC, der von 1948-1958 in Betrieb war, konnte Grafiken auf einem Bildschirm darstellen. Der Whirlwind am MIT verfügte 1949 über ein funktionierendes Oszilloskop-Display. Die Motivation für das Projekt bestand darin, einen allgemeinen Flugsimulator zu entwickeln, der in der Lage war, die Rückmeldung der Instrumente für mehrere Flugzeuge zu simulieren. Dies führte zur Entwicklung des SAGE-Rechnersystems. Der TX-2 war ein Testcomputer für SAGE.

Die erste Programmiersprache, die allgemein als „objektorientiert“ anerkannt wurde, war Simula, die 1965 spezifiziert wurde. Wie Sketchpad enthielt Simula Objekte und führte schließlich Klassen, Klassenvererbung, Unterklassen und virtuelle Methoden ein.

Anmerkung: Eine virtuelle Methode ist eine Methode, die in einer Klasse definiert ist und von Unterklassen außer Kraft gesetzt werden soll. Virtuelle Methoden ermöglichen es einem Programm, Methoden aufzurufen, die zum Zeitpunkt der Kompilierung des Codes noch nicht existieren, indem dynamisches Dispatching eingesetzt wird, um zu bestimmen, welche konkrete Methode zur Laufzeit aufgerufen werden soll. JavaScript verfügt über dynamische Typen und verwendet die Delegationskette, um zu bestimmen, welche Methoden aufgerufen werden sollen, so dass das Konzept der virtuellen Methoden dem Programmierer nicht offengelegt werden muss. Anders ausgedrückt: Alle Methoden in JavaScript verwenden die Methodenverteilung zur Laufzeit, so dass Methoden in JavaScript nicht als „virtuell“ deklariert werden müssen, um diese Funktion zu unterstützen.

„Ich habe den Begriff ‚objektorientiert‘ erfunden, und ich kann Ihnen sagen, dass ich nicht C++ im Sinn hatte.“ ~ Alan Kay, OOPSLA ’97

Alan Kay prägte den Begriff „objektorientiertes Programmieren“ während seines Studiums im Jahr 1966 oder 1967. Die große Idee war, gekapselte Minicomputer in Software zu verwenden, die über Message Passing statt über direkten Datenaustausch kommunizierten – um Programme nicht mehr in separate „Datenstrukturen“ und „Prozeduren“ zu zerlegen.

„Das Grundprinzip des rekursiven Designs besteht darin, den Teilen die gleiche Macht zu geben wie dem Ganzen.“ ~ Bob Barton, der Hauptentwickler des B5000, eines Großrechners, der für die Ausführung von Algol-60 optimiert war.

Smalltalk wurde von Alan Kay, Dan Ingalls, Adele Goldberg und anderen bei Xerox PARC entwickelt. Smalltalk war stärker objektorientiert als Simula – alles in Smalltalk ist ein Objekt, einschließlich Klassen, Ganzzahlen und Blöcke (Closures). Das ursprüngliche Smalltalk-72 verfügte nicht über eine Unterklassenbildung. Das wurde erst in Smalltalk-76 von Dan Ingalls eingeführt.

Während Smalltalk Klassen und schließlich Subklassifizierung unterstützte, ging es bei Smalltalk nicht um Klassen oder Subklassifizierung. Es war eine funktionale Sprache, die sowohl von Lisp als auch von Simula inspiriert war. Alan Kay hält die Konzentration der Industrie auf die Unterklassenbildung für eine Ablenkung von den wahren Vorteilen der objektorientierten Programmierung.

„Es tut mir leid, dass ich vor langer Zeit den Begriff „Objekte“ für dieses Thema geprägt habe, weil er viele Leute dazu bringt, sich auf die weniger wichtige Idee zu konzentrieren. Die große Idee ist Messaging.“
~ Alan Kay

In einem E-Mail-Austausch aus dem Jahr 2003 stellte Alan Kay klar, was er meinte, als er Smalltalk als „objektorientiert“ bezeichnete:

„OOP bedeutet für mich nur Messaging, lokales Behalten und Schützen und Verstecken von Zustandsverläufen und extremes Late-Binding von allen Dingen.“
~ Alan Kay

Mit anderen Worten, nach Alan Kay sind die wesentlichen Bestandteile von OOP:

  • Message passing
  • Encapsulation
  • Dynamic binding

Besonders die Vererbung und der Subklassen-Polymorphismus wurden von Alan Kay, dem Mann, der den Begriff geprägt und OOP der breiten Masse nahegebracht hat, NICHT als wesentliche Bestandteile von OOP angesehen.

Das Wesen von OOP

Die Kombination von Nachrichtenübermittlung und Kapselung dient einigen wichtigen Zwecken:

  • Vermeidung von gemeinsamem veränderlichem Zustand durch Kapselung des Zustands und Isolierung anderer Objekte von lokalen Zustandsänderungen. Die einzige Möglichkeit, den Zustand eines anderen Objekts zu beeinflussen, besteht darin, dieses Objekt zu bitten (nicht zu befehlen), den Zustand zu ändern, indem eine Nachricht gesendet wird. Zustandsänderungen werden auf lokaler, zellularer Ebene kontrolliert und sind nicht dem gemeinsamen Zugriff ausgesetzt.
  • Entkopplung von Objekten voneinander – der Nachrichtensender ist nur lose an den Nachrichtenempfänger gekoppelt, und zwar über die Nachrichten-API.
  • Anpassungsfähigkeit und Widerstandsfähigkeit gegenüber Änderungen zur Laufzeit durch Late Binding. Die Anpassungsfähigkeit zur Laufzeit bietet viele große Vorteile, die Alan Kay als wesentlich für OOP erachtete.

Diese Ideen wurden durch biologische Zellen und/oder einzelne Computer in einem Netzwerk inspiriert, was auf Alan Kays Hintergrund in Biologie und den Einfluss des Designs von Arpanet (einer frühen Version des Internets) zurückzuführen ist. Schon damals stellte sich Alan Kay vor, dass die Software auf einem riesigen, verteilten Computer (dem Internet) läuft, auf dem die einzelnen Computer wie biologische Zellen funktionieren, die unabhängig voneinander in ihrem eigenen isolierten Zustand arbeiten und über die Weitergabe von Nachrichten kommunizieren.

„Mir wurde klar, dass die Zelle/Gesamtcomputer-Metapher Daten überflüssig machen würde“
~ Alan Kay

Mit „Daten überflüssig machen“ meinte Alan Kay sicherlich die Probleme mit gemeinsam genutzten veränderlichen Zuständen und die enge Kopplung, die durch gemeinsam genutzte Daten verursacht wird – heute gängige Themen.

Aber in den späten 1960er Jahren waren die Programmierer der ARPA frustriert, weil sie vor der Erstellung der Software eine Datenmodell-Darstellung für ihre Programme wählen mussten. Prozeduren, die zu eng an bestimmte Datenstrukturen gekoppelt waren, ließen sich nicht einfach ändern. Sie wollten eine homogenere Behandlung von Daten.

„Der Sinn von OOP ist es, dass man sich keine Gedanken darüber machen muss, was in einem Objekt enthalten ist. Objekte, die auf verschiedenen Rechnern und in verschiedenen Sprachen erstellt wurden, sollten in der Lage sein, miteinander zu kommunizieren“ ~ Alan Kay

Objekte können die Implementierung von Datenstrukturen abstrahieren und verbergen. Die interne Implementierung eines Objekts kann sich ändern, ohne andere Teile des Softwaresystems zu zerstören. Bei extremem Late Binding könnte sogar ein völlig anderes Computersystem die Aufgaben eines Objekts übernehmen, ohne dass die Software darunter leidet. In der Zwischenzeit könnten Objekte eine Standardschnittstelle bereitstellen, die mit jeder Datenstruktur arbeitet, die das Objekt intern verwendet. Die gleiche Schnittstelle könnte mit einer verknüpften Liste, einem Baum, einem Strom usw. funktionieren.

Alan Kay sah Objekte auch als algebraische Strukturen, die bestimmte mathematisch nachweisbare Garantien über ihr Verhalten abgeben:

„Mein mathematischer Hintergrund ließ mich erkennen, dass jedes Objekt mehrere Algebren haben könnte, die mit ihm verbunden sind, und dass es Familien von diesen geben könnte, und dass diese sehr, sehr nützlich wären.“
~ Alan Kay

Dies hat sich als wahr erwiesen und bildet die Grundlage für Objekte wie Versprechen und Linsen, die beide von der Kategorientheorie inspiriert sind.

Die algebraische Natur von Alan Kays Vision für Objekte würde es ermöglichen, dass Objekte formale Überprüfungen, deterministisches Verhalten und verbesserte Testbarkeit bieten, da Algebren im Wesentlichen Operationen sind, die einigen wenigen Regeln in Form von Gleichungen gehorchen.

Im Programmierjargon sind Algebren wie Abstraktionen, die aus Funktionen (Operationen) bestehen, begleitet von spezifischen Gesetzen, die durch Einheitstests erzwungen werden, die diese Funktionen bestehen müssen (Axiome/Gleichungen).

Diese Ideen wurden jahrzehntelang in den meisten OO-Sprachen der C-Familie, einschließlich C++, Java, C# usw., vergessen, aber sie finden allmählich ihren Weg zurück in die neueren Versionen der meisten weit verbreiteten OO-Sprachen.

Man könnte sagen, dass die Programmierwelt die Vorteile der funktionalen Programmierung und des durchdachten Denkens im Zusammenhang mit OO-Sprachen wiederentdeckt.

Wie JavaScript und Smalltalk vor ihnen, werden die meisten modernen OO-Sprachen immer mehr zu „Multi-Paradigmen-Sprachen“. Es gibt keinen Grund, sich zwischen funktionaler Programmierung und OOP zu entscheiden. Wenn wir das historische Wesen der beiden Sprachen betrachten, sind sie nicht nur kompatibel, sondern ergänzen sich sogar.

Da sie so viele Gemeinsamkeiten haben, würde ich sagen, dass JavaScript die Rache von Smalltalk an dem Missverständnis der Welt von OOP ist. Both Smalltalk and JavaScript support:

  • Objects
  • First-class functions and closures
  • Dynamic types
  • Late binding (functions/methods changeable at runtime)
  • OOP without class inheritance

What is essential to OOP (according to Alan Kay)?

  • Encapsulation
  • Message passing
  • Dynamic binding (the ability for the program to evolve/adapt at runtime)

What is non-essential?

  • Classes
  • Class inheritance
  • Special treatment for objects/functions/data
  • The new keyword
  • Polymorphism
  • Static types
  • Recognizing a class as a „type“

If your background is Java or C#, you may be thinking static types and Polymorphism are essential ingredients, but Alan Kay preferred dealing with generic behaviors in algebraic form. For example, from Haskell:

fmap :: (a -> b) -> f a -> f b

This is the functor map signature, which acts generically over unspecified types a and b, applying a function from a to b in the context of a functor of a to produce a functor of b. Functor is math jargon that essentially means „supporting the map operation“. If you’re familiar with .map() in JavaScript, you already know what that means.

Here are two examples in JavaScript:

// isEven = Number => Boolean
const isEven = n => n % 2 === 0;const nums = ;// map takes a function `a => b` and an array of `a`s (via `this`)
// and returns an array of `b`s.
// in this case, `a` is `Number` and `b` is `Boolean`
const results = nums.map(isEven);console.log(results);
//

The .map() method is generic in the sense that a and b can be any type, and .map() handles it just fine because arrays are data structures that implement the algebraic functor laws. Die Typen spielen für .map() keine Rolle, weil es nicht versucht, sie direkt zu manipulieren, sondern eine Funktion anwendet, die die richtigen Typen für die Anwendung erwartet und zurückgibt.

// matches = a => Boolean
// here, `a` can be any comparable type
const matches = control => input => input === control;const strings = ;const results = strings.map(matches('bar'));console.log(results);
//

Diese generische Typbeziehung ist in einer Sprache wie TypeScript nur schwer korrekt und gründlich auszudrücken, war aber in Haskells Hindley-Milner-Typen mit Unterstützung für höher geartete Typen (Typen von Typen) ziemlich einfach auszudrücken.

Die meisten Typensysteme waren zu restriktiv, um den freien Ausdruck dynamischer und funktionaler Ideen zu ermöglichen, wie z.B. Funktionskomposition, freie Objektkomposition, Laufzeit-Objekterweiterung, Kombinatoren, Linsen, etc. Mit anderen Worten: Statische Typen erschweren häufig das Schreiben kompatibler Software.

Wenn Ihr Typsystem zu restriktiv ist (z.B. TypeScript, Java), sind Sie gezwungen, komplizierteren Code zu schreiben, um die gleichen Ziele zu erreichen. Das bedeutet nicht, dass statische Typen eine schlechte Idee sind oder dass alle Implementierungen statischer Typen gleich restriktiv sind. Ich bin mit dem Typsystem von Haskell auf weit weniger Probleme gestoßen.

Wenn Sie ein Fan von statischen Typen sind und Ihnen die Einschränkungen nichts ausmachen, dann nur zu, aber wenn Sie einige der Ratschläge in diesem Text schwierig finden, weil es schwierig ist, zusammengesetzte Funktionen und zusammengesetzte algebraische Strukturen zu typisieren, dann geben Sie dem Typsystem die Schuld, nicht den Ideen. Die Leute lieben den Komfort ihrer SUVs, aber niemand beschwert sich darüber, dass man damit nicht fliegen kann. Dafür braucht man ein Fahrzeug mit mehr Freiheitsgraden.

Wenn Einschränkungen Ihren Code einfacher machen, großartig! Aber wenn Einschränkungen Sie dazu zwingen, komplizierteren Code zu schreiben, sind die Einschränkungen vielleicht falsch.

Was ist ein Objekt?

Objekte haben im Laufe der Jahre eindeutig viele Bedeutungen angenommen. Was wir in JavaScript als „Objekte“ bezeichnen, sind einfach zusammengesetzte Datentypen, die weder mit der klassenbasierten Programmierung noch mit Alan Kays Message-Passing etwas zu tun haben.

In JavaScript können diese Objekte Kapselung, Nachrichtenübermittlung, gemeinsame Nutzung von Verhalten über Methoden und sogar Unterklassen-Polymorphismus unterstützen (wenn auch eher über eine Delegationskette als über typbasiertes Dispatch) und tun dies auch häufig. Sie können jeder Eigenschaft eine beliebige Funktion zuweisen. Sie können das Verhalten von Objekten dynamisch aufbauen und die Bedeutung eines Objekts zur Laufzeit ändern. JavaScript unterstützt auch die Kapselung mit Hilfe von Closures für den Implementierungsschutz. Aber all das ist ein Opt-in-Verhalten.

Unsere derzeitige Vorstellung von einem Objekt ist einfach eine zusammengesetzte Datenstruktur, die nichts weiter erfordert, um als Objekt zu gelten. Aber das Programmieren mit dieser Art von Objekten macht Ihren Code genauso wenig „objektorientiert“ wie das Programmieren mit Funktionen Ihren Code „funktional“ macht.

OOP ist nicht mehr echtes OOP

Da „Objekt“ in modernen Programmiersprachen viel weniger bedeutet als für Alan Kay, verwende ich „Komponente“ statt „Objekt“, um die Regeln des echten OOP zu beschreiben. Viele Objekte sind im Besitz von anderem Code in JavaScript und werden direkt von diesem manipuliert, aber Komponenten sollten ihren eigenen Zustand kapseln und kontrollieren.

Echte OOP bedeutet:

  • Programmieren mit Komponenten (Alan Kays „Objekt“)
  • Der Zustand von Komponenten muss gekapselt sein
  • Nachrichtenübermittlung für die Kommunikation zwischen Objekten verwenden
  • Komponenten können zur Laufzeit hinzugefügt/geändert/ersetzt werden

Die meisten Verhaltensweisen von Komponenten können generisch mit algebraischen Datenstrukturen spezifiziert werden. Vererbung ist hier nicht erforderlich. Komponenten können Verhaltensweisen aus gemeinsam genutzten Funktionen und modularen Importen wiederverwenden, ohne ihre Daten zu teilen.

Objekte zu manipulieren oder Klassenvererbung in JavaScript zu verwenden, bedeutet nicht, dass man „OOP“ betreibt. Die Verwendung von Komponenten auf diese Weise schon. Aber der allgemeine Sprachgebrauch ist die Art und Weise, wie Wörter definiert werden, also sollten wir vielleicht OOP aufgeben und dies „Message Oriented Programming (MOP)“ anstelle von „Object Oriented Programming (OOP)“ nennen?

Ist es ein Zufall, dass Mops zum Aufräumen verwendet werden?

Wie gutes MOP aussieht

In der meisten modernen Software gibt es eine Benutzeroberfläche, die für die Verwaltung von Benutzerinteraktionen verantwortlich ist, einen Code, der den Anwendungsstatus (Benutzerdaten) verwaltet, und einen Code, der System- oder Netzwerk-E/A verwaltet.

Jedes dieser Systeme kann langlebige Prozesse benötigen, wie z.B. Event-Listener, Zustände, um Dinge wie die Netzwerkverbindung, den Status von UI-Elementen und den Zustand der Anwendung selbst zu verfolgen.

Gutes MOP bedeutet, dass das System mit anderen Komponenten über den Versand von Nachrichten kommuniziert, anstatt dass alle diese Systeme sich gegenseitig erreichen und ihren Zustand direkt manipulieren. Wenn der Benutzer auf eine Schaltfläche zum Speichern klickt, könnte eine "SAVE"-Nachricht versandt werden, die von einer Zustandsanwendungskomponente interpretiert und an einen Zustandsaktualisierungs-Handler (z. B. eine reine Reduzierfunktion) weitergeleitet wird. Nachdem der Zustand aktualisiert wurde, könnte die Zustandskomponente eine "STATE_UPDATED"-Nachricht an eine UI-Komponente senden, die wiederum den Zustand interpretiert, abgleicht, welche Teile der UI aktualisiert werden müssen, und den aktualisierten Zustand an die Unterkomponenten weiterleitet, die diese Teile der UI behandeln.

In der Zwischenzeit könnte die Netzwerkverbindungskomponente die Verbindung des Benutzers zu einem anderen Rechner im Netzwerk überwachen, auf Nachrichten warten und aktualisierte Zustandsdarstellungen senden, um Daten auf einem entfernten Rechner zu speichern. Sie verfolgt intern einen Heartbeat-Timer für das Netzwerk, ob die Verbindung gerade online oder offline ist, und so weiter.

Diese Systeme müssen nichts über die Details der anderen Teile des Systems wissen. Nur über ihre individuellen, modularen Belange. Die Systemkomponenten sind dekomponierbar und rekomponierbar. Sie implementieren standardisierte Schnittstellen, so dass sie interoperabel sind. Solange die Schnittstelle zufriedenstellend ist, können Sie Ersatzkomponenten einsetzen, die dasselbe auf unterschiedliche Weise oder völlig andere Dinge mit denselben Nachrichten tun können. Man kann dies sogar während der Laufzeit tun, und alles würde weiterhin richtig funktionieren.

Komponenten desselben Softwaresystems müssen sich nicht einmal auf demselben Rechner befinden. Das System könnte dezentralisiert sein. Der Netzwerkspeicher könnte die Daten auf ein dezentralisiertes Speichersystem wie IPFS verteilen, so dass der Benutzer nicht auf den Zustand eines bestimmten Rechners angewiesen ist, um sicherzustellen, dass seine Daten sicher gesichert und vor Hackern, die sie stehlen wollen, geschützt sind.

OOP wurde teilweise von Arpanet inspiriert, und eines der Ziele von Arpanet war es, ein dezentralisiertes Netzwerk aufzubauen, das Angriffen wie Atombomben standhalten kann. Laut dem Direktor der DARPA während der Entwicklung des Arpanet, Stephen J. Lukasik („Why the Arpanet Was Built“):

„Das Ziel war es, neue Computertechnologien zu nutzen, um die Bedürfnisse der militärischen Führung und Kontrolle gegen nukleare Bedrohungen zu erfüllen, eine überlebensfähige Kontrolle der US-Atomstreitkräfte zu erreichen und die militärische taktische und Management-Entscheidungsfindung zu verbessern.“

Anmerkung: Der primäre Antrieb für Arpanet war die Bequemlichkeit und nicht die nukleare Bedrohung, und die offensichtlichen Vorteile für die Verteidigung kamen erst später zum Vorschein. Die ARPA benutzte drei verschiedene Computerterminals, um mit drei verschiedenen Computerforschungsprojekten zu kommunizieren. Bob Taylor wollte ein einziges Computernetz, um jedes Projekt mit den anderen zu verbinden.

Ein gutes MOP-System könnte die Robustheit des Internets teilen, indem es Komponenten verwendet, die während des Betriebs der Anwendung ausgetauscht werden können. Es könnte weiter funktionieren, wenn der Benutzer ein Mobiltelefon benutzt und offline geht, weil er einen Tunnel betreten hat.

Es ist an der Zeit, dass die Softwarewelt das gescheiterte Experiment der Klassenvererbung hinter sich lässt und sich auf die mathematischen und wissenschaftlichen Prinzipien besinnt, die ursprünglich den Geist der OOP definiert haben.

Es ist an der Zeit, dass wir anfangen, flexiblere, widerstandsfähigere und besser komponierte Software zu entwickeln, bei der MOP und funktionale Programmierung harmonisch zusammenarbeiten.

Anmerkung: Das Akronym MOP wird bereits verwendet, um die „überwachungsorientierte Programmierung“ zu beschreiben, und es ist unwahrscheinlich, dass OOP still und leise verschwinden wird.

Seien Sie nicht verärgert, wenn sich MOP nicht als Programmiersprache durchsetzt.
Machen Sie MOP zu Ihrem OOP.

Mehr erfahren auf EricElliottJS.com

Video-Lektionen über funktionale Programmierung sind für Mitglieder von EricElliottJS.com verfügbar. Wenn Sie noch kein Mitglied sind, melden Sie sich noch heute an.