Achtung, das abstract hier kollidiert mit abstract und vielliecht sollte man es hier ändern.
GraphicObject contains // enthält abstrakte Methoden, ergo nicht instanziierbar
{
/* Dieses Objekt wird automatisch initialisiert */
Name is string : "Gunther";
/* Dieses Objekt wird automatisch initialisiert, die Zuweisung ist aber überschreibbar */
Type is virtual string : "unknown object";
/* crude (unreif) erklärt, dass die Methode auch von unfertig konstruierten Objekten gerufen werden darf */
OperatorAsString is abstract crude method as string; // <- abstract: Wird hier nicht definiert
GetTypeDesc( TypeDesc ref ) is abstract method as bool; // <- abstract: Wird hier nicht definiert
/* Construct::body, destruct::body, copy::body und move::body rufen zuerst die Varianten ihrer Basisklasse auf. */
construct
/* Ruft eine unbenannte Funktion auf, die Type initialisiert */
{
print "Generiere {Type}\n"; // Type ist schon initialisiert
print "Typ is {OperatorAsString()}\n"; // OperatorAsString darf vom Konstruktor aufgerufen werden
}
destruct is virtual; // Wir haben abstrakte und virtuelle Funktionen, der Destruktor ist damit automatisch virtuell, aber man kann es ruhig auch sagen/per warning melden?
// ist virtuell, muss also nicht von abgeleiteten Klassen überschrieben werden
copy( value is thistype ) abstract // ist hier implementiert, verlangt aber, dass abgeleitete Klassen es ebenfalls implementieren.
{ /* Ohne Definition würde automatisch für beide Typen "copy" aufgerufen */
Name : value.Name;
Type : value.Type;
}
move( value is thistype rip )
{
Name : value.Name; // value.name ist string rip, wir reißen also die Daten raus -> Rest in Peace
Type : value.Type; // value.Type ist string rip, wir reißen also die Daten raus -> Rest in Peace, das ganze landet also in string::move( string rip );
}
// Factory muss die Konstruktoren noch nicht kennen, solange sie später gültig auftauchen
// Factory bekommt als Result einen GraphicsObject ptr. return muss also auch Speicher anfordern.
// wenn die Funktion Fehlschlagen kann muss sie als thiytype ptr agieren
create( value is string; x,y is double, r is double : 0. ) as thistype ptr
{
if( value == "Point" ) return Point( x, y );
if( value == "Circle" ) return Circle( Point(x,y), Radius(r) );
fail;
}
// Kann sie hingegen nicht fehlschlagen, darf ein thistype ref erwartet werden
create( value is TypeEnum; x,y is double, r is double : 0. ) //as thistype var ref
{
switch( value )
{
case TypeEnum::Point: return Point( x, y );
case TypeEnum::Circle:
assert( r ==! default ); // r muss gesetzt worden sein, sonst Kompilierabbruch (static_assert)
assert( r ==! 0. ); // Laufzeitüberprüfung zu Debugzwecken, wird nur im Debugmode geloggt, kein fail.
return Circle( Point(x,y), Radius(r) );
// default nicht erforderlich, weil es nur die zwei Enums gibt, also alle Fälle abgedeckt sind
}
// hier kann man nicht ankommen, weil alle Pfade vorher in einem return enden
}
};
c = GraphicObject.create( TypeEnum::Circle, 2, 3, 5 );
p = GraphicObject.create( TypeEnum::Point, 1, 2 );
cPtr = GraphicObject.create( "Circle", 4, 5 /* Radius ist 0 */ );
overrides Alternativen
swaps statt overrides (anregung von Michael Bachnick)
change (michael)
shifts
passes
Point is GraphicObject
contains
{
Type swaps string : "Point";
OperatorAsString swaps method : "Point";
x,y is double : [0., 0.]; // Default-Initialisierung
construct( x, y );
}
Radius contains double : 0;
Bla is Point(0,0), Radius(5); // Objekt "Bla" wird gleich instanziiert, existiert also
Circle is Point, Radius; // SecondCircle ist nur Typ, solange es nicht benutzt wird (also Funktionen gerufen werden oder Variablen abgefragt oder gesetzt werden)
MyCircle is Circle( Point(0,0), Radius(5) ); // Alle Konstruktoren werden bedient, MyCircle ist ein initialisiertes Circle-Objekt.