Morgengrauner Dokumentation
Dateipfad: /home/mud/mudlib/doc//LPC/inheritanceKONZEPT
inheritance
BESCHREIBUNG
Mittels Vererbung kann man das Verhalten und/oder die implementierten
Methoden eines Objektes in ein neues Objekt hineinerben.
1. Wozu ist Vererbung gut
1.1. Erben von Implementationen: Strukturieren von Bibliotheken
Mit Vererbung der Implementation koennen aufeinander aufbauende und gut
wart-/nutzbare Strukturen geschaffen werden:
/std/thing/description beinhaltet
- wie ein Objekt aussieht/welche Details es gibt
- wie man das Objekt finden/identifizieren kann
/std/thing/moving beinhaltet
- wie man ein Objekt bewegen kann
/std/thing erbt
- /std/thing/description und
- /std/thing/moving
- damit den Code aus beiden Objekten (und noch andere)
Sowohl thing/description als auch thing/moving sind nicht als
eigenstaendige Objekte sinnvoll nutzbar, sind abstrakt.
1.2. Erben von Schnittstellen und Verhalten
Durch das Erben von /std/thing werden Schnittstelle und auch
Verhalten von /std/thing geerbt:
-- keks.c --
inherit "/std/thing";
--
Dadurch koennen jetzt andere Objekte, wie NPCs oder Spieler mit dem
Keks-Objekt so interagieren, als wenn es ein /std/thing waere.
Morgengrauen stellt eine grosse Bibliothek von miteinander sinnvoll
agierenden Objekten unter /std zur Verfuegung, Die dort verfuegbaren
Objekte sind groesstenteils selbsterklaerend, wie /std/npc,
/std/armour oder /std/weapon.
Das Keks-Objekt muss erweitert werden, wenn es sich vom normalen
Ding unterscheiden soll. Typischerweise geschieht das durch
Ueberschreiben der Initialisierungmethode namens "create".
2. Ueberschreiben/Erweitern von Verhalten/Methoden
2.1. Ueberschreiben
Um das Verhalten von geerbten Methoden zu erweitern, muss diese
ueberschrieben werden:
-- keks.c --
...
void create() {
SetProp(P_NAME, "Keks");
SetProp(P_GENDER, MALE);
AddId(({"keks"}));
}
--
Allerdings wuerde jetzt der normale Code von "create" in /std/thing
nicht mehr ausgefuehrt werden. Mit dem 'scope'-Operator :: wird
innerhalb einer ueberschriebenen Methode auf deren bisherige
Implementation zugegriffen:
-- keks.c --
...
void create() {
::create();
SetProp(P_NAME, "Keks");
SetProp(P_GENDER, MALE);
AddId(({"keks"}));
}
--
Auf geerbte globale Variablen kann normal zugegriffen werden, wenn
diese nicht vor direktem Zugriff durch erbende Objekte geschuetzt
wurden. Also in ihrer Sichtbarkeit veraendert wurde.
Ueberschreiben von Methoden in den davon erbenden Objekten kann durch
das Schluesselwort 'nomask' verboten werden.
2.2. Sichtbarkeit von Methoden und Variablen
Es ist moeglich, Methoden und Variablen mit einem Modifikator fuer
ihre Sichtbarkeit zu versehen, der auch bei der Vererbung zum
Tragen kommt:
- 'public' Methoden sind von aussen/innen in Eltern/Kind zugreifbar
- 'protected' Methoden sind nur von innen in Eltern/Kind zugreifbar
- 'private' Methoden sind nur von innen in Eltern zugreifbar
(also nur im definierenden Objekt selbst)
2.3 Laufzeit-Polymorphie (vielgestaltes Verhalten)
Methoden werden in LPC immer erst zum Zeitpunkt ihres Aufrufs
gebunden, also nicht schon beim Laden. Beispielsweise koennen
wir unseren Keks essbar machen:
-- keks.c --
...
void create() {
...
AddCmd("iss&@ID", "action_essen", "Was willst du essen?");
}
int action_essen() {
if(this_player()->eat_food(1, 0,
"Du bekommst "+QueryPronoun(WEN)+
" nicht mehr hineingestopft.\n")>0) {
write("Du isst "+name(WEN,1)+".\n");
say(this_player()->Name(WER)+" isst "+name(WEN)+".\n");
remove();
}
return 1;
}
--
und jetzt in einem davon erbenden Kruemelkeks diesen beim Entfernen
im "remove" kruemeln lassen:
-- kruemelkeks.c --
inherit "/doc/beispiele/inherit/keks.c";
...
varargs int remove(int silent) {
if(!silent && living(environment()))
tell_object(environment(), Name(WER,1)+
" kruemelt ein letztes Mal.\n");
return ::remove(silent);
}
--
Trotzdem wir "action_essen" nicht modifiziert haben, wird von dieser
Methode in einem Kruemelkeks immer automatisch die aktuelle und
kruemelende "remove" aufgerufen.
3. Multiple Inheritance
In LPC ist multiple Vererbung moeglich. Das heisst, man kann von
mehreren Objekten erben. Das kann zu Konflikten fuehren, zum Beispiel
wenn eine Methode in zwei Objekten von denen geerbt wurde vorkommt.
Diese Konflikte sollte man dann "per Hand" loesen in dem man diese
spezielle Methode ueberschreibt und mittels des scope-Operators die
gewuenschte(n) geerbt(en) Methode(n) aufruft:
inherit "/std/weapon";
inherit "/std/lightsource";
void create() {
weapon::create();
lightsource::create();
...
}
void init() {
weapon::init();
lightsource::init();
// oder sehr generell und unkontrolliert:
// "*"::init();
}
Schwerwiegende Konflikte koennen auftreten, wenn eine gleichnamige
Variable aus verschiedenen Objekten geerbt wird. Die Variablen
existieren im letztlich erbenden Objekt zweimal und die verschiedenen
geerbten Methoden greifen auf ihre jeweilige Variante zu.
Beispiel ist die sog. "Masche" oder der "Diamond of Death":
A (Ursprungsobjekt mit globaler Variable x)
/ \
B C (B, C erben von A und manipulieren beide x)
\ /
D (D erbt von B und A)
Mit dem Schluesselwort 'virtual' kann die Doppelung beim Erben
in B und C unterbunden werden.
SIEHE AUCH:
inherit
private, protected, public, nomask
virtual
objekte, oop
/doc/beispiele/inherit
2.Feb 2008 Gloinson
zurück zur Übersicht