 Ja, herzlich willkommen zum Programmierparadigmen. In diesem Modul geht es um Datenabstraktion und Objektorientierung, also der Frage, wie Programmiersprachen überhaupt erlauben Daten und die gemeinsame Eigenschaften von mehreren Daten und vielleicht auch das Verhalten, was mit diesen Daten assoziiert ist, eleganz zu beschreiben und zu abstrahieren. Wir werden uns da eine ganze Reihe von Konstrukten und Konzepten dazu anschauen und das Ganze erfolgt in fünf Teilen, von denen Sie jetzt hier gerade den ersten sehen. Fangen wir vielleicht mal an mit der Frage, was ist überhaupt Datenabstraktion? Also was ist das Ziel dieser ganzen Geschichte? Im Speicher eines Programms gibt es ja verschiedene Speicherobjekte und oft gibt es mehrere Speicherobjekte, die viele Dinge gemeinsam haben, zum Beispiel eine gemeinsame Struktur haben und außerdem noch Verhalten, was mit diesen Datenobjekten assoziiert ist. Eine Art und Weise, wie man das üblicher Weise abstrahiert, ist in abstrakte Datentypen. Sie haben das sicherlich schon mal irgendwo gesehen. Also die Idee ist, dass wir eine Menge von Werten und darauf definierte Operationen haben und das dann als ein Konstrukt in unser Programmiersprache betrachten wollen. Ein klassisches Beispiel, wo man das zum Beispiel machen möchte, ist, wenn man einen Stack beschreibt, denn ein Stack besteht ja aus Daten, die einfach übereinander gestapelt werden und dann auch entsprechend im Speicher repräsentiert sind und eine Reihe von Operationen, nämlich um diese Daten zu manipulieren oder vielleicht auch zu inspizieren, zum Beispiel Push, um weitere Daten auf den Stack hinzuzufügen oder Pop, um Daten runterzunehmen oder sowas wie Size oder Length, um zu schauen, wie groß der Stack denn aktuell ist. Und solche Typen wollen wir eben abstrahieren in abstrakte Datentypen. Die bei weitem populärste Art und Weise, wie man das in Programmiersprachen heutzutage macht, sind Klassen und das ist auch das, worum es in diese Module Veranstaltung hauptsächlich gehen soll. Also Klassen sind eine Form, mit der man abstrakte Datentypen beschreiben kann und üblicherweise haben die zwei Arten von Members, die eben genau diese beiden Dinge, die ich gerade genannt habe, beschreiben können, nämlich zum einen die Fields oder die Felder, mit denen man die Daten, die überhaupt in diesem abstrakten Datentypen drin sind, beschreiben kann, also im Falle eines Stacks, zum Beispiel die verschiedenen Elemente, die auf dem Stack gespeichert sind und dann zum anderen die Methoden, die eben das Verhalten beschreiben, was man dann auf diesen Daten ausführen kann, also Dinge wie zum Beispiel auf den Stack was hinzufügen oder wieder etwas herunternehmen. Das tolle an so einer Klasse ist, dass die Implementierung vom Interface getrennt wird. Das heißt also, wie genau die Pop oder Push-Methode vielleicht implementiert ist oder wie genau die Daten eigentlich gespeichert sind werden, das wird dem Benutzer dieser Klasse eigentlich gar nicht so genau gezeigt und alles, was der Benutzer dieser Klasse wissen muss, ist, dass es da diese Felder und diese Methoden gibt. Manche von dem können vielleicht auch noch versteckt sein, das sehen wir dann später noch und ja, aber der Benutzer der Klasse muss nicht wissen, wie genau die Klasse implementiert ist. Ein weiterer Vorteil ist, dass man mithilfe von Verärgung Code sehr einfach wiederverwenden kann und auch, dass werden wir noch genauer anschauen, aber die Grundidee ist, dass eine Klasse eine andere Klasse erweitert und dadurch den Code der erweiterten Klasse dann wiederverwendet. Ein weiterer Begriff, der in dem Zusammenhang sehr wichtig ist und der aber nicht genau das gleiche ist wie Klassen oder Klassenbasiertes Programmieren, ist der des objekte-orientierten Programmierens. Objekte-orientiertes Programmieren bedeutet im Prinzip einfach nur, dass die meisten Daten, die ich habe, in einem sogenannten Objekt gespeichert sind, also einer Datenentität, die gewisse Felder hat, in der dann weitere Daten gespeichert sind und diese Objekte können aber müssen nicht Instanzen von Klassen sein. Also in der Klassenbasierten Programmiersprache zum Beispiel Java oder C++ erstelle ich Instanzen von Klassen und diese Instanzen sind dann Objekte, das heißt es gibt selbstverständlich Klassenbasierte Programmiersprachen, die auch objekte-orientiert sind. Das muss aber nicht so sein, es gibt auch objekte-orientierte Programmiersprachen, die keine Klassen haben und wo die Objekte zum Beispiel im Tipp von sogenannten Prototypen erstellt werden. Beispiele hier werden javas gibt oder smalltalk. Auch da habe ich Objekte, die genauso wie in der Klassenbasierten Sprache Felder enthalten und die meisten Daten werden dann einfach innerhalb dieser Objekte und deren Felder gespeichert, aber es gibt eben keine Klassen, sondern irgendein anderes Konstrukt zum Beispiel Prototypen, um diese Objekte zu erstellen. Was alle objekte-orientierten Sprachen in der Regel gemeinsam haben, ist, dass Objekte-Methoden auch anbieten und ich dann auf diesen Objekten diese Methoden ausführen kann, also das Verhalten ausführen kann, was mit den Daten assoziiert ist. Programmiersprachen gibt es ja schon etwas länger, deswegen ist es manchmal ganz interessant, sich die Geschichte ein bisschen anzuschauen, einfach zu schauen, wo kommen diese ganzen Ideen, die wir heute vielleicht tagtäglich verwenden denn eigentlich her. Diese Idee von Objekte-Ontierung und Klassen ist ursprünglich in Simula entstanden, das war eine Sprache, die in den 60ern in Norwegen entwickelt wurde und die heute als die erste objekte-orientierte Programmiersprache überhaupt gilt. Bis dahin gab es diese Idee nicht, aber mit Simula wurde das Ganze dann eingeführt und das waren relativ viele von den Konstrukten, die wir heute auch in moderneren Sprachen kennen, schon vorhanden, insbesondere eben Klassen, Objekte und auch Vererbung, mit denen man eben dann Klassen miteinander verknüpfen und Kot aus einer Klasse in einer anderen Klasse wieder verwenden kann. Nach Simula gab es dann irgendwann Smalltalk, das wurde in Xerox Park entwickelt und hat diese ganzen Ideen, die in Simula eingeführt wurden, deutlich weiterentwickelt und insbesondere eben auch die Idee gebracht, dass eine Sprache, die Objekte-Ontiert ist, nicht unbedingt auch gleichzeitig Klassen haben muss, sondern was da passiert ist, dass wir Objekte haben, die sogenannte Nachrichten hin und her schicken, das ist so ähnlich wie Methoden aufrufen. Und eine andere Neuheit, die hier drin ist, ist, dass die Sprache dynamisch typisiert ist. Das heißt also, der Typ einer Variable steht nicht unbedingt statisch fest, wie man das heutzutage vielleicht von Java oder C++ kennt, sondern kann sich zur Laufzeit auch mal ändern. Und aus dieser Geschichte, also aus Simula, dann Smalltalk, sind die Sprachen entstanden, die man heutzutage so als Objekte-Ontierte oder Klassenbasierte Sprachen kennt, nämlich C++, Eiffel oder Ada oder noch ein bisschen moderner dann Java und Csharp. So, nach dieser kleinen Einführung schauen wir uns mal an, was wir jetzt überhaupt in diesem Modul alles machen wollen. Also es wird fünf Teile geben, die hier mal aufgelistet sind. Zunächst schauen wir uns mal ein bisschen genauer an, was eigentlich Encapsulation oder Capsulung und Information Hiding bedeutet, weil das zwei der grundlegenden Konzepte von Objekte-Ontierte-Programmierung sind. Dann schauen wir uns ein bisschen genauer an, wie Vererbung eigentlich funktionieren kann und was es da alles zu beachten gibt. Dann soll es um Initialisierung und Finalisierung von Objekten gehen, also der Frage, wie die überhaupt genau erstellt werden und wie die auch wieder zerstört oder finalisiert werden und was ja wie das Ganze mit der Vererbung zusammenhängt, weil da gibt es interessante Interaktionen. Im vierten Teil des Moduls geht es dann um Dynamic Method Binding. Also der Frage, wie überhaupt entschieden wird, welche Methode jetzt aufgerufen wird, wenn der Typ das Objekt vielleicht nicht statisch klar ist oder wenn vielleicht der Laufzeit Typ eines Objekts nicht gleich dem Typen ist, den die Variable hat. Und schlussendlich am Ende geht es dann um ein bisschen komplexere Formen von Vererbung, wo eben nicht nur von einer Klasse vererbt wird, sondern mit Hilfe von Mixins oder Mehrfachvererbung von mehreren anderen Datentypen geerbt werden kann. Fangen wir mal mit dem ersten Begriff an, nämlich Encapsulation beziehungsweise auf Deutsch Kapslung. Also die Grundidee von dieser Encapsulation ist, dass wir verschiedene Daten und Operationen, die semantisch irgendwie zusammengehören, innerhalb von einer Entität zusammenführen und quasi an einer Stelle beschreiben. Und diese eine Stelle ist typischerweise eine Klasse, in der wir dann eben die Members der Klasse, also die Felder und die Methoden festlegen, die dann diese Daten beziehungsweise Operationen beschreiben. Es gibt da immer zwei Ebenen, die man unterscheiden muss bei der Frage, wo genau jetzt Daten oder Operationen anzufinden sind, nämlich einmal die Ebene der Instanzen, also der einzelnen Objekte, die ich aus der Klasse erstellen kann. Und dann zum anderen der Ebene der Klasse selbst, wo Dinge gespeichert werden, die alle Objekte dieser Klasse betreffen. Also auf dem Instanzlevel kann ich dann z.B. Felder oder auch Verhalten definieren, was individuell für die einzelnen Instanzen gelten soll. Aber alles, was über diese Instanzen hinweg gilt und einfach nur mit der Klasse zu tun hat, das wird auf dem Klassenlevel dann beschrieben. Als kleines Beispiel haben wir hier mal eine Klasse in C++, die heißt Account und beschreibt ein Konto bei einer Bank. Was wir hier sehen sind zum einen die Felder dieser Klasse und dann zum anderen Methoden beziehungsweise auch ein Konstrukte, der mit dieser Klasse assoziiert ist. Und diese Unterscheidung zwischen Instanz und Klassenlevel sieht man hier an diesen Feldern ganz gut. Also es gibt in dem Fall zwei Felder, nämlich die Kontonummer und den Namen der Person, die dieses Konto hat, die auf dem Level der Instanz festgelegt sind. Das heißt, jeder Bankaccount hat eine andere Nummer und hat auch einen anderen oder einen eigenen Namen, der damit assoziiert ist. Und dann gibt es aber auch dieses Feld hier oben, was die Zinsrate festlegt und das wird auf dem Klassenlevel festgelegt und unter der Annahme, dass alle Konten diese Art eben genau dieselben Zinsen anbieten und das deshalb eine Eigenschaft ist, die sozusagen über alle Accounts hinweckelt. Was wir hier noch haben ist zum einen der Konstrukte, der uns einfach erlaubt, ein neues Konto anzulegen, wo wir dann die Kontonummer und den Namen angeben müssen und das Ganze wird dann in die entsprechenden Felder gespeichert. Und dann haben wir hier noch so eine Methode Display, die im Prinzip einfach nur den Zustand dieses Kontos ausgibt und außerdem auch diese Zinsrate ausgibt, die dann eben für alle Accounts gleich sein müsste. Hier unten sehen wir, damit das Ganze benutzt werden kann. Also wir setzen einmal diese Zinsrate hier fest und zwar global sozusagen oder auf dem Klassenlevel für alle Instanzen dieser Klasse und stellen dann zwei konkrete Instanzen, nämlich einmal ein Konto für Joe und ein Konto für Mary mit einer entsprechenden Kontonummer und geben dann aus, wie der Zustand dieser beiden Objekte ist, indem wir diese Display-Methode aufrufen. Wenn wir das Ganze jetzt mal kompilieren und ausführen, dann können wir noch sehen, was das Ganze dann tut. Nämlich, dann ist das wir hier zweimal Display aufrufen, wird dann zweimal die Account-Nummer ausgegeben, die für Joe und die für Mary dazu dann der Name und anschließend noch die Zinsrate, die aber für beide gleich ist, weil das eben auf der Instanzebene festgelegt ist und das fällt einfach für alle Instanzen dieser Klasse gleich ist. Der zweite große Vorteil neben der Kapselung, die wir gerade gesehen haben, den Objekteorientierung und klassenorientierte Programmierung anbietet, ist das sogenannte Information Heiding. Also die Grundidee hier ist, dass bestimmte Information, die für jemanden, der eine Klasse benutzt, nicht relevant sind und deswegen vor diesem Benutzer der Klasse versteckt werden. Insbesondere versteckt man gerne zwei Sachen, nämlich zum einen, wie genau die Daten gespeichert werden, also ob ich jetzt zum Beispiel den Namen des Konto-Inhabers als String oder als Array aus Characters oder in irgendeiner anderen Art und Weise speichere, das wird versteckt und zum anderen auch das Verhalten, was diese Klasse anbietet. Also wenn es jetzt zum Beispiel eine Methode gäbe, die die Zinsen über einen bestimmten Zeitraum ausrechnet, dann gibt es vielleicht verschiedene Artenweisen, wie man das genau implementieren kann und wie genau das implementiert ist, das versteckt die Klasse, so das Dänige, der die Klasse benutzt, das am Prinzip gar nicht sieht und auch nicht wissen muss, sondern die Methode einfach aufruft und weiß, was am Ende rauskommen soll, aber eben nicht, wie genau das berechnet wird. Und der große Vorteil davon ist, dass man die Implementierung einer Klasse dann ändern kann, ohne dass man die Benutzer, also die Clients der Klasse anpassen muss, denn die haben sozusagen dieses Interface dazwischen, diese Schnittstelle, die beschreibt einfach nur, welche Typen von Daten, also Eingabereien gehen und welche Typen von Daten rausgehen und implizit gibt es dann natürlich auch noch eine Übereinkunft darüber, was diese Methode eigentlich machen soll, aber wie genau sie das macht, das kann geändert werden, ohne dass man dann die Clients, die Benutzer dieser Klasse entsprechend anpassen müsste. Eine besondere Form von dieser Idee des Information Hidings sind sogenannte Getter und Setter. Wer schon ein bisschen mehr Mediabere programmiert hat, hat die sicherlich schon mal gesehen und sich vielleicht manchmal auch gefragt, warum man die überhaupt braucht, weil die in vielen Fällen ja ein bisschen überflüchtig scheinen zumindest am Anfang. Die Grundidee ist, dass man hier eben verstecken kann, wie genau Daten gespeichert sind. Das heißt, anstatt dass ich ein Feld direkt den Clients der Klasse zur Verfügung stelle, sodass die Clients direkt in dieses Feld schreiben und das Feld direkt wieder auslesen, biete ich zwei Arten von Methoden an, nämlich zum einen diese Getter Methods, die den aktuellen Wert, der in diesem Feld gespeichert ist, zurückgeben und eine Setter Method, die erlaubt diesen Wert zu überschreiben. Und der große Vorteil an diesen Gettern und Settern ist, dass ich die Art und Weise, wie genau die Daten gespeichert sind, jetzt vor den Clients verstecken kann. Das heißt, wenn ich mir späte überlege, dass ich das Feld vielleicht in einem anderen Typen oder auf eine andere Art und Weise speichern möchte, oder dass die Daten, die in diesem Feld sind, vielleicht gar nicht lokal als Instanzfeld gespeichert werden sollen, sondern vielleicht ob irgendeinem anderen Rechner gespeichert werden oder dass bestimmte Dinge, die ich lese, gecached werden und so nicht jedes Mal aus dem Feld komplett ausgelesen werden müssen, dann kann ich das alles machen, weil die Getter und Setter die Art und Weise, wie die Daten genau gespeichert sind, eben abstrahiert. Während man den manchen Sprachen wie zum Beispiel Java, diese Getter und Setter in der Regel explizit programmieren muss oder von seiner IDE vielleicht generell ein Nest, gibt es auch andere Sprachen, die das Ganze noch ein bisschen eleganter lösen, nämlich mit so genannten Properties oder Accessors. Also die Idee ist hier, dass man Felder und Getter und Setter im Prinzip transparent macht für den Klein. Das heißt, der Benutzer in der Klasse sieht gar nicht, ob er jetzt wirklich mit einem Feld interagiert oder mit so einem Getter oder Setter, weil beides aus Sicht des Kleines gleich aussieht, aber diese bestimmten, diese speziellen Accessor Methods eben dann doch ein Getter bzw. ein Setter implementieren. Hier unten sind wir mal ein kleines Beispiel in C sharp, also eine Sprache, die diese Accessors anbietet. Was die Klasse macht ist, eine Zeiteinheit zu repräsentieren, die intern in Sekunden gespeichert ist, aber aus Sicht der Klein gibt es auch ein Feld namens hours, also der Anzahl der Stunden. Ich zeig vielleicht erstmal, wie das aus Klein Sicht aussieht. Also hier oben sehen wir so ein bisschen Kleincode, der diese Time-Klasse verwendet und was der Klein hier machen kann, ist, der erstellt sich so eine Instanz dieser Klasse und setzt dann dieses Feld oder sie zumindest so aus, als würde er direkt dieses Feld setzen und sagt, die Zeit sind jetzt fünf Stunden und kann das Feld dann natürlich auch entsprechend wieder auslesen. Was aber eigentlich passiert ist, dass stattdessen diese Getter und Setter, die hier definiert sind ausgeführt werden, also wenn ich die Anzahl der Stunden auslese, dann wird eigentlich die Anzahl der Sekunden gelesen und dann entsprechend in Stunden umgerechnet und genauso, wenn ich das Feld beschreibe, dann wird eigentlich dieser Setter hier unten ausgeführt, indem noch überprüft wird, ob der Wert tatsächlich legal ist und anschließend wird der eigentliche Wert, der dann wirklich mit der Instanz in der Instanz gespeichert wird, in Sekunden gespeichert. Das heißt, wir haben diese zwei Felder, die eigentlich auf dieselben Daten zugreifen und das ganze geschieht aber transparent für den Kleint, sodass beides wie ein Feld aussieht, dieses Ours aber eigentlich ein Excessor ist. Um diese Idee des Information Hidings gut implementieren zu können, ist es oft nötig oder praktisch, wenn man bestimmte Dinge verstecken kann. Also wenn ich Teile der Felder oder auch der Methoden, die eine Klasse hat, eben nicht für alle Kleins sichtbar mache. Und genau das ist, was man mit den Visibilities oder Sichtbarkeiten in vielen Sprachen machen kann, denn die Erlaubnis im Prinzip zu sagen, dass bestimmte Members, also bestimmte Felder oder bestimmte Methoden eben nicht für alle Kleins dieser Klasse oder vielleicht nur in bestimmten Situationen tatsächlich sichtbar sind. So in der einfachsten Form gibt es im Prinzip zwei Sichtbarkeiten, nämlich Private und Public. Wobei Private bedeutet, dass dieses Feld oder diese Methode nur für die Klasse selbst sichtbar ist, also das ist sozusagen intern. Und Public bedeutet, dass das Feld oder die Methode für alle Benutzer dieser Klasse sichtbar sind. Wir sehen gleich noch ein bisschen Verfeinerung von diesen zwei Arten, die es in einen Gesprachen gibt. Was wichtig ist, ist, dass man grundsätzlich versuchen sollte, nur die Methoden und Felder als Public zu deklarieren, also wirklich für alle Sichtbar zu machen, wenn es wirklich nötig ist. Das heißt, wenn es geht, immer alles Private halten, denn das maximiert im Prinzip die Anpassbarkeit der Klasse an, spätere Anforderungen, die im Laufe der Entwicklung des Programms dann irgendwann vielleicht noch auftreten, ohne dass man die Kleins ändern muss. Denn alles, was Private ist, sieht der Kleint ja nicht. Das heißt, ich kann das alles ändern, ohne dass die Kleins das sehen wir uns mitbekommen können. Wogegen alles, was irgendwann mal Public gemacht wurde, von irgendeinem Kleint irgendwo benutzt werden kann und dann natürlich nur entsprechend vorsichtig angepasst werden kann, weil das Potenzial ja sonst den Code der Kleins kaputt macht. Jetzt habe ich gerade schon gesagt, dass es in manchen Sprachen noch ein bisschen Verfeinerung von diesem einfachen Private-Public-Bild der Sichtbarkeiten gibt. Und hier sind einfach mal zwei Beispiele. Also in Java gibt es neben Private und Public, die da so definiert sind, wie ich es gerade gesagt habe, auch noch Default und Protected. Default-Visibility ist ganz einfach das, was man bekommt, wenn man nichts dran schreibt. Also wenn ich eine Klasse aufschreibe und die weder als Private, Public oder Protected deklariere, dann ist die automatisch in der Default-Visibility. Und was das bedeutet ist, dass dieses Feld oder diese Methode innerhalb desselben Paket komplett sichtbar ist, aber außerhalb überhaupt nicht. Also sozusagen wie Public, aber nur innerhalb des aktuellen Pakets. Protected bedeutet ein bisschen was anderes. Es ist so ähnlich, denn es bedeutet auch, dass dieses Feld oder diese Methode innerhalb desselben Pakete sichtbar ist, aber außerdem auch in allen Unterklassen, egal ob diese Unterklassen im selben Paket sind oder nicht. In C++ gibt es auch noch zwei Verfeinerungen. Die eine heißt interessanterweise auch Protected, bedeutet aber nicht genau dasselbe wie in Java. Also wer zwischen diesen zwei Sprachen manchmal hin und her springt, muss da gut aufpassen. Denn in C++ bedeutet Protected, dass das Ganze in der selben Klasse sichtbar ist und in allen Unterklassen, aber eben nicht wie in Java im selben Paket. Und anderen deshalb, weil es die Art von Paketen, so wie man sie aus Java kennt, in C++ auch gar nicht gibt. Also muss man aufpassen, ist genau dasselbe Keyword, aber bedeutet eben nicht genau das gleiche. Und dann gibt es noch die sogenannten Friend-Klases. Das ist ein spezielles Konstrukt, was C++ anbietet, um die Visibilities in bestimmten Fällen anzupassen. Also wenn man quasi eine Visibility so als den Standard oder Default haben möchte, aber für bestimmte Klassen dann eben doch noch mehr Sichtbarkeit ermöglichen möchte, dann kann man das mit Hilfe dieser Friend-Klassen machen. Denn was Friend ermöglicht, ist, dass eine Klasse, einer speziellen anderen Klasse Zugriff auf ihre privaten oder Protected Members gibt und sagt, diese eine Klasse kann eben auch auf die ganzen anderen Sachen zugreifen, aber alle anderen Clients von mir, bitte nicht. So, zum Abschluss dieses ersten Teils des Moduls, haben wir noch ein kleines Quiz und zwar wieder in der Form von vier Sätzen, die vielleicht stimmen, vielleicht auch nicht. Und was ich sie jetzt bitten würde zu tun ist, das Video sich an der Stelle anhalten, im Ilias abstimmen, um zu testen, ob sie herausfinden, welche Sätze hier richtig sind und welche falsch sind und das Video danach dann erst fortsetzen. Gut, schauen wir mal die Lösung an. Also von diesen vier Sätzen waren drei falsch und zwar die drei am Ende. Der erste Satz ist richtig, also Kapselung ist tatsächlich die Idee, dass man miteinander verwandte Daten und die Operationen darauf an einer Stelle bündelt und gleichzeitig die nicht relevanten Details vor den Clients dieses Datentyps versteckt. Das ist die Idee von Kapselung, wobei der zweite Teilsatz eigentlich so ein bisschen die Idee von Information Hiding ist, genau gesagt. Der zweite Satz, der stimmt so nicht, denn die Idee von der Abstraktion in Klassen ist eben, dass die Clients einer Klasse sich nicht jedes Mal daran angepasst werden müssen, wenn die interne Repräsentation der Daten, die diese Klasse speichert, verändert wird, sondern dass wir das eben gerade verstecken, sodass die Clients da nicht angepasst werden müssen. Protected bedeutet in Java und C++ nicht dasselbe, haben wir gerade auf der letzten Folie gesehen, da gibt es einen Fein, aber manchmal einen wichtigen Unterschied und schlussendlich der letzte Satz stimmt auch nicht. Also Class-Level-Members sollten nicht immer public sein. Es gibt durchaus Szenarien, wo man einen Class-Level-Field zum Beispiel als Private-Field deklarieren möchte, wenn das zwar wichtig für alle Instanzen dieser Klasse ist, aber eben nicht wichtig für die Clients der Klasse. Ja, und dann sind wir auch schon am Ende vom ersten von fünf Teilen in diese Modul-Datenabstraktion. Ich hoffe, Sie wissen jetzt ein bisschen besser, was Capsulung und Information Hiding denn eigentlich ist. Und ja, in den nächsten vier Teilen schauen wir uns dann verschiedene Aspekte von Datenabstraktion und Objektorientierung noch ein bisschen genauer an. Bis dann, erst mal Danke fürs Zuhören und bis zum nächsten Mal.