Der Objekte Kern

Erstellt am 28. April 2011 von Ralfwestphal @ralfw

Was ist eigentlich Objektorientierung? Eigentlich sollte das doch klar sein – und doch erhitzen sich die Gemüter immer wieder darüber. Wenn ich Flow-Design vorstelle, höre ich z.B. den Einwand, das sei doch gar nicht mehr objektorientiert. Das ist dann nicht nur Feststellung, sondern auch Kritik. Aber ist das wirklich so? Und ist das kritikwürdig, wenn es denn so wäre?

Ich denke, wir müssen uns die Objektorientierung ein wenig näher ansehen, um das beurteilen zu können. Die scheinbar eine Objektorientierung gibt es nämlich nicht.

Harte Objektorientierung – Technik

Unter Objektorientierung verstehen die meisten Entwickler zunächst einmal Mittel einer Programmiersprache. Es geht also um Technik, um Code. Diese technische Objektorientierung lässt sich für mich so auf das absolut Essenzielle reduzieren:

Hinter einem Interface verborgene allozierbare strukturierte Speicherbereiche.

Jup, das ist es für mich. Mehr ist technische Objektorientierung nicht. Alles andere ist nice to have, aber nicht wirklich essenziell.

Allozierbarer Speicherbereich: Ein Speicherbereich, den Code durch einen Befehl für sich reklamieren kann; der Code erhält ein Handle für diesen Speicherbereich, so dass er ihn benutzen kann. Der Speicherbereich wird nach einem Schema strukturiert – aber auch das ist nicht zentral für die Objektorientierung.
Da der allozierende Code nur ein Handle auf den Speicherbereich bekommt, weiß er nicht, wo genau der Speicherbereich liegt. Er hat also keinen direkten Zugriff auf ihn.

Interface: Eine Menge von Prozeduren und Funktionen. Nur die Prozeduren und Funktionen des bei der Allokation eines Speicherbereichs angegebenen Interface haben Zugriff auf ihn.

Technische Objektorientierung verbindet also Speicher mit Funktionalität, die auf diesem Speicher arbeitet.

Wie passen dazu aber…

  • Klassen: Klassen sind die Kombination von Schema und Interface. Ihre Felder strukturieren Speicherplatz und ihre Methoden bilden das default Interface für die Arbeit mit dem strukturierten Speicherplatz.
    Speicherallokation findet statt unter Angabe einer Klasse. Sie instanziiert eine Klasse zu einem…
  • Objekte: Objekte sind allozierter, hinter einem Interface verborgener Speicher.
  • Felder: Bereiche mit spezieller Semantik im allozierten Speicherbereich. Das Schema für Speicherbereiche besteht aus Felddefinitionen.
    Von außen sichtbare Felder (public, internal) widersprechen der obigen Definition von technischer Objektorientierung.
  • Prozeduren/Funktionen: Objekte kommunizieren via Nachrichten. Prozeduren und Funktionen sind die nachrichtenverarbeitenden Funktionseinheiten auf Interfaces.
    Ob die Kontinuität der Prozedur/Funktionssyntax von C eine gute Wahl zur Definition dieser Funktionseinheiten war, lasse ich mal dahingestellt.
  • Properties: Prozeduren bzw. Funktionen, die suggerieren, dass ein Durchgriff auf den Speicherbereich von außen möglich ist.
    Von außen sichtbare Properties (public, internal) widersprechen nicht direkt der obigen Definition von technischer Objektorientierung, da es sich ja um Methoden handelt. Dennoch ist Vorsicht angezeigt, denn wo Properties ins Spiel kommen, liegt es nahe, dass Schemadetails nach außen sichtbar gemacht werden.
  • Vererbung: Vererbung ist für die obige Definition von Objektorientierung nicht essenziell. Sie ist nice to have, um hier und da Schemata oder Interfaces wiederzuverwenden.
  • Polymorphie: Polymorphie gehört auch nicht essenziell zur Objektorientierung nach obiger Definition. Sie ist nice to have, um denselben Speicherbereich in unterschiedlichen Zusammenhängen auch durch unterschiedliche Interfaces gekapselt angesprechen zu können.
  • Dynamische Programmierung: Ob das Interface, hinter dem ein Speicherbereich versteckt ist, fix oder dynamisch ist, ist unwesentlich für die obige Definition von Objektorientierung. In einigen Situation ist solche Dynamik nice to have.

Von der Wikipedia-Definition der Objektorientierung bleibt für mich also nur die Kapselung als essenziell übrig. Damit möchte ich nicht den Nutzen von Polymorphie oder Vererbung negieren, sondern nur fokussieren. Um was geht es wirklich, wirklich ganz fundamental bei der Objektorientierung? Eben um hinter einem Interface verborgenen nach einem Schema allozierten Speicher. Nicht mehr, nicht weniger.

Ich würde daher eigentlich auch gern den Begriff “Objekt” aus der Objektorientierung herausnehmen. Er ist mir zu suggestiv. Die scheinbare Entsprechung von programmiersprachlichen Objekten zu realweltlichen hat schon viel Schaden angerichtet.

Für mich geht es eher um Funktionale Strukturen oder so, d.h. nach Schema strukturierte Speicherbereiche wie sie schon C und Pascal kannten – die nun aber eben reflexive Funktionalität tragen. Das gab es in C und Pascal nicht.

Ob der Speicherbereich umfangreich oder detailliert strukturiert ist… das hängt von seinem Zweck ab. Er kann 0 Bytes umfassen oder 2 GB; sein Schema kann kein Feld oder hunderte Felder definieren.

Wie der Umfang des Interface aussieht, ist jedoch relevant. Ist das Interface leer und liegt die Struktur damit zwangsläufig offen (sonst hätte die Umwelt ja keinen Zugriff darauf), dann handelt es sich im Grunde nicht mehr um Objektorientierung. Ihr zentraler Zweck, die Kapselung, wird dann ja nicht mehr verfolgt. (Standardfunktionen wie Equals() oder ToString() zähle ich hier mal nicht mit. Das wirkliche Interface eines Objektes beginnt erst jenseits von ihnen.)

Die Entwicklung hin zur Objektorientierung ähnelt mithin ein wenig der Entwicklung primitiven Lebens. Dessen Evolution führte von einzelnen Molekülen über semilebendige Ansammlungen (die heutigen Zellorganellen) zu Zellen. Der entscheidende Schritt war dabei der der Entwicklung einer Membran, d.h. die Schaffung einer Blase. Die Membran oder Zellwand trennt Innenwelt von Außenwelt. Sie entkoppelt das Zellinnere von der Umwelt und ermöglicht damit den Aufbau von Zustand in gewisser Unabhängigkeit.

Das Wesentliche an Objektorientierung ist also, dass sie Speicherplatzdetails (Ort und Schema) von der Umwelt, d.h. seiner Verwendung entkoppelt. Objektorientierung dreht sich um Kapselung, Kapselung, Kapselung. Und alles, was die Kapselung aufweicht, widerspricht ihr.

Nicht, dass ungekapselte strukturierte Speicherbereiche sinnlos wären. Man spricht dann nur besser nicht mehr von Objektorientierung.

Ein weiteres Argument spricht dafür, dass offengelegte Speicherbereiche keine Objekte sind. Die Kommunikation mit ihnen erfolgt dann nämlich nicht mehr über Nachrichten. Sie kann es nicht, weil Nachrichten Funktionalität zur Verarbeitung brauchen. Eine Nachricht selbst besteht ja nur aus Daten. Ohne passende Interfacemethode können die aber nicht verarbeitet werden. Daten direkt in einen Speicherbereich zu legen, ist keine Funktionalität, die ein Objekt bräuchte; das konnten schon C und Pascal.

Technisch essenzielle Objektorientierung braucht also sehr wenig. Sie kann mit einer Untermene von C# oder Java betrieben werden. Klassen, Methoden, private/protected Felder, Interfaces – das ist es. Vererbung, Properties… nicht nötig. Für den Zweck der Kapselung reichen wenige Mittel. Alles andere ist nice to have; wir sollten uns darüber nicht die Köpfe heiß reden. Das wäre am wirklich spannenden Thema vorbei. Das ist nämlich: Wie sollte mit technischer Objektorientierung umgegangen werden?

Weiche Objektorientierung – Methode

Von der technischen Objektorientierung ist zu trennen der Umgang mit ihr. Technische Objekte sind “leer”, d.h. sie geben nicht vor, wie man sie benutzt. Technische Objekte sind sozusagen nur “kleine C Programme” (d.h. Einheiten von Daten und Funktionalität). Die Frage ist also: Wie sollte ein Problem in viele, viele “kleine C Programme” zerlegt werden, damit etwas qualitativ hochwertiges herauskommt?

Dazu gibt es mehrere Ansätze. Der am weitesten verbreitete ist der von Objektorientierte Analyse und Design (OOAD) – oder noch nicht einmal. Verbreitet ist eher eine stark abgespeckte Version davon, sozusagen OOAD ultra light (OOADUL) :-)

Das primäre Entwurfsmittel von OOADUL – wenn denn überhaupt expliziter Codeentwurf stattfindet – ist das Klassendiagramm. Und das primäre Vorgehen ist die Suche nach Substantiven in Anforderungen. Bei einem Tic Tac Toe Spiel würden sofort Spiel, Spieler, Spielbrett, Spielstein als Objektkandidaten identifiziert werden – um dann auf ihnen irgendwie erstens Zustand und zweitens Funktionalität zu verteilen.

Die Prämisse bei OOADUL lautet: Software besteht aus Objekten, die nahe an der realen Welt sind. Was in der Problemdomäne zu sehen und anzufassen ist, das liegt nahe in problemlösenden Software als Objekte repräsentiert zu sein.

An dieser Stelle will ich diesen Ansatz nicht bewerten, sondern ihn nur darstellen. Mir ist im Augenblick wichtiger, ihn als Methode zu trennen von der technischen Objektorientierung.

Zusammenschau

Konzeptionelle/methodische Objektorientierung und technische Objektorientierung sind zwei Paar Schuhe. Man kann auch methodisch objektorientiert Software planen – und dann mit einer technisch nicht objektorientierten Programmiersprache umsetzen. Und umgekehrt.

Technische Objektorientierung ist ein leeres oder neutrales Werkzeug. Es dient durch die Kombination von Daten und Funktionalität im Verein mit der konsequenten Kapselung dem wohl ältesten Prinzip der Softwareentwicklung: der Entkopplung. “Neumodischer Krams” wie Properties oder auch Altmodisches wie öffentliche Felder widersprechen dem jedoch. Deshalb kann man sie trotzdem anwenden – muss nur eben wissen, dass man sich damit jenseits der Objektorientierung bewegt.

Zurück zum Ausgangspunkt: die oft erhitzten Gemüter. Wo sich nun die Gemüter über die Objektorientierung erhitzen, da sollte als erstes gefragt werden, worum es geht. Erhitzt man sich über die Methode oder die Technik?

Mein Gefühl ist, dass die Diskussionen sich weniger um technische Objektorientierung drehen. Die, die am Wert der technischen Objektorientierung zweifeln, treffen einfach relativ selten auf Freunde der technischen Objektorientierung. Am Ende ist sie ja auch unkritisch. Solche Daten-Funktionalität-Blasen zu haben, ist einfach eine nützliche Sache. Warum darauf verzichten.

Viel schwieriger und inzwischen kontroverser ist jedoch, wie mit diesen Blasen umgehen? Hitzige Diskussionen entstehen, wenn die Methode angezweifelt wird. (Oder auch, wenn es um die Methode geht, aber jemand glaubt, es ginge um die Technik.)

Auch ich wende mich mit Flow-Design gegen die Methode und nicht gegen die Technik. Zukünftig will ich das noch besser deutlich machen, um Missverständnissen vorzubeugen. Flow-Design ist eine Methode, um Software zu entwerfen. Und Event-Based Components (EBC) sind eine Übersetzung von Flow-Designs in C# Code; da geht es also um die Nutzung objektorientierter Technik.

Wer Flow-Design für kritikwürdig hält, weil es nicht der (oder seiner Sicht auf die) Objektorientierung entspricht, der muss sich also mit seinen Argumenten auf der methodischen Ebene bewegen. Pauschal zu sagen, Flow-Design sei nicht objektorientiert, ist mithin falsch. Flow-Design ist nur methodisch nicht objektorientiert; und mit der Technik hat Flow-Design nichts zu schaffen.

Und wer Event-Based Components als nicht objektorientiert kritisiert, der muss sich auf Technik konzentrieren. Er muss zeigen, inwiefern EBC der technischen Objektorientierung widerspricht. Und das wird schwer, würde ich sagen :-)