Object Model
1. Objekte und Klassen
Ein Objekt in Gambas ist eine Datenstruktur, die Propeties, Variablen, Methoden und (Events) bereitstellt.
Auf Objekte wird "per Referenz" zugegriffen, d.h. mittels eines Zeigers auf das Objekt, d.i. eine Variable dessen Wert die Adresse der Objekt-Datenstruktur im Speicher ist.
Man kann die Adresse eines Objektes mithilfe der Instruktion Print anzeigen:
DIM aStr AS NEW String[]
PRINT aStr
(String[] 0x80dccf8)
Die Struktur eines Objektes wird durch eine Klasse beschrieben.
1.1. Klassen
Ein Gambas-Objekt geht auf eine Klasse zurück, in der alle öffentlichen Eigenschaften, Methoden und Ereignisse beschrieben sind.
Diese Klasse ist ebenfalls ein Gambas-Objekt, dessen Klasse "Klasse" (
Class) heißt.
Eine
statische Klasse ist eine Klasse deren Elemente alle
statisch sind (s.u.). In Gambas wird eine statische Klasse auch
Modul genannt.
Aus einer statische Klasse kann kein Objekt erzeugt werden: Es wäre ein Objekt ohne dynamische Eigenschaften – also nutzlos.
Beispiel
Die Klasse
System ist eine statische Klasse: alle ihre Methoden sind statisch und man kann kein Objekt erstellen, dessen Klasse System wäre.
Eine
ist eine versteckte Pseudo-Klasse, die man nicht explizit manipulieren kann.
1.2. Eigenschaften, Methoden und Variablen
Eigenschaften und Methoden erlauben die Manipulation der Datenstruktur.
Eine Eigenschaft, eine Methode oder eine kann
statisch sein:
-
eine statische Variable wird von allen Instanzen (Objekten) derselben Klasse geteilt;
-
eine statische Eigenschaft oder kann nur statische Variablen verändern.
Eine Methode oder eine Variable kann entweder
öffentlich oder
privat sein. Eine Eigenschaft ist immer öffentlich.
Private Symbole können nur innerhalb einer Klasse verwendet werden.
Öffentliche Symbole können überall verwendet werden, vorausgesetzt man hat eine Referenz auf das Objekt.
1.3. Referenzen
Es gibt keinen in Gambas. Daher erhält jedes Objekt einen
Referenzzähler, der immer dann inkrementiert wird, wenn das Objekt von einer Variablen, einem
Array, einer
Collection oder anderem Objekt referenziert wird. Er wird dekrementiert, wenn diese Referenz verschwindet.
Dieser Referenzzähler ist null, wenn das Objekt erstellt wird und wenn er erneut null wird, wird das Objekt aus dem Speicher entfernt.
1.4. Ungültige Objekte
Ein Objekt kann
werden, wenn es z.B. mit einem internen Objekt, das nicht von Gambas verwaltet wird, verbunden ist und dieses freigegeben wird.
Der Versuch, ein ungültiges Objekt zu benutzen, verursacht einen Fehler.
1.5. Spezielle Methoden
Spezielle Methoden sind in einer Klasse deklarierte Methoden, deren Name mit einem Unterstrich beginnt und in den folgenden Situationen vom Interpreter aufgerufen werden:
-
wenn ein Objekt erstellt wird;
-
wenn ein Objekt freigegeben wird;
-
wenn die Klasse eines Objekts geladen wird;
-
wenn die Klasse eines Objekts entfernt wird;
-
wenn ein Objekt verwendet wird, wie ein Array;
-
wenn über ein Objekt iteriert wird;
-
wenn ein Objekt wie eine Funktion verwendet wird;
-
wenn eine unbekannte Methode oder Eigenschaft des Objektes aufgerufen wird.
Siehe
Special Methods und
Special Methods für weiterführende Informationen.
2. Events & Observer
2.1. Events
Events sind Signale, die einem Objekt gesendet werden, wnen etwas auf ihm passiert.
Wenn ein Objekt Events hervorruft, hält es eine Referenz auf seinen
Observer (Überwacher) oder sein
Elternobjekt.
Der Observer ist ein weiteres Objekt, das
Event-Handler implementiert. Ein Event-Handler ist eine öffentliche Methode, die aufgerufen wird, wenn ein eintritt.
Per Voreinstellung ist der Observer eines Objektes das aktuelle Objekt, in dem ersteres Objekt erstellt wird.
Um Events hervorzurufen muss ein Objekt einen
Eventnamen besitzen. Der Eventname wird bei der Objektinstantiierung zugewiesen, wenn man die Instruktion
NEW zusammen mit dem Schlüsselwort
AS verwendet. Dieser Eventname ist das Präfix aller Event-Handler-Methoden.
Beispiel
Dies erzeugt einen
Button, der Events erzeugt.
DIM hButton AS Button
hButton = NEW Button(ME) AS "ButtonEventName"
Wenn kein Eventname spezifiziert wird, dann wird das Objekt keine Events erzeugen.
2.2. Sperren von Objekten
Ein Objekt kann gesperrt (locked) werden, sodass es aufhört, Events zu produzieren. Wenn es wieder befreit wird, ruft es erneut Events hervor.
Siehe die
Object.Lock()- und
Object.Unlock()-Methoden.
Einige Events können durch den Event-Handler abgebrochen werden, indem man die
STOP EVENT-Instruktion verwendet.
Der Effekt dieses Abbruchs hängt vom jeweiligen ab.
Ein Objekt wird automatisch wären der Ausführung seines Konstruktors gesperrt: es kann keine Events erzeugen oder empfangen.
2.3. Observer
Observer sind Objekte, die es erlauben, Events abzufangen, die von anderen Objekten generiert werden. Sie "überwachen" sie.
Man kann Events abfangen, kurz bevor oder wahlweise kurz nachdem sie erzeugt wurden.
Für jedes abgefangene wird der Observer eines mit demselben Namen und denselben Argumenten hervorrufen.
Wenn man
STOP EVENT in einem Observer-Event-Handler verwendet, kann man das eigentliche Event abbrechen.
Beispiel
PRIVATE $hButton as Button
PRIVATE $hObserver AS Observer
PUBLIC SUB Form_Load()
$hButton = NEW Button(ME) AS "Button"
$hObserver = NEW Observer(hButton) AS "Observer"
END
PUBLIC SUB Observer_Click()
DEBUG "Der Button wurde geklickt. Ich breche das Event ab!"
STOP EVENT
END
PUBLIC SUB Button_Click()
DEBUG "Du solltest mich nicht sehen."
END
3. Vererbung
Vererbung ist die Möglichkeit einer Klasse, eine spezialisierte Version einer anderen Klasse zu werden.
3.1. Was wird vererbt?
Die Klasse erbt von ihrer Elternklasse alle Methoden, Eigenschaften, Konstanten und Events.
Man muss das Schlüsselwort
ME benutzen, um ererbte Elemente aus der erbenden Klasse heraus anzusprechen.
3.2. Welche Klasse kann Elternklasse sein?
Man kann jede Klasse ableiten, sogar native!
Bspw. kann man eine eigene MyListBox-Klasse kreieren, die von
ListBox erbt, aber es erlaubt, einen Tag mit jedem Listenelement zu assoziieren.
Man beachte, dass man
INHERITS
nicht in einer Form-Klasse verwenden kann, weil diese bereits implizit von der Klasse
Form erben.
Die Tiefe des Vererbungsbaums darf nicht größer als 16 sein. Dies ist ein konstantes, eingebautes Limit des Gambas-Interpreters.
3.3. Virtual dispatching
Wenn über eine Objektreferenz eine Methode aufgerufen wird oder auf eine Eigenschaft zugegriffen, dann benutzt Gambas
dispatching.
Das bedeutet, dass die wirkliche Klasse eines Objektes benutzt wird und nicht der Typ der Variablen, von der die Referenz gehalten wird - wie es noch in Gambas 1.0 war.
3.4. Vererbung und der Konstruktor
Im Gegensatz zu allen objektorientierten Sprachen, die ich kenne, werden in Gambas im Konstruktur einer erbenden Klasse alle Konstruktor-Parameter der vererbenden Klassen notiert.
Angenommen, wir haben diesen Vererbungsbaum:
MyListBox ---erbt--> ListBox ---erbt---> Control
-
Control._new() existiert nicht.
-
ListBox._new() übernimmt einen Parameter: das Elternsteuerelement.
-
MyListBox._new() übernimmt einen Parameter: einen Namen - nur ein Beispiel.
Also wird
New MyList
zwei parameter übernehmen:
-
der erste wird an MyListBox._new() übergeben;
-
der zweite an ListBox._new().
Vorsicht:
ListBox._new() wird zuerst aufgerufen, um sicherzustellen, dass die ListBox existiert, wenn MyListBox._new() aufgerufen wird.
Eine MyListBox wird wie folgt erzeugt:
hMyListBox = NEW MyListBox("Name", hContainer)
Die Reihenfolge der Argument ist wie folgt:
-
verlangte Argumente werden zuerst notiert, dann optionale, wenn vorhanden;
-
Argumente vererbender Klassen werden zuerst notiert.
In Gambas 2.0, ist die Reihenfolge umgekehrt!
Bspw., wenn wir die folgende Vererbungshierarchie haben:
MyForm --> Form --> Window
mit dem MyForm-Konstruktor:
Sub _new(ErstesArg As String, ZweitesArg as Integer, Optional DrittesArg As Integer)
Man bemerke: der
Form-Konstruktor übernimmt kein Argument, der
Window-Konstruktor übernimmt ein optionales Argument.
Die Signatur des finalen Konstruktors wird also
New MyForm(ErstesArg As String, ZweitesArg As Integer, Optional ElternStrgelem As Control, Optional DrittesArg As Integer)
sein. Auf allgemeinere Weise, die Reihenfolge der Argumente für eine Vererbung dritten Grades ist:
-
verlangte Argumente des Großelternkonstruktors;
-
verlangte Argumente des Elternkonstruktors;
-
verlangte Argumente des finalen (Kind-)Konstruktors;
-
optionale Argumente des Großelternkonstruktors.
-
optionale Argumente des Elternkonstruktors;
-
optionale Argumente des finalen Konstruktors;
3.5. Überschreiben von Symbolen (Overriding)
Wenn ein Symbol überschrieben werden soll, muss die Signatur des Symbols in der Kindklasse dieselbe sein, wie die der Elternklasse.
Die Regeln sind:
-
ein dynamisches Symbol muss von einem dynamischen Symbol ersetzt werden, ein statisches von einem statischen;
-
eine Methode muss mit einer Methode mit exakt derselben Signatur (Argumentdatentypen, Rückgabewert, wenn vorhanden) überschrieben werden;
-
eine les-/schreibbare Eigenschaft muss von einer les-/schreibbaren Eigenschaft desselben Datentyps überschrieben werden;
-
eine nur-lesbare Eigenschaft muss von einer nur-lesbaren Eigenschaft desselben Typs überschrieben werden;
-
eine Konstante kann nur von einer Konstante desselben Typs überschrieben werden.
4. Komponenten (Components)
Gambas sind externe, dynamische Bibliotheken, die in C, C++ oder in Gambas selbst geschrieben wurden und neue Funktionen und/oder Klassen zum Interpreter hinzufügen.
Klassen werden in der Komponente gruppiert, von der sie kommen.
4.1. Interne Komponente
Der Interpreter beinhaltet eine interne Komponente namens
gb, die alle Standardklassen der Sprache definiert.
Diese Komponente wird immer geladen und kann als Teil der Sprache betrachtet werden.
4.2. Symboltabellen
Each component has its own private class symbol table, so that class names do not conflict.
Jede Komponente verfügt über ihre private Symboltabelle, sodass Klassen gleichen Namens nicht in Konflikt miteinander geraten.
4.3. Globale Symboltabelle
Damit Komponenten miteinander interagieren können gibt es eine globale Symboltabelle, in der alle von Komponenten und dem aktuell ausgeführten Projekt exportierten Klassen aufgelistet sind.
Wenn ein Namenskonflikt zweier Klassen in der globalen Symboltabelle entsteht, überschreibt die zuletzt geladene Klasse die vorherige desselben Namens unter Verwendung von Vererbung. Mit anderen Worten, diese überschreibende Klasse erweitert die überschriebene.
Diese letzte Besonderheit kann benutzt werden:
-
um eine bereits deklarierte Klasse zu erweitern, indem neue Methoden oder Eigenschaften hinzugefügt werden. Z.B., Application-Klasse aus
gb.qt4
erweitert die Application-Klasse aus gb
;
-
um bereits existente Symbole zu überschreiben. Z.B., die
gb.form.dialog=-Komponente ersetzt die meisten statischen Methoden der Dialog-Klasse.
4.4. Projekt-Symboltabelle
Das aktuell ausgeführte Projekt besitzt ebenfalls seine private Symboltabelle, wie jede Komponente, und kann ihre Klassen in die globale Symboltabelle exportieren, indem das Schlüsselwort Export benutzt wird.
Die Projekt-Klassen werden nach den Komponenten geladen. Somit wird sichergestellt, dass die lokalen Klassen diejenigen der benutzten Komponenten überschreiben können.
Siehe auch