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