Klassenvererbung, was fehlt mir?Beim Schreiben einer Reihe von Klassen, die voneinander erben, stoße ich auf ein Problem.
Der Einfachheit halber habe ich den Code auf das folgende Grundbeispiel reduziert.
Basically i have a series of classes inheriting from each other in cascade (called MyBaseClass, MyDerivedClass, MyDoubleDerived)and they differ for the value assigned to the F_Type property (value 1 2 and 3 respectively). i am creating an instance of MyDoubleDerived Class assigning it to a variable (and also storing into an Associative array where i can get to it by its ID).
Ich sehe, dass der F_Type während der Instanziierung korrekt auf 3 gesetzt ist und auch, wenn ich versuche, die Eigenschaft vom Objekt selbst aus zu erreichen.
Wenn ich mich jedoch in einer Methode befinde, ist der Wert von this:F_Type oder F_Type aus irgendeinem Grund 2 (wenn ich jedoch über das assoziative Array zugreife, in dem das Objekt gespeichert ist, ist der Wert korrekt 3). Seltsamerweise ist die Methode in der Basisklasse definiert (die den F_Type = 1 hat, daher kann ich nicht verstehen, warum der Wert 2 ist. Könnte das ein Fehler irgendeiner Art sein? (Ich sehe das sowohl in JMP 17.1.0 als auch in JMP 18 Early Version, ich habe keine frühere Version zum Testen verfügbar)
If(!Namespace Exists( "MyNameSp" ),
NS=New Namespace( "MyNameSp" );
);
MyNameSp:ARList=associative array(); // i store here the object that i can access via its key for convenience
<BR />// below the fucntion to instantiate the object from the class
MyNameSp:CreateNewObj=Function({ClassName, NewKey},
{Default Local},
ObjRef=NewObject(ClassName,{NewKey});
show(ObjRef:ObjID);
MyNameSp:ARList[ObjRef:ObjID]=ObjRef;
return(ObjRef);
);
Define Class( "MyBaseClass",
F_Type=1;
ObjID="";
_init_=Method({NewKey},
this:ObjID=NewKey;
show("value during initialization");
show(this:F_Type);
show(F_Type);
);
MyMethod=Method({},
show("value in method");
show(F_Type);
show(this:F_Type);
show(this:ObjID);
show(MyNameSp:ARList[this:ObjID]:F_Type);
);
);
Define Class( "MyDerivedClass",
baseclass( MyBaseClass ),
F_Type=2;
);
////////////////////////////////////////////////////////////////////////////
Define Class( "MyDoubleDerived",
baseclass( MyDerivedClass ),
F_Type=3;
);
<BR />// start of the actual script
SampleObj=MyNameSp:CreateNewObj("MyDoubleDerived","Key1");
show("value From script");
show(SampleObj:F_Type);
SampleObj:MyMethod();
Automatisierung und SkripterstellungBetreff: Klassenvererbung, was fehlt mir?Ich habe es noch einmal überprüft und das gleiche Verhalten gilt auch für Methoden.
Wenn Sie eine bestimmte Methode von einer anderen Methode aufrufen und sich die „Ziel“-Methode in der unterschiedlichen Vererbungsebene unterscheidet, wird die Methode der „eine Ebene höher“-Klasse im Stapel verwendet (nicht sicher, ob der Wortlaut sinnvoll ist).
Ebenso funktioniert alles wie erwartet, wenn ich die Methode der abgeleiteten Klasse innerhalb der Methode „_init_“ definiere
Während ich für Variablen die Logik der Vorgehensweise in der Methode „_init_“ verstehen kann, erscheinen mir die Methoden kontraintuitiv.
Gibt es eine Dokumentation dazu?
hier das modifizierte Beispiel:
If(!Namespace Exists( "MyNameSp" ),
NS=New Namespace( "MyNameSp" );
);
MyNameSp:ARList=associative array();
MyNameSp:CreateNewObj=Function({ClassName, NewKey},
{Default Local},
ObjRef=NewObject(ClassName,{NewKey});
show(ObjRef:ObjID);
MyNameSp:ARList[ObjRef:ObjID]=ObjRef;
return(ObjRef);
);
Define Class( "MyBaseClass",
F_Type=1;
K_Type=1;
ObjID="";
_init_=Method({NewKey},
this:ObjID=NewKey;
);
MyChangeInitMethod=Method({PassedType, passedID},
show("INIT Base Level method (1) ",PassedType, passedID);
);
MyMethod=Method({},
this:MyChangeMethod(this:F_Type,this:ObjID);
this:MyChangeInitMethod(this:F_Type,this:ObjID);
);
MyChangeMethod=Method({PassedType, passedID},
show("Base Level method (1) ",PassedType, passedID);
);
);
Define Class( "MyDerivedClass",
baseclass( MyBaseClass ),
F_Type=2;
_init_=Method({NewKey},
this:ObjID=NewKey;
K_Type=2;
MyChangeInitMethod=Method({PassedType, passedID},
show("INIT Derived class Method (2)",PassedType, passedID);
);
);
MyChangeMethod=Method({PassedType, passedID},
show("Derived class Method (2)",PassedType, passedID);
);
);
////////////////////////////////////////////////////////////////////////////
Define Class( "MyDoubleDerived",
baseclass( MyDerivedClass ),
F_Type=3;
_init_=Method({NewKey},
this:ObjID=NewKey;
K_Type=3;
MyChangeInitMethod=Method({PassedType, passedID},
show("INIT double Derived class method (3)",PassedType, passedID);
);
);
MyChangeMethod=Method({PassedType, passedID},
show("double Derived class method (3)",PassedType, passedID);
);
);
////////////////////////////////////////////////////////////////////////////
Define Class( "MyTripleDerived",
baseclass( MyDoubleDerived ),
F_Type=4;
_init_=Method({NewKey},
this:ObjID=NewKey;
K_Type=4;
MyChangeInitMethod=Method({PassedType, passedID},
show("INIT Triple Derived class method (4)",PassedType, passedID);
);
);
MyChangeMethod=Method({PassedType, passedID},
show("Triple Derived class method (4)",PassedType, passedID);
);
);
SampleObj1=MyNameSp:CreateNewObj("MyBaseClass","Key1");
SampleObj2=MyNameSp:CreateNewObj("MyDerivedClass","Key2");
SampleObj3=MyNameSp:CreateNewObj("MyDoubleDerived","Key3");
SampleObj4=MyNameSp:CreateNewObj("MyTripleDerived","Key4");
show("----------------Calling the method via a method-------------------");
show(" 1 ");
SampleObj1:MyMethod();
show(" 2 ");
SampleObj2:MyMethod();
show(" 3 ");
SampleObj3:MyMethod();
show(" 4 ");
SampleObj4:MyMethod();
show("----------------Clling the method directly------------------------");
show(" 1 ");
SampleObj1:MyChangeMethod(SampleObj1:F_Type,SampleObj1:ObjID);
show(" 2 ");
SampleObj2:MyChangeMethod(SampleObj2:F_Type,SampleObj2:ObjID);
show(" 3 ");
SampleObj3:MyChangeMethod(SampleObj3:F_Type,SampleObj3:ObjID);
show(" 4 ");
SampleObj4:MyChangeMethod(SampleObj4:F_Type,SampleObj4:ObjID);</CODE></P><P> </P></LINGO-BODY><LINGO-SUB id="lingo-sub-725797" slang="en-US" mode="CREATE">Betreff: Klassenvererbung, was fehlt mir?</LINGO-SUB><LINGO-BODY id="lingo-body-725797" slang="en-US" mode="CREATE"><P>Danke für die Antwort.</P><P>Obwohl die Neuanpassung des Codes so einfach ist, dass ich weiterarbeiten kann, verstehe ich nicht wirklich, was sich hinter den Kulissen verbirgt.</P><P>Wenn ich die Eigenschaften direkt aufrufe, erhält es den richtigen Wert (3). Nur innerhalb der Methode ist der Wert 2 (und nicht 1). Ich habe auch versucht, eine weitere Ebene hinzuzufügen (und das ist immer die n-1-Ebene). gilt als).</P><P> </P><P>Darüber hinaus wirft dies eine weitere Frage auf: Was ist mit der Vererbung von Methoden, die auch in Variablen gespeichert sind? Sollte ich auch die spezifischen Methoden innerhalb von „_init_“ definieren?</P><P> </P><P> </P></LINGO-BODY><LINGO-SUB id="lingo-sub-725714" slang="en-US" mode="CREATE">Betreff: Klassenvererbung, was fehlt mir?</LINGO-SUB><LINGO-BODY id="lingo-body-725714" slang="en-US" mode="CREATE"><P>Dies kommt wahrscheinlich näher an dem, was Sie wollen</P>
<PRE><CODE class=" language-jsl">If(!Namespace Exists( "MyNameSp" ),
NS=New Namespace( "MyNameSp" );
);
MyNameSp:ARList=associative array(); // i store here the object that i can access via its key for convenience
// below the fucntion to instantiate the object from the class
MyNameSp:CreateNewObj=Function({ClassName, NewKey},
{Default Local},
ObjRef=NewObject(ClassName,{NewKey});
show(ObjRef:ObjID);
MyNameSp:ARList[ObjRef:ObjID]=ObjRef;
return(ObjRef);
);
Define Class( "MyBaseClass",
F_Type=1;
ObjID="";
_init_=Method({NewKey},
this:ObjID=NewKey;
show("value during initialization");
show(this:F_Type);
show(F_Type);
);
MyMethod=Method({},
show("value in method");
show(F_Type);
show(this:F_Type);
show(this:ObjID);
show(MyNameSp:ARList[this:ObjID]:F_Type);
);
);
Define Class( "MyDerivedClass",
baseclass( MyBaseClass ),
// F_Type=2;
_init_=Method({NewKey},
super:_init_(NewKey);
F_Type=2;
show("MyDerivedClass",F_Type);
);
);
////////////////////////////////////////////////////////////////////////////
Define Class( "MyDoubleDerived",
baseclass( MyDerivedClass ),
// F_Type=3;
_init_=Method({NewKey},
super:_init_(NewKey);
F_Type=3;
show("MyDoubleDerived",F_Type);
);
);
// start of the actual script
SampleObj=MyNameSp:CreateNewObj("MyDoubleDerived","Key1");
show("value From script");
show(SampleObj:F_Type);
SampleObj:MyMethod();
"value during initialization";
this:F_Type = 1;
F_Type = 1;
// above, the base class is constructed (at beginning of intermediate)
"MyDerivedClass";
F_Type = 2;
// above, the intermediate class is constructed (at the beginning of most derived)
"MyDoubleDerived";
F_Type = 3;
// above, the most derived class is constructed
ObjRef:ObjID = "Key1";
"value From script";
// below: there is really only one variable F_Type, belonging to the base class
SampleObj:F_Type = 3;
"value in method";
F_Type = 3;
this:F_Type = 3;
this:ObjID = "Key1";
MyNameSp:ARList[this:ObjID]:F_Type = 3;
Die abgeleiteten Klassen verwenden super:_init_, um sicherzustellen, dass ihre Basisklasse vollständig erstellt ist, und fahren dann mit der Initialisierung der abgeleiteten Klasse fort. Durch die Neudefinition des F_Type auf jeder Ebene wird die Basisklasse F_Type ausgeblendet, nicht ersetzt. Ich verstehe nicht, warum Ihr Beispiel im Methodenaufruf F_Type=2 statt F_Type=1 anzeigt, aber das Problem verschwindet mit dem obigen Verwendungsmuster.
Betreff: Klassenvererbung, was fehlt mir?Beginnen Sie mit der Verwendung von Define Class
Ich habe seit einiger Zeit keine Klassen mehr verwendet, bin mir aber ziemlich sicher, dass Sie die F_Type-Variable nicht als Teil der Unterklassendefinition neu angeben möchten. Sie sollten es in der Unterklassenmethode _init_ festlegen.
Betreff: Klassenvererbung, was fehlt mir?Tolle Fragen. Das Dokument, so wie es ist, deckt es nicht in der von Ihnen gesuchten Ausführlichkeit ab ... zumindest keine, die ich gefunden habe.
Ich glaube, Sie haben das richtige intuitive Verständnis dafür, wie man eine Methode in der Basisklasse überschreibt; Das Zuweisen einer neuen Methode in der abgeleiteten Init-Klasse funktioniert. Aus dem gleichen Grund funktioniert F_Type, wenn F_Type in der abgeleiteten Klasse nicht „neu deklariert“ wird.
Ich glaube auch, dass Sie einen Fehler beobachten, der zuvor von @hardner https://community.jmp.com/t5/Uncharted/How-to-use-Define-Class/bc-p/694620/highlight/true#M384 beschrieben wurde Ich habe es teilweise vergessen, aber damals war ich überzeugt, dass sich ein Objektzeiger manchmal auf das vollständig abgeleitete Objekt und manchmal auf das super:-Objekt beziehen könnte.
@julian – diese Art von Dokument muss von einem Entwickler geschrieben werden, der versteht, warum eine virtuelle Methode nützlich ist.
Betreff: Klassenvererbung, was fehlt mir?Craig,
Nochmals vielen Dank für die sehr schnelle Antwort.
Je mehr ich damit spiele, desto mehr bin ich davon überzeugt, dass meine ursprüngliche „Überschreibung“ von Variable und Methode so sein sollte, wie sie sein sollte, und dass das aktuelle Verhalten geändert werden sollte.
Leider wurde mir klar, dass das Hinzufügen der Methode in _init_ keine wirklich sinnvolle Lösung für die „mehrstufige“ Vererbung ist.
Wenn eine Methode natürlich in der „mittleren“ Klasse beschrieben werden sollte, wäre sie für die unteren Ebenen nicht zugänglich, da das Hinzufügen zu _init_ sie nur für das von mir erstellte aktive Objekt festlegt, aber nicht als Prototyp für die davon abgeleitete Klasse fungiert.
Das gleiche Verhalten sollte auch für die Eigenschaften gelten. Ich denke, es sollte möglich sein, sie zu deklarieren, ohne dass _Init_ erforderlich ist. _Init_ sollte nur verwendet werden, wenn der Wert zur Laufzeit festgelegt und nicht vordefiniert ist (meiner Meinung nach). Bei der Definition in _Init_ müsste ich ihn für alle abgeleiteten Klassen angeben, nicht nur auf der Ebene, die ich benötige, was einen höheren Wartungsaufwand mit sich bringt
Machen wir das Beispiel der Formen. Ich habe ein allgemeines Polygon, für das Sie zur Laufzeit „Seiten“ als Eingabe angeben müssen, damit ich es in der Eingabe habe und es definiert, wenn das Objekt initiiert wird. Ich gehe davon aus, dass ich für ein Viereck die „Seiten“ fest auf 4 codieren würde. Für das Quadrat würde ich jedoch gerne vom Viereck erben und es nicht erneut auf 4 definieren.
für Seiten ist in Ordnung ... aber wenn ich eine lange Liste oder ein assoziatives Array initialisieren muss, ist es nervig, es für jede Klasse zu wiederholen.
Was ist deine Meinung?
Betreff: Klassenvererbung, was fehlt mir?Ich habe mehrmals versucht, die Klassen zu verwenden, und einige gute Anwendungsfälle (Pass-by-Reference), einige Fehler und einige fehlende Funktionen (dies) gefunden.
Ich verfeinere immer noch die Funktionsweise meines Modells, danke für die Fragen und die Diskussion.
- Ich denke, es wird hilfreich sein, sich jede Unterklasse so vorzustellen, dass sie über einen eigenen Namensraum-ähnlichen Satz von Mitgliedsvariablen und einen versteckten Link zum nächsten Satz von Mitgliedsvariablen der Basisklasse verfügt. Und stellen Sie sich das als diesen Namensraum vor. Sie möchten, dass dies die am stärksten abgeleitete Klasse ist, denn von dort aus können Sie den Links zur am weitesten verbreiteten Klasse folgen und alles lernen. Was schief gehen kann, oder zumindest wider Erwarten, ist, dass beim Aufruf einer Methode manchmal dieser Namespace zum Namensraum wird, in dem sich die Methode befindet. Es gibt keinen Link zurück zur Unterklasse. Es spielt keine Rolle, es sei denn, die Methode erwartet, virtuelle Funktionen in der Unterklasse aufzurufen. Es macht also einen Unterschied, ob die Unterklasse _init_ verwendet hat, um die virtuelle Funktion in der Basisklasse zu ersetzen (OK) oder sie dem Namespace der virtuellen Funktion zugewiesen hat (Fehlgeschlagen).
- Ich bin mir ziemlich sicher, dass mehr kopiert wird, als Sie erhoffen, und dass dies geschieht, damit die mehrstufige Vererbung funktioniert. Wenn eine 3-Ebenen-Klasse erstellt wird, wird die Basisklasse kopiert, dann die mittlere und dann die am meisten abgeleitete Klasse. Ich glaube, dass die verschachtelten _init_-Aufrufe danach erfolgen.