Programmierkurs: Delphi: Pascal: Eigenschaften
Eigenschaften
[Bearbeiten]Einleitung
[Bearbeiten]Objekte haben Eigenschaften. Dies ist eine Regel, die in der freien Natur wie auch in der objektorientierten Programmierung unter Delphi zutrifft.
Eigenschaften stellen einen Zwischenweg zwischen Feldern und Methoden einer Klasse dar. So kann man zum Beispiel beim Auslesen einer Eigenschaft einfach den Wert eines Feldes zurück erhalten, löst aber beim Ändern dieser Eigenschaft eine Reihe von Folgeaktionen aus. Dies liegt daran, dass man bei Eigenschaften genau festlegen kann, was im jeweiligen Falle passieren soll. Ebenso kann jeweils das Lesen oder das Schreiben einer Eigenschaft unterbunden werden.
Wie man bereits sieht, kann ein Programm über Eigenschaften sicherer und gleichzeitig flexibler gestaltet werden.
Eigenschaften definieren
[Bearbeiten]In der Klassendefinition legt man Eigenschaften mit folgender Syntax an:
property Name: <Typ> [read <Feld oder Methode>] [write <Feld oder Methode>];
Die Schlüsselwörter read und write sind so genannte Zugriffsbezeichner. Sie sind beide optional, wobei jedoch mindestens einer von beiden angegeben werden muss. Die folgenden Beispiele sind alle gültig:
property Eigenschaft1: Integer read FWert; property Eigenschaft2: Integer write ZahlSetzen; property Eigenschaft3: string read FText write TextSetzen;
Zur kurzen Erklärung:
- mit Zugriff auf Eigenschaft1 wird der Wert des Feldes FWert zurück gegeben, eine Zuweisung an Eigenschaft1 ist jedoch nicht möglich (Nur-Lesen-Eigenschaft)
- Eigenschaft2 dürfen nur Werte zugewiesen werden, diese werden dann an die Methode ZahlSetzen weitergereicht. Das Auslesen des Wertes ist über Eigenschaft2 nicht möglich (Nur-Schreiben-Eigenschaft)
- Eigenschaft3 ermöglicht hingegen sowohl das Schreiben als auch das Lesen von Daten (Lesen-Schreiben-Eigenschaft)
Im Normalfall wird man überwiegend Lese-Schreib-Eigenschaften verwenden und ggf. einige Nur-Lesen-Eigenschaften. Nur-Schreib-Eigenschaften sind hingegen sehr selten anzutreffen, können aber im Einzelfall einen ebenso nützlichen Dienst erweisen.
Verwendet man für den Zugriff ein Feld, muss dieses lediglich den gleichen Typ haben wie die Eigenschaft. Sollen jedoch Methoden beim Zugriff zum Einsatz kommen, müssen diese bestimmten Regeln folgen: Für den Lesezugriff benötigt man eine Funktion ohne Parameter, deren Ergebnis vom Typ der Eigenschaft ist. Beim Schreibzugriff muss eine Prozedur verwendet werden, deren einziger Parameter den gleichen Typ wie die Eigenschaft besitzt.
Gut, das alles hört sich etwas kompliziert an. Gestalten wir doch mal ein kleines Gerüst für die Verwaltung eines Gebrauchtwagenhändlers:
type TFarbe = (fSchwarz, fWeiß, fSilber, fBlau, fGruen, fRot, fGelb); TAutoTyp = (atKlein, atMittelStufe, atMittelFliess, atKombi, atLuxus, atGelaende); TAuto = class private FFarbe: TFarbe; FTyp: TAutoTyp; procedure FarbeSetzen(Farbe: TFarbe); function PreisBerechnen: Single; public property Farbe: TFarbe read FFarbe write FarbeSetzen; property Typ: TAutoTyp read FTyp; property Preis: Single read PreisBerechnen; constructor Create(Farbe: TFarbe; Typ: TAutoTyp); end;
Wie man bereits ohne das vollständige Programm erkennen kann, haben wir mit dieser Klasse ähnliche Möglichkeiten wie in der realen Welt. Zum Beispiel ist es dem Händler möglich, das Auto umzulackieren. Hierbei wird eine Methode aufgerufen, die möglicherweise Folgeaktionen auslöst (z.B. das Auto für eine Weile als „nicht im Bestand“ markiert). Andererseits kann der Händler zwar abrufen, um welchen Autotyp es sich handelt, kann diesen aber selbstverständlich nicht verändern. Der Preis ist nicht starr festgelegt, sondern wird über eine Funktion berechnet (die sich wahrscheinlich unter anderem an der Farbe und am Typ des Autos orientiert).
Weiterhin ist diese Klasse so angelegt, dass man „von außen“ nur Zugriff auf die Eigenschaften und den Konstruktor von TAuto hat. Die Felder und Methoden dienen nur der internen Verwaltung der Eigenschaften und sind daher im private-Abschnitt verborgen.
Array-Eigenschaften
[Bearbeiten]Eigenschaften können auch als Arrays definiert werden. Dies ist immer dann sinnvoll, wenn man Daten in Form einer Tabelle bzw. Auflistung speichern möchte. Der Zugriff erfolgt wie bei Variablen über Indizes in eckigen Klammern. Hinter den Zugriffsbezeichnern darf bei Array-Eigenschaften allerdings kein Feld angegeben werden, hier sind nur Methoden zulässig:
property Zeile[Nr: Integer]: string read HoleZeile write SchreibeZeile; property Punkt[X, Y: Integer]: Integer read HolePunkt;
Die zugehörigen Methoden werden dann wie folgt definiert:
function HoleZeile(Nr: Integer): string; procedure SchreibeZeile(Nr: Integer; Text: string); function HolePunkt(X, Y: Integer): Integer;
Als erste Parameter werden also immer die Felder des Arrays angesprochen, dann folgen die Parameter analog den einfachen Eigenschaften. Die Bezeichner der Variablen müssen mit den Indizes der Eigenschaften nicht notwendigerweise übereinstimmen. Um die Anwendung jedoch übersichtlich zu gestalten, sollte man gleiche Bezeichner verwenden. Grundsätzlich wäre also auch folgende Notation gültig:
function HoleZeile(ZeilenNr: Integer): string; property Zeile[X: Integer]: string read HoleZeile;
Überschreiben
[Bearbeiten]Sichtbarkeit ändern
[Bearbeiten]Bei der Vererbung von Klassen kann es vorkommen, dass man Eigenschaften mit der Sichtbarkeit protected erbt, diese aber in der eigenen Klasse als public kennzeichnen möchte. Hierfür ist es möglich, die Eigenschaften mit der gewünschten Sichtbarkeit neu zu definieren.
Beispiel: Wir haben von der Klasse TBasis die als protected gekennzeichnete Eigenschaft „Name“ geerbt. Um diese nun allgemein zugänglich zu machen, schreiben wir einfach:
public property Name;
Damit wurde an der Funktionsweise der Eigenschaft nichts geändert, sie ist jetzt lediglich „besser sichtbar“. Damit ist der Programmcode bereits vollständig. Es müssen keine weiteren Zugriffsfelder oder -methoden hierfür geschrieben werden.
Anders herum ist es auf diese Weise nicht möglich, Eigenschaften zu verstecken. Selbst wenn Sie eine öffentliche Eigenschaft in einem Nachkommen als strict private kennzeichnen, können Sie immer noch darauf zugreifen. Das Programm verwendet dann die Eigenschaft des Vorfahren mit der höheren Sichtbarkeit.
Neu definieren
[Bearbeiten]Wenn Sie nun von einer Klasse eine Eigenschaft vererbt bekommen, die Sie anders verarbeiten wollen, können Sie diese wie eine gewöhnliche Eigenschaft mit dem gleichen Namen neu definieren. Hierzu sind ebenfalls die Angaben der Zugriffsbezeichner erforderlich. So lässt sich zum Beispiel eine Lese-Schreib-Eigenschaft als Nur-Lesen-Eigenschaft neu definieren:
type TBasis = class FFarbe: string; property Farbe: string read FFarbe write FFarbe; end; TErbe = class(TBasis) property Farbe: string read FFarbe; end;
„FFarbe“ muss in der Klasse „TErbe“ nicht neu eingeführt werden, es wird das Feld aus der Basis-Klasse verwendet. Falls ein Zugriffsbezeichner in der Basisklasse als private gekennzeichnet wurde, gibt Delphi bei der Kompilierung eine entsprechende Warnung aus. Für reine Delphi-Programme kann diese Warnung ignoriert werden. Sie ist für die Portierung des Programms in andere Programmiersprachen gedacht, da dort eine solche Schreibweise unter Umständen nicht erlaubt ist.
Falls eine Eigenschaft eine Methode für den Zugriff verwendet und Sie das Verhalten ändern wollen, müssen Sie lediglich die geerbte Methode überschreiben. Mehr hierzu im nächsten Kapitel „Vererbung“.
Zugriff auf Eigenschaften
[Bearbeiten]Der Zugriff auf Eigenschaften erfolgt wie bei Feldern und Methoden:
Objekt.Eigenschaft := Wert; Wert := Objekt.Eigenschaft;
Hierbei ist jedoch zusätzlich zur Sichtbarkeit auch die Zugriffsmöglichkeit der Eigenschaft zu beachten, das heißt, ob Sie eine Nur-Lesen-, Nur-Schreiben- oder eine Lese-Schreib-Eigenschaft verwenden wollen. Die Eigenschaften verhalten sich wie eine Mischung aus Variablen und Methoden, z.B. kann man Werte auslesen und übergeben. Nicht möglich ist dagegen die Übergabe einer Eigenschaft als Variablen-Parameter einer Funktion. So erzeugt dieser Programmschnipsel eine Fehlermeldung:
type TKlasse = class private FZahl: Integer; public property Zahl: Integer read FZahl write FZahl; end; var Test: TKlasse; begin Test := TKlasse.Create; Test.Zahl := 0; Inc(Test.Zahl); // <-- Fehlermeldung Test.Destroy; end.
Obwohl hier die Daten letztlich in der Klassenvariablen FZahl gespeichert werden, darf die dazugehörige Eigenschaft Zahl nicht an die Routine Inc übergeben werden. Hier bleibt letztlich nur der direkte Weg:
Test.Zahl := Test.Zahl + 1;
Standard-Eigenschaften
[Bearbeiten]Wie oben beschrieben muss beim Zugriff auf eine Eigenschaft immer der Name der Eigenschaft selbst angegeben werden. Für Array-Eigenschaften gibt es eine praktische Besonderheit: die Standard-Eigenschaft. Wenn eine Array-Eigenschaft als Standard definiert wird, kann der Name dieser Eigenschaft beim Zugriff weggelassen werden. Dies erreicht man durch Angabe der Direktive default hinter der Eigenschaften-Definition:
property Eigenschaft[X: Integer]: string read LeseMethode write SchreibMethode; default;
Jetzt sind diese beiden Angaben gleichbedeutend:
Wert := Objekt.Eigenschaft[Index]; Wert := Objekt[Index];
Selbstverständlich kann nur jeweils eine Eigenschaft je Klasse als Standard definiert werden.
Pascal: Konstruktoren und Destruktoren | Inhaltsverzeichnis | Pascal: Vererbung |