3.2 Kapselung
Was nicht ist, kann noch werden.
Diese mahnenden Worte sind letztendlich die Motivation dafür, nicht alle Definitionen einer Klasse öffentlich zu machen. Wenn Sie irgendetwas aus der Hand geben, wissen Sie nie, was damit passiert. In der Praxis passiert das z. B. bei der Erweiterung eines Projektes. In den neu hinzugekommenen Klassen setzen Sie Objektzustände nach Bedarf. Wenn Sie die Anwendung dann starten, passiert es sehr schnell, dass die Programmlogik früherer Teile nicht mehr stimmt. Was ist passiert?
Frühere Teile des Projekts gehen von bestimmten Annahmen über die Zustände von Objekten aus. Durch den direkten Zugriff auf diese in späteren Teilen des Projekts stimmen die Annahmen nicht mehr, und niemand hat die früheren Teile über die geänderten Voraussetzungen informiert. In einer solchen Situation ist es wünschenswert, Änderungen nur über klar definierte Kanäle zuzulassen. Die einfachste Art ist es, eine öffentliche Funktion zum Ändern bereitzustellen und den Zugriff auf die Zustände selbst zu unterbinden. Die Zugriffsfunktionen können sich dann um Seiteneffekte kümmern. Dadurch, dass man sie benutzen muss (nur sie sind öffentlich), kann man Nebenschauplätze nicht vergessen.
Ein anderes Szenario betrifft die Wartung Ihrer Software. Stellen Sie sich vor, eine Funktion, die Sie nur als Hilfsmittel für sich in aller Eile geschrieben haben, wird ohne Ihr Wissen von den Benutzern Ihrer Software entdeckt und in deren Programmen verwendet. Nun stellen Sie fest, dass eine Änderung der Funktion Ihre Software besser macht. Da es ja nur eine Hilfsfunktion ist und kein wesentlicher Bestandteil der Anwendung, passen Sie die Funktion entsprechend Ihren Erfordernissen an, ohne das groß zu dokumentieren. Nach der nächsten Aktualisierung Ihrer Software bei den Anwendern hagelt es Beschwerden, weil die Programme der Benutzer durch die Änderungen nicht mehr richtig laufen. In einer solchen Situation wünscht man sich, man hätte die Funktion nie geschrieben. Um das zu verhindern, sollte man Hilfsfunktionen vor dem Zugriff Fremder schützen. So kann die beschriebene Situation gar nicht auftreten, da eine Benutzung durch den Schutz von vornherein ausgeschlossen ist.
Diesen beiden Beispielen könnte man noch sehr viele hinzufügen, die nichts an dem Wunsch nach einer Zugriffskontrolle ändern würden. Daher bietet Visual Basic die Möglichkeit, den Grad der Sichtbarkeit einer Klasse oder eines Klassenmitglieds über Modifikatoren zu steuern. Wir haben uns schon einmal kurz in Abschnitt 2.5.5, »Sichtbarkeit und Lebensdauer«, damit auseinandergesetzt. Mit diesen Modifikatoren können Sie Kontrolle über Ihr Werk behalten. Jedes Klassenmitglied, wie zum Beispiel Felder und Methoden, kann mit einer der Spezifikationen in Tabelle 3.2 kontrolliert werden (die vererbungsbezogenen Modifikatoren mit Protected werden in Abschnitt 3.13.2, »Sichtbarkeitsmodifikatoren«, besprochen).
Modifikator | Sichtbarkeit |
Private |
innerhalb derselben Klasse |
Friend |
innerhalb derselben Anwendung (Projekt) |
Protected |
innerhalb von Kindklassen |
Protected Friend |
innerhalb derselben Anwendung (Projekt) / innerhalb von Kindklassen |
Public |
überall |
Hinweis |
Ohne Sichtbarkeitsmodifikator sind Felder Private und alle anderen Klassenmitglieder Public. |
3.2.1 Kombinationen
Um das Geheimnisprinzip zu wahren, gilt eine allgemeine Regel:
Ein Element (Variable, Methode, Typ, …) hat höchstens die Sichtbarkeit des umgebenden Elements. |
Sie können sich das als einen Stapel von Folien vorstellen, durch den Sie hindurchsehen. Ist nur eine Folie im Stapel undurchsichtig, ist der ganze Stapel nicht mehr transparent. Wenn zum Beispiel eine Klasse als Friend deklariert ist und ein als Public gekennzeichnetes Feld hat, kann man nicht von außerhalb der Anwendung auf das Feld zugreifen, da die Klasse durch ihre Beschränkung die restriktivere Sichtbarkeit hat und damit die Sichtbarkeit dominiert. Das folgende Codefragment zeigt einen weiteren Fall, der nicht sofort offensichtlich ist:
'...\Klassendesign\Kapselung\Schnittmenge.vb |
Option Explicit On Namespace Klassendesign Friend Class Anwendung End Class Public Class Umgehung 'Public Jeder As Anwendung End Class End Namespace
Die auskommentierte Zeile in Umgehung würde einen Compilerfehler erzeugen, da versucht würde, über das öffentliche Feld Jeder der öffentlichen Klasse Umgehung die auf die Anwendung beschränkte Klasse Anwendung öffentlich zur Verfügung zu stellen.
3.2.2 Private
Wie in Tabelle 3.2, »Sichtbarkeitsmodifikatoren eines Klassenmitglieds«, steht, sind private Klassenmitglieder von innerhalb der Klasse verwendbar. Es ist wichtig zu sehen, dass es hier um Klassen und nicht um Objekte geht. Ein Objekt kann auf die privaten Teile eines anderen Objekts zugreifen, wenn beide den gleichen Datentyp haben. Das folgende Codefragment zeigt, dass über die Referenz nullpunkt das private Feld wert in Addieren() benutzt wird.
'...\Klassendesign\Kapselung\Schnittmenge.vb |
Option Explicit On Namespace Klassendesign Class MitPrivatemFeld Private wert As Integer = Now.Millisecond Public nullpunkt As MitPrivatemFeld Sub Addieren() Console.WriteLine("Summe {0}.", nullpunkt.wert + wert) End Sub End Class Module Privat Sub Zugriff() Dim p As MitPrivatemFeld = New MitPrivatemFeld() p.nullpunkt = New MitPrivatemFeld() p.Addieren() Console.ReadLine() End Sub End Module End Namespace
In diesem Kontext gelten Objekte als typgleich, wenn der Operator TypeOf Objekt Is Typ den Wert True ergibt (vergleiche Abschnitt 3.13.1, »Klassenbeziehung durch Inherits«).
Hinweis |
Private beschränkt den Zugriff, indem es ein Klassenmitglied gegenüber anderen Klassen unsichtbar macht (siehe zum Beispiel Abschnitt 3.13.4, »Modifikation: Shadows und Overloads (Overrides)«). |
3.2.3 Sichtbarkeitsmodifikatoren
Um festzustellen, welcher Zugriff von wo aus möglich ist, definieren wir eine Klasse, die Mitglieder mit verschiedenen Sichtbarkeiten hat. In Abschnitt 3.2.1, »Kombinationen«, wurde dargelegt, dass das Element mit der kleinsten Transparenz die gesamte Sichtbarkeit dominiert. Daher wird die Klasse als unbeschränkt (Public) deklariert. In der Klasse ist auch eine Prozedur, die erfolgreich auf alle Klassenmitglieder zugreift (die Prozedur macht »nichts«, hier geht es nur um einen Compilertest). Dies zeigt, dass eine Klasse immer vollständigen Zugriff auf alle ihre Mitglieder hat.
'...\Klassendesign\Kapselung\Kapselung.vb |
Option Explicit On Namespace Klassendesign Public Class Jeder Public Jeder As Short Friend Anwendung As Short Protected Erbe As Short Protected Friend ErbeUndAnwendung As Short Private Klasse As Short Dim FeldOhne As Short Function MethodeOhne() As Short End Function Sub Zugriff() Dim v() As Short = { _ Jeder, Anwendung, Erbe, ErbeUndAnwendung, Klasse, _ FeldOhne, MethodeOhne() _ } End Sub End Class End Namespace
Schauen wir uns nun an, was eine andere Klasse sehen kann. Wie das nächste Codefragment zeigt, das sich im selben Projekt befindet, ist ein Teil des Zugriffs nicht mehr möglich. Die verbotenen Möglichkeiten sind als Kommentar angegeben. Wir befinden uns außerhalb der Klasse, also sind private Mitglieder (Private und Felder ohne Modifikatoren) nicht sichtbar. Ebenso ist ein Protected-Mitglied, das nur bei Vererbungsbeziehungen genutzt werden kann, außerhalb der Reichweite. Was dabei »Vererbungsbeziehung« bedeutet, ist hier noch uninteressant und wird in Abschnitt 3.13.2, »Sichtbarkeitsmodifikatoren«, erklärt. Hier ist nur wichtig, dass ohne eine solche Beziehung der Zugriff verweigert wird. Die Kombination Protected Friend der Variablen ErbeUndAnwendung erlaubt über Friend den Zugriff innerhalb der Anwendung (die Rechte sind in diesem einen Fall additiv).
'...\Klassendesign\Kapselung\Zugriff.vb |
Option Explicit On
Namespace Klassendesign
Module Zugriff
Sub Main()
Dim obj As Jeder = New Jeder()
Dim v1() As Short = { _
obj.Jeder, obj.Anwendung, obj.ErbeUndAnwendung, obj.MethodeOhne() _
}
'nicht: obj.Vererbung, obj.Privat, obj.FeldOhne
End Sub
End Module
...
End Namespace
Beim Zugriff von einem fremden Projekt sind zusätzlich die mit Friend gekennzeichneten Klassenmitglieder verschlossen, wie das nächste Codefragment zeigt. So bleiben nur die öffentlichen übrig. Bitte achten Sie bei eigenen Tests darauf, das verwendete Projekt mit der Klasse Jeder in den Projekteigenschaften zu referenzieren und beiden Projekten in ihren Eigenschaften denselben Wurzelnamensraum zu geben.
'...\Klassendesign\Fremdklasse\Zugriff.vb |
Option Explicit On
Namespace Klassendesign
Module Zugriff
Sub Main()
Dim jeder As Jeder = New Jeder()
Dim v1() As Short = {obj.Jeder, obj.MethodeOhne()}
'nicht: obj.Anwendung, obj.Erbe, obj.ErbeUndAnwendung,
' obj.Privat, obj.FeldOhne
End Sub
End Module
...
End Namespace
3.2.4 Lokale Variablen
Variablen sind immer in dem Block und den in diesem enthaltenen Blöcken gültig, in dem sie deklariert sind. Sie werden mit Dim oder – bei Datentypen – mit einem der Modifikatoren aus Tabelle 3.2: Sichtbarkeitsmodifikatoren eines Klassenmitglieds deklariert. Darüber hinaus erlauben die For-Schleifen und Using eine besondere Art der Deklaration (siehe Abschnitt 2.10.2, »For«, und Abschnitt 3.16.3, »Dispose und Using«). Die Effekte der Lokalität sind in Abschnitt 2.5.5, »Sichtbarkeit und Lebensdauer«, erläutert. Als Block gelten dabei:
- Datentypen: Module, Class, Structure
- Methoden: Sub, Function, Operator, Get, Set
- Schleifen: Do, While, For, For Each
- Kontrollstrukturen: If, Select Case
- Fehlerbehandlung: Try-Catch-Finally
- Objektzugriff: SyncLock, Using, With
3.2.5 Softwareschutz
Die Sichtbarkeitssteuerung kann nicht verhindern, dass sich Hacker Informationen über Ihre Klassen verschaffen. Brauchen Sie einen Schutz Ihrer Software vor neugierigen Blicken, können Sie sogenannte Obfuskatoren einsetzen (siehe zum Beispiel http://de.wikipedia.org/wiki/Obfuscator). Eine einfache Möglichkeit zu sehen, was andere auch sehen könnten, besteht in der Verwendung eines sogenannten Reflektors. Er analysiert ein Programm und stellt es in einer Form dar, die von Menschen gelesen werden kann. Ein relativ weit entwickeltes und kostenloses Produkt stammt von Lutz Roeder (http://www.aisto.com/roeder/dotnet/). Nach der Lektüre und eigenen Experimenten werden Sie feststellen, dass es nicht möglich ist, eigene Programme perfekt gegen Hacker zu sichern. Sie können jedoch die Hürde so hoch legen, dass in der Praxis diese Leute das Interesse verlieren.
3.2.6 Grafikbibliothek: private Größe des Rechtecks
In diesem Abschnitt erweitern wir die in Abschnitt 3.1.12, »Grafikbibliothek: Beispiel für Kapitel 3 und 4«, eingeführte Grafikanwendung. Jede Geometrie hat eine Ausdehnung, die in ihren Abmaßen definiert ist. Es wäre nicht sinnvoll, eine direkte Manipulation dieser Maße zuzulassen, da sonst schnell der Fall negativer Abmessungen entstehen kann. Daher deklarieren wir im nächsten Codefragment diese Größen mit Private. Damit ist nur ein Zugriff nur aus der Klasse heraus möglich und kann gesteuert werden.
'...\Klassendesign\Graphik\Dimension.vb |
Option Explicit On
Namespace Klassendesign
Partial Public Class Rechteck
Private a As Double = 1
Private b As Double = 1
End Class
End Namespace
Da wir noch keine weiteren Klassenmitglieder haben, erfolgt der Test in Abschnitt 3.3.7, »Grafikbibliothek: Zugriffsmethoden auf die Größe des Rechtecks«, nach der Einführung von Zugriffsmethoden.
Ihre Meinung
Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.