Grundlagen der Informatik 2, Vorlesung 8: Zugriffskontrolle Sebastian Küpper https://www.youtube.com/watch?v=bx27H9423qk Transkript (automatisch erstellt) 0:00 Hallo liebe Fernstuden und herzlich willkommen zur achten Vorlesung zum Kurs Grundlagen der Informatik 2. In dieser Vorlesung werden wir uns mit 0:08 Zugrskontrollen befassen. Sie haben ja bereits in der Vergangenheit gesehen, dass wenn wir Zugriff erlauben wollen auf bestimmte Attribute oder Methoden, 0:17 wir hierfür Schlüsselwörter verwenden müssen, wie beispielsweise public. Und in dieser Vorlesung wollen wir einmal systematisch darüber sprechen, 0:25 welche Schlüsselwörter es gibt und welche Bedeutungen diese in C#ARP haben. Wir beginnen mit der Diskussion der Zugriffskontrolle auf der obersten 0:36 Ebene, auf der Zugriffskontrolle eine Rolle spielt und zwar bei den Namensräumen oder auch in anderen Programmiersprachen manchmal Pakete 0:44 genannt. Also in Java heißen die z.B. Pakete. In C#ARP können Klassen in Namensräumen gebündelt werden. Das heißt, wir können 0:52 Namensrä Räume definieren und darin verschiedene Klassen anbieten. Wir definieren einen Namensraum über das Schlüsselwort Namespace, das oberhalb 1:01 der Klasse angegeben werden muss. Also, wenn wir eine Klasse in einen Namespace packen wollen, dann müssen wir oberhalb der Klasse schreiben Namespace und dann 1:09 den Namen des Namensraums schreiben. Namespaces dienen dazu, dass man einen Gültigkeitsrahmen für Namen festlegen kann. Also, wenn wir einen Klassennamen 1:21 verwenden innerhalb eines Namensraums, dann kann es nicht eine zweite Klasse haben, die den gleichen Namen trägt im gleichen Namensraum. Aber wenn wir einen 1:28 anderen Namensraum nutzen, dann können wir den gleichen Namen wieder verwenden. Das heißt, wenn wir einen ersten Namensraum A haben und einen zweiten 1:37 Namensraum B haben, dann können wir sowohl in A als auch in B verschiedene Klassen namens vielleicht K haben, also der gleiche Name für die Klasse, sowohl 1:47 in Namensraum A als auch in Namensraum B. Und diese beiden Klassen haben dann nichts miteinander zu tun. Wohingegen, wenn man innerhalb eines Namensraums 1:55 arbeitet, dann kann es immer nur eine Klasse mit einem Namen geben. Nun ist es aber natürlich so, dass man äh eventuell nicht nur innerhalb eines 2:05 Namensraums arbeiten möchte und man kann auf Klassen anderer Namensräume zugreifen. Wir können das entweder machen, indem wir die Namensräume 2:16 explizit vorher erwähnen, also wenn wir jetzt z.B. Beispiel von vorhin nehmen, dass wir in Namensraum A und in Namensraum B jeweils eine Klasse K 2:23 haben. Dann können wir sowas schreiben wie A.K oder B.K, und K, um auf die jeweilige Klasse aus dem Namensraum A oder B zuzugreifen. Außerdem können wir 2:32 auch sagen, dass wir einen Namensraum komplett einbinden. Dafür können wir oben in der Klassendatei schreiben using und dann den Namen des jeweiligen 2:41 Namensraums. Dadurch wird die Klasse, in der wir gerade arbeiten, nicht in den Namensraum hinzugefügt, aber wir können alle Klassen, die in diesem Namensraum 2:50 definiert sind, anschließend verwenden, als wären sie im gleichen Namensraum. Das heißt, wir müssen sie nicht mehr qualifizieren. Wir könnten also, wenn 2:57 wir den Namensraum A einbinden, einfach den Namen K verwenden, ohne dass wir sagen, dass wir das K aus Namensraum A nehmen. Zumindest solange wir nicht auch 3:07 noch einen anderen Namensraum einbinden, indem der gleiche Name auch definiert wird. Schauen wir einmal ein Beispiel dafür 3:15 an, wie ein Namensraum eingebunden wird. Wir definieren hier eine Klasse Order in dem Namensraum Customer und wir wollen in der Klasse Order die Klasse Article 3:26 List nutzen. Und die Klasse Articleist ist aber nicht Teil des Nampaces Customer, sondern des Namensraums Flowershop. Was wir also machen können, 3:36 ist, dass wir oben eine Using Direktive angeben und wir sagen using flowershop.articlist. Wenn wir das machen, dann können wir im 3:44 Rest der Datei auf den Namen Articellist zugreifen, ohne da jeweils Flowershop davorzuschreiben. Das sehen wir hier jetzt in dem 3:53 Beispielscode. Was wir auch tun könnten, wäre den gesamten Namensraum Flowershop einzubinden. Dann würden wir einfach nur schreiben using Flowershop. Allerdings 4:02 hätte das zurfolge, dass wirklich alles, was in dem Namensraum ist, eingebunden würde. Und das hieße jetzt für uns hier, dass wir bei unserer Namensgebung darauf 4:10 achten würden, dass wir nicht nur keine Kollision zu den konkret eingebundenen Klassen haben, sondern dass wir keine Kollision zu irgendeiner Klasse in dem 4:18 Namensraum haben. Und darum ist es häufig besser, wenn man nur einzelne Klassen einmittet, zumindest wenn man nicht sowieso sehr viel Funktionalität 4:26 aus dem anderen Namensraum benötigt. Hier sehen wir jetzt noch einmal, wie das dann tatsächlich aussieht im Code. Wenn wir einen ganzen Namensraum 4:35 einbinden, dann schreiben wir also so etwas wie hier using Flowershop und ansonsten ändert sich im Vergleich zu unserem vorherigen Code nichts. 4:44 Eine wichtige Sache, die vielen Menschen erst einmal nicht klar ist und vielleicht sogar unintuitiv erscheint, ist der Umstand, dass Unternamensräume 4:52 separat importiert werden müssen. Das heißt, wenn wir hier so zwei Zeilen haben, using A und using A.B. B, dann ist die zweite Zeile nicht redundant. 5:01 Der Unternamensraum B von A wird nicht automatisch mit eingebunden, wenn wir den Namensraum A entbinden. 5:11 Bevor wir jetzt zu den Zugriffsbeschränkungen kommen, die für uns eine größere Rolle spielen, nämlich von den Bestandteilen einer Klasse, 5:19 wollen wir zunächst einmal erklären, warum macht man das überhaupt mit Zugriffsbeschränkung, denn man könnte ja in seinem gesamten Code einfach alles 5:26 auf public setzen und dadurch überall maximale Sichtbarkeit haben. Und das tut man aber nicht. Warum denn nicht? Zunächst einmal wäre da die Kontrolle 5:35 des eigenen Zustandes oder auch die Koherenz. Das heißt, wenn wir keinen wahllosen Zugriff von überall her auf unsere Attribute zulassen, dann haben 5:44 wir mehr Kontrolle darüber, welche Werte Attribute einnehmen können. Insbesondere können Setter dazu verwendet werden, auch zu überprüfen, ob ein neu gesetzter 5:54 Wert für ein Attribut überhaupt zulässig ist. Wohingegen, wenn wir ein Attribut einfach nach außen bekannt geben, dann kann jede andere Codeelle unsere 6:02 Attribute auf irgendeine Weise verändern und gegebenenfalls daranhängende andere Attribute nicht berücksichtigen oder sogar illegale Werte für unser 6:12 zusetzenes Attribut setzen. Außerdem haben wir eine größere Unabhängigkeit von der Implementierung, denn wir könnten uns z.B. entscheiden, dass wir 6:22 eine Variable, die wir bisher verwendet haben, als Attribut abschaffen und daraus ein abgeleitetes Attribut machen. Also kein echtes Attribut mehr, sondern 6:30 einen Wert, der jedes Mal, wenn er abgefragt wird, über den Getter neu berechnet wird. Wenn wir allerdings unsere Variablen sichtbar machen nach 6:40 außen, dann können wir das nicht mehr ohne weiteres machen. Denn wenn wir jetzt ein Attribut entfernen und ersetzen durch ein abgeleitetes 6:48 Attribut, dann hat das Zufolge, dass von außen das Verhalten nicht mehr so aussieht, wie es vorher aussah und da müssen wir gegebenenfalls auch anderen 6:57 Code noch mitändern. Außerdem haben wir eine höhere Übersichtlichkeit des Interfaces und der Dokumentation, denn eine Methode, die 7:07 von außen nicht gesehen wird, die muss auch nicht erläutert werden, jedenfalls nicht nach außen erläutert werden. Und das Interface wird dadurch schlanker, 7:15 weil Methoden, die für Außenstehende vielleicht überhaupt keine Relevanz haben, für diese auch nicht sichtbar werden. Und das heißt, wenn Sie dann 7:22 z.B. Codevorschläge bekommen, weil sie mit einem Punktoperator eine Methode auswählen wollen, dann werden auch nur solche Methoden 7:30 vorgeschlagen, die für sie überhaupt eine Rolle spielen. Und wir können all diese Punkte bereits zur Kompilationszeit 7:37 sicherstellen. Das heißt, wir sorgen damit auch dafür, dass wir bestimmte Aufgaben, die sich ansonsten ergeben würden, aus der Testphase oder aus der 7:48 Verifikationsphase des Programms nach vorne ziehen können, sodass wir bereits zur Kompilation feststellen können, dass bestimmte Dinge nicht passieren können. 7:56 Beispielsweise oben, was wir angesprochen haben, dass ein nichtkohärenter Zustand eines Objekts von außen her induziert wird. 8:05 kommen wir dazu, welche Sichtbarkeiten wir beschränken wollen. Und da gibt es im Grunde drei Stellen, an denen wir Sichtbarkeiten gegebenenfalls 8:13 modifizieren wollen. Das wären Klassen, Methoden und Attribute. Wobei wir sehen werden, bei Klassen ist es hier scharf bezüglich der Sichtbarkeit nicht so viel 8:22 zu machen. Bezüglich Methoden und Attributen haben wir aber einige Möglichkeiten, die wir auch kennenlernen und nutzen sollen. Bei den Klassen gibt 8:30 es nur zwei Stufen von Sichtbarkeit und das wäre einmal die Standardsichtbarkeit, die erfolgt, wenn man einfach überhaupt keinen 8:37 Zugsmodifikator angibt und andererseits die public Sichtbarkeit. Dafür müssen wir den Zugsmodifikator public angeben. Die Public Sichtbarkeit bedeutet, dass 8:47 man wirklich von überall aus die Klasse aufrufen kann. Insbesondere und das ist der entscheidende Unterschied von jedem Assembly aus. Das heißt, wenn wir unser 8:55 Assembly beispielsweise als DLL anbieten, dann kann von einem anderen Assembly aus auf diese Klassen, die als public markiert sind, zugegriffen 9:04 werden. Für Klassen, die die Standardsichtbarkeit haben, ist ein solcher Zugriff in einem anderen 9:09 Assembly aber nicht möglich. Und das ist auch der einzige Unterschied. Innerhalb eines Assemblies können wir auf Klassen mit der Standardsichtbarkeit frei 9:19 zugreifen. Die einzige Voraussetzung ist das, was wir oben bereits besprochen haben, dass also wir entweder uns im gleichen Namensraum befinden, den 9:28 entsprechenden Namensraum ausdrücklich einbinden oder aber einen qualifizierten Aufruf an die Klasse durchführen, also mit einem Punkt nach dem Namensraumen 9:39 und dann erst den Namen der Klasse angeben. In Java ist die Standardsichtbarkeit der Klassen auf das Paket und das ist das 9:47 Äquivalent des Namensraumes beschränkt. Eine solche Sichtbarkeitsklasse für Klassen gibt's in C#ARP nicht. Das heißt, wir können nicht sagen, diese 9:54 Klasse kann nur innerhalb unseres Namensraums verwendet werden. Denn wenn wir eine Klasse definieren, haben wir gerade gesehen, dann ist sie ja 10:02 mindestens mit Standardzugriff ausgestattet und dann können wir von jedem Namensraum im gleichen Assembly drauf zugreifen. In diesem Kurs werden 10:09 wir uns aber nicht um das Einbinden von Assemblies, also DLLs kümmern. Und das bedeutet, für uns macht es überhaupt keinen Unterschied, ob eine Klasse 10:17 public ist oder nicht. Sie sollten das grundsätzlich im Kopf behalten, denn wenn sie später produktiv arbeiten, könnte es durchaus mal passieren, dass 10:25 sie mit Assemblies arbeiten. Aber für unseren Einführungskurs hier spielen Assembleys keine Rolle. Es gibt einen Zusammenhang zu dem 10:35 Masterkurs moderne Methoden der Softwareentwicklung. Public Klassen sind solche, die dort veröffentlichte Schnittstellen genannt werden, 10:43 wohingegen die Standardsichtbarkeit mit einem in diesem Kurs öffentlich genannten Namen korrespondieren. Das heißt, ein 10:54 Interface, das als öffentlich bezeichnet wird in diesem Kurs ist das, was wir hier mit der Standardsichtbarkeit auffassen und das, was sogar fair 11:03 öffentlicht ist, sind diejenigen Klassen, die wir hier als Public Kennzeichnen. Wenn wir die Sichtbarkeit von 11:08 Klassenelementen besprechen wollen, dann müssen wir nicht unterscheiden zwischen Methoden und Attributen, denn Methoden und Attribute haben die gleichen 11:17 Sichtbarkeitsklassen zur Verfügung. Diese Sichtbarkeitsklassen, das sind insgesamt sechs Stück, heißen public, protected internal, protected, 11:27 internal, private protected und private. Und wir unterscheiden danach, ob wir aus der gleichen Klasse darauf zugreifen können, aus einer Kindklasse imselben 11:38 Assembly oder in einem anderen Assembly, aus einer nicht abgeleiteten Klasse imselben Assembly oder in einem anderen Assembly. Und wenn wir das Schlüsselwort 11:47 public verwenden, dann bedeutet das, wir können von überall darauf zugreifen. Wohingegen protected bedeutet, wir können von der eigenen Klasse aus darauf 11:55 zugreifen oder von einer Kindklasse, egal ob die im selben Assembly ist oder in einem anderen Assembly. Das Schlüsselwort Internal hingegen 12:03 kümmert sich nicht darum, ob es eine Kindklasse ist, sondern darum, ob wir uns imselben Assembly befinden. Das heißt, internal bedeutet, wir können von 12:11 der gleichen Klasse aus, von der Kindklasse und von nicht abgeleiteten Klassen imselben Assembly auf das entsprechende Klassenelement zugreifen, 12:19 aber nicht in einem anderen Assembly. Wenn man jetzt die Erlaubnisse von Protected und Internal kombinieren möchte, dann kann man den Begriff 12:27 Protected Internal verwenden. Der verbietet dann nur den Zugraff auf dieses jeweilige Klassenelement von einer nicht abgeleiteten Klasse aus, die 12:37 in einem anderen Assembly definiert wird. Wenn man hingegen die Stränge der beiden Elemente protected und internal kombinieren möchte, dann ist Private 12:47 Protected das Richtige. Hier erlauben wir generell gar keinen Zugriff aus einem anderen Assembly und aber auch keinen Zugriff aus nicht abgeleiteten 12:56 Klassen, selbst wenn die im selben Assembly sind. Und schließlich haben wir die letzte Sichtbarkeitsstufe, die auch standardmäßig ohnehin vergeben wird. Das 13:04 ist also hier die Standarddichtbarkeit, das ist private. In der gleichen Klasse können wir damit noch immer auf das jeweilige Klassenelement zugreifen. Das 13:12 lässt sich nie ausschließen, aber keine Kindklasse kann darauf zugreifen und auch keine Nichtkindklasse, egal ob im gleichen oder in einem anderen Assembly 13:22 kann dann darauf zugreifen. Diese Zugriffsmodifikatoren, die wir oben angegeben haben, die werden vor der Deklaration angegeben und sind auch noch 13:32 vor dem Begriffen Static oder Seal zu nennen. Im folgenden beschränken wir uns auf die drei Sichtbarkeitsklassen Private, Protected und Public. Und das 13:40 sind auch die einzigen, die für die Prüfung relevant sein werden, denn wir befassen uns ja in dieser Vorlesung nicht mit Assemblies und die beiden 13:48 anderen Sichtbarkeitsklassen spielen ja hier nur eine weitere Rolle dann, wenn man verschiedene Assemblies betrachtet. Um diese Begriffe ein wenig einzuüben, 13:59 betrachten wir hier einmal ein Beispiel mit drei Klassen A, B und C, wobei B von A abgeleitet ist und C unabhängig von den Klassen A und B ist. Und jede dieser 14:08 drei Klassen hat jeweils ein Attribut. Bei A heißen sie XYZ, bei B heißen sie UVW und bei C heißen sie ABC. Das private protected respektive public ist. 14:21 Beginnen mit der Klasse A. Das Private Attribut X ist nur in A sichtbar, weil ja private bedeutet, dass nur die eigene Klasse es sehen kann. Dann haben wir ein 14:31 Protected Attribut Y. Das ist sichtbar in A und B, denn B ist von Ageleitet. Das heißt, B kann auch darauf zugreifen und schließlich haben wir ein public 14:41 Attribut Z und das ist in allen drei Klassen A, B und C sichtbar, weil es eben public ist und damit keine Einschränkung besteht bezüglich der 14:49 Sichtbarkeit des Attributs. In Klasse B haben wir jetzt UV und W, wobei U private ist und damit automatisch nur in B sichtbar ist. Dann 14:59 haben wir V, das ist protected. Das ist jetzt aber auch nur in B sichtbar, denn B ist zwar vom Aleitet, aber umgekehrt gibt's eine solche Relation ja nicht. 15:08 Wir haben auch keine andere Klasse, die von B abgeleitet ist. Also ist V nur in B sichtbar. Und schließlich haben wir wieder eine Public Variable W und die 15:18 ist in allen drei Klassen sichtbar. Und bei Klasse C stellt sich das jetzt im Grunde genauso da wie bei Klasse B. Wir haben ein private Attribut A, das ist 15:27 damit auch automatisch nur in A nur in C sichtbar. Dann haben wir ein Protected Attribut B. Das ist nur in C sichtbar, denn es gibt keine von C abgeleitete 15:37 Klasse. Und schließlich ein public Attribut, das auch C heißt. Und das ist sichtbar in den Klassen A, B und C, einfach, weil es eben public ist und 15:45 damit keine Einstellung für die Sichtbarkeit besteht. Die Sichtbarkeitsmodifikatoren, die uns in Zuge dieser Vorlesung 15:54 interessieren, nämlich Public Protected Private, lassen sich auch in der UML ausdrücken. Und zwar schreibt man dort die Sichtbarkeitsmodifikatoren, das ist 16:02 jeweils nur ein Zeichen, direkt vor das jeweilige Attribut respektive die jeweilige Methode. Und für public verwenden wir das Zeichen Plus, für 16:11 Protected die Raute und für Private ein Minus. Um das Ganze einmal anhand eines Beispiels zu illustrieren, habe ich hier 16:22 einen kleinen Ausschnitt aus unserer Klassenhierarchie von unserem Leitbeispiel herausgegriffen und diesen Teil des Klassendiagramms 16:31 einmal mit den Sichtbarkeitsmodifikatoren versehen. Wie Sie hier sehen, ist das Ganze im Grunde sehr simpel, denn alle 16:39 Attribute sind hier einfach als private markiert und alle Methoden als public. Das ist nicht zwingend immer so, aber grundsätzlich bietet es sich schon an 16:49 und es ist meistens zu empfehlen, tatsächlich alle Attribute private zu setzen. Und bei den Methoden ist es so, dass normalerweise in einem UML-diagramm 17:01 besonders die öffentlichen Methoden eine wichtige Rolle spielen. Teilweise verzichtet man insbesondere auf die privaten Hilfsmethoden. Also, wenn man 17:10 jetzt eine Methode hat, die nur als Hils Methode verwendet wird, dann ist das für die Modellierung in dem UML-diagramm nicht wichtig. Das heißt, private 17:20 Methoden tauchen hier in aller Regel nicht auf. hingegen protected Methoden spielen natürlich schon eine relevante Rolle und die sind dann auch häufig in 17:29 einem solchen Klassendiagramm zu finden und dann eben zu erkennen, dass da kein Plus steht, sondern eine Raute. Aber die grundlegende Idee ist schon meistens, 17:37 dass man die Attribute nicht sichtbar haben möchte und die Methoden sichtbar haben möchte. Wenn wir einen Klassenmember bei einer 17:47 Vererbung überschreiben und das ist jetzt etwas, was nur Methoden betrifft, dann dürfen die Zugsrechte nur erweitert werden. Das heißt, wir können von 17:58 private zu protected und von Protected zu public übergehen, aber nicht umgekehrt. Das heißt, wenn etwas bereits in der Oberklasse public ist, dann kann 18:07 es in der abgeleiteten Klasse nicht beispielweise auf Protected herabgesetzt werden. Ich habe vorhin bereits einmal angesprochen, dass es grundsätzlich 18:16 sinnvoll und empfehlenswert ist, Attribute private zu behalten und nicht für abgleitete Klassen oder gar außenstehende Klassen sichtbar zu 18:26 machen. Das nennt man auch das Geheimnisprinzip. Und dieses Geheimnisprinzip, das möchten wir jetzt einmal motivieren, indem wir 18:35 ein Beispiel besprechen, bei dem ein echter Vorteil aus dem Geheimnisprinzip erwächst. Man stelle sich vor, man habe eine Klasse Complex Numbers, um die 18:43 komplexen Zahlen zu repräsentieren. Und diese Klasse habe nun zwei Attribute, nämlich den Realteil und den Imaginärteil Real und Imaginary. Die 18:53 haben auch jeweils einen Getter und einen Zter. Jetzt könnte es natürlich sein, dass man sich entscheidet, wir wollen vielleicht 19:01 die komplexen Zahlen gar nicht über Realteilen, die Imaginärteil repräsentieren, sondern viel sinnvoller wäre es doch eigentlich das Ganze über 19:09 Polarkoordinaten zu repräsentieren. Je nachdem, wie man mit diesen komplexen Zahlen rechnet, ist mal die eine und mal die andere Darstellung sinnvoller. Aber 19:17 wenn man z.B. jetzt feststellt die ursprüngliche Idee das über die x und Y Koordinaten zu repräsentieren, also über den realen Imaginärteil, das ist 19:27 meistens jetzt ineffizienter für das, was wir machen wollen. Dann würde man ja vielleicht das Ganze umstellen, würde entsprechend Attribute Radius und Angle 19:36 einfügen und jeweils einen Getter und Zettel dafür angeben. In der Form, die wir gerade gesehen haben, wäre allerdings die Konsequenz 19:44 natürlich, dass das Ganze nicht mehr kompatibel wäre mit Code, der für die alten komplexen Zahlen geschrieben wäre. Was wir aber jetzt tun könnten, ist, 19:53 dass wir die Getter und Zter für die alten Realteile und Imaginärteile hier übersetzen, dass wir also sagen, wir setzen hier gar nicht mehr wirklich 20:03 eine Variable real oder eine Variable Imaginary, sondern wir errechnen, was ist denn die Auswirkung auf Radius und Angle, wenn wir den Realteil oder den 20:14 Imaginärteil separat setzen würden. Das heißt, der Getter ist an der Stelle relativ einfach. Wir rechnen hier einfach über die üblichen Sinus und 20:23 Cosinus Funktionen aus, wie man aus dem Radius und aus dem Winkel den entsprechenden real und Imaginärteil bekommt. Und die Zter, die berechnen 20:33 nun, wie man aus den kathesischen Koordinaten die Polarkoordinaten ermittelt. Das heißt, wir setzen jetzt beispielsweise den Realteil, das heißt, 20:43 wir belassen den imaginärteil unverändert, den nehmen wir uns zunächst einmal mit Get Imaginary. Den berechnen wir also an der Stelle. 20:50 Und dann setzen wir den neuen Winkel und den neuen Radius aus diesen kathetesischen Koordinaten, also aus den Werten Real Imaginary. Und genau das 21:02 gleiche machen wir, wenn der Imaginärteil gesetzt wird. In der obigen Implementation fehlte jetzt noch die vorausgesetzte Methode 21:11 Set from Cartesion. Und diese Methode würde man jetzt üblicherweise private setzen, denn das ist ja nur eine Hilfsmethode für unsere Implementation 21:20 der komplexen Zahlen als Polarkoordinaten. Und diese Methode bekommt also einen Realteil, ein Imaginärteil übergeben und 21:28 berechne ich jetzt einfach den Radius und den Winkel entsprechend der Zuordnungsvorschriften, die üblicherweise verwendet werden, um eine 21:37 solche Transformation zwischen diesen beiden Darstellungen vorzunehmen. Die genauen Formeln müssen sie sich jetzt eigentlich gar nicht groß anschauen an 21:44 der Stelle. Wichtig ist nur, wir verwenden auch hier wieder den Setter. Das heißt, selbst wenn wir uns später entscheiden würden, wieder zu wechseln 21:52 zwischen den Polarkoordinaten und den Realteil und Imaginärteil, könnten wir an der Stelle mit wenig Aufwand diese Methoden immer noch beibehalten. 22:05 Der große Vorteil ist jetzt an der Stelle jedenfalls, dass von außen wir überhaupt nichts verloren haben und sich nichts geändert hat für den Aufrufer, 22:15 indem wir hier die Polarkoordinaten statt der realen Imaginärteile verwendet haben. Das heißt, wenn wir uns jetzt entschieden hätten, realen Imaginärteil 22:26 nicht privat zu halten, dann hätten wir jetzt das Problem, dass wir alle Klassen, die unsere komplexen Zahlen genutzt haben, daraufhin überprüfen 22:34 müssten, ob dort irgendeinen Zugriff auf den realen Imaginärteil stattgefunden hat und das dann jetzt auf unsere Getter und Zter umlenken oder aber komplett den 22:46 Code zu ändern, um direkt mit den Polarkoordinaten zu arbeiten. Das ist natürlich jetzt ein Beispiel, dem dem sie nicht häufig zu tun haben 22:55 werden, dass sie jetzt konkret die komplexen Zahlen implementieren, aber es ist etwas, was immer mal wieder vorkommen kann, dass sie abgeleitete 23:02 Attribute und nicht abgeleitete Attribute haben und zwischen denen wechseln müssen. Üblich schließen wir diese Vorlesungseinheit mit drei Fragen 23:10 zur Selbstdiagnose, mit denen Sie überprüfen können, ob Sie die Inhalte dieser Vorlesung gut verstanden haben. Die erste Frage ist, wozu sollte man 23:19 Zugriffsbeschränkungen vornehmen? Die zweite Frage ist, was gilt für die Zugriffsmöglichkeiten auf Klassen, Attribute und Methoden, wenn man keinen 23:28 Zugriffsmodifikator angibt? Und schließlich, was ist hinsichtig der Sichtbarkeitsklassen bei der Spezialisierung, also der Ableitung zu 23:36 berücksichtigen? Vielen Dank für Ihre Aufmerksamkeit und bis zum nächsten Mal. M.