Héritage de classe, qu'est-ce qui me manque ?Je rencontre un problème en écrivant une série de classes héritant les unes des autres.
j'ai réduit le code à l'exemple de base ci-dessous pour plus de simplicité.
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).
Je vois que lors de l'instanciation, le F_Type est correctement défini sur 3 ainsi que lorsque j'essaie d'atteindre la propriété à partir de l'objet lui-même.
Cependant, lorsque je suis à l'intérieur d'une méthode, la valeur de this:F_Type ou F_Type est pour une raison quelconque 2 (mais si j'accède à partir du tableau associatif stockant l'objet, la valeur est correctement 3). Assez bizarrement, la méthode est définie sur la classe Base (qui a le F_Type = 1, donc je ne peux pas comprendre pourquoi la valeur est 2. Cela pourrait-il être un bug d'un certain type ? (je vois cela à la fois dans JMP 17.1.0 et JMP 18 Early release, je n'ai pas de version antérieure disponible pour tester)
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();
Automatisation et scriptsRe : Héritage de classe, qu'est-ce qui me manque ?C'est probablement plus proche de ce que tu veux
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;
les classes dérivées utilisent super:_init_ pour s'assurer que leur classe de base est entièrement construite, puis continuent à initialiser la classe dérivée. Redéfinir le F_Type à chaque couche masque la classe de base F_Type, et non la remplacer. Je ne comprends pas pourquoi votre exemple montre F_Type=2 au lieu de F_Type=1 dans l'appel de méthode, mais le problème disparaît avec le modèle d'utilisation ci-dessus.
Re : Héritage de classe, qu'est-ce qui me manque ?Commencez par Comment utiliser Définir une classe
Je n'ai pas utilisé de classes depuis un moment, mais je suis presque certain que vous ne souhaitez pas respécifier la variable F_Type dans le cadre de la définition de la sous-classe ; vous devriez le définir dans la méthode _init_ de la sous-classe.
Re : Héritage de classe, qu'est-ce qui me manque ?Excellentes questions. La doc, telle qu'elle est, ne le couvre pas dans les détails que vous recherchez... du moins aucun que j'ai trouvé.
Je pense que vous avez une compréhension intuitive correcte de la manière de remplacer une méthode dans la classe de base ; l'attribution d'une nouvelle méthode dans l'initialisation dérivée fonctionnera, pour la même raison que le F_Type fonctionne lorsque F_Type n'est pas "redéclaré" dans la classe dérivée.
Je pense également que vous observez un bug, précédemment décrit par @hardner https://community.jmp.com/t5/Uncharted/How-to-use-Define-Class/bc-p/694620/highlight/true#M384 qui J'ai en partie oublié, mais à l'époque, j'étais convaincu que parfois un pointeur d'objet pouvait être vers l'objet entièrement dérivé et d'autres fois vers l'objet super:.
@julian - ce type de document doit être écrit par un développeur qui comprend pourquoi une méthode virtuelle est utile.
Re : Héritage de classe, qu'est-ce qui me manque ?Craig,
Merci encore pour la réponse très rapide.
Plus je joue avec cela, plus je suis convaincu que mon "remplacement" original de variable et de méthode devrait être tel qu'il devrait être et que le comportement actuel devrait être modifié.
Malheureusement, j'ai réalisé que l'ajout de la méthode dans _init_ n'est pas vraiment une solution utile pour l'héritage "multiniveau".
si une méthode devait être naturellement décrite dans la classe "moyenne", elle ne serait pas accessible par les niveaux inférieurs car l'ajout au _init_ la définirait uniquement pour l'objet actif que je crée mais n'agirait pas comme prototype pour la classe qui en dérive.
Il devrait en être de même pour les propriétés. Je pense que je devrais pouvoir les déclarer sans avoir besoin de _Init_. _Init_ ne doit être utilisé que lorsque la valeur est définie au moment de l'exécution et non prédéfinie (à mon avis), la définition dans _Init_ m'obligerait à la spécifier pour toutes les classes dérivées, pas seulement au niveau dont j'ai besoin avec plus d'effort de maintenance
Prenons l'exemple des formes. J'ai un polygone général auquel vous devez spécifier des "côtés" comme entrée au moment de l'exécution afin que je l'aie dans l'entrée et défini lorsque l'objet est initié. je m'attends à ce que pour le quadrilatère, je code en dur les "côtés" pour qu'ils soient 4. pour le carré, cependant, j'aimerais hériter du quadrilatère sans le définir à nouveau à 4.
pour les côtés, c'est bien... mais si j'ai besoin d'initialiser une longue liste ou un tableau associatif, c'est ennuyeux de le répéter pour chaque classe.
Quel est ton opinion?
Re : Héritage de classe, qu'est-ce qui me manque ?Merci pour la réponse.
Bien que réadapter le code soit assez simple pour que je puisse continuer à travailler, je ne comprends pas vraiment ce qui se cache derrière les rideaux.
lorsque j'appelle directement les propriétés, elle obtient la bonne valeur (3), ce n'est qu'à partir de la méthode que la valeur est 2 (et non 1), j'ai également essayé d'ajouter un autre niveau (et c'est toujours le niveau n-1 qui est considéré).
De plus, cela soulève une autre question : qu'en est-il de l'héritage des méthodes qui sont également stockées dans des variables... dois-je également définir les méthodes spécifiques à partir du "_init_" ?.
Re : Héritage de classe, qu'est-ce qui me manque ?J'ai vérifié et le même comportement est également vrai pour les méthodes.
si vous appelez une méthode spécifique à partir d'une autre méthode et que la méthode "cible" diffère par les différents niveaux d'héritage, la méthode de la classe "un niveau supérieur" dans la pile est utilisée (je ne suis pas sûr que le libellé ait du sens).
de même, si je définis la méthode de la classe dérivée à partir de la méthode "_init_", tout fonctionne comme prévu
Alors que pour les variables, je peux comprendre la logique de faire dans la méthode "_init_", pour les méthodes cela semble contre-intuitif.
Y a-t-il de la documentation à ce sujet ?
voici l'exemple modifié :
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);
Re : Héritage de classe, qu'est-ce qui me manque ?J'ai essayé d'utiliser les classes plusieurs fois et j'ai trouvé de bons cas d'utilisation (passage par référence), des bugs et des fonctionnalités manquantes (ceci).
Je suis encore en train d'affiner mon modèle pour son fonctionnement, merci pour les questions et la discussion.
- Je pense qu'il sera utile de considérer chaque sous-classe comme ayant son propre ensemble de variables membres de type espace de noms et un lien caché vers l'ensemble de variables membres de la classe de base suivante. Et considérez cela comme cet espace de noms. Vous aimeriez que ce soit la classe la plus dérivée, car à partir de là, vous pouvez suivre les liens vers la classe la plus basique et tout apprendre. Ce qui peut mal tourner, ou du moins contre toute attente, c'est parfois qu'après l'appel d'une méthode, cela devient l'espace de noms dans lequel réside la méthode. Il n'y a aucun lien vers la sous-classe. Cela n'a pas d'importance, sauf si la méthode s'attend à appeler des fonctions virtuelles dans la sous-classe. Cela fera donc une différence si la sous-classe a utilisé _init_ pour remplacer la fonction virtuelle dans la classe de base (OK) ou l'a assignée dans l'espace de noms de la fonction virtuelle (Fails).
- Je suis presque certain qu'il y a plus de copies en cours que vous ne l'espérez, et cela est fait pour faire fonctionner l'héritage multi-niveaux. Lorsqu'une classe à 3 niveaux est créée, la classe de base est copiée, puis celle du milieu, puis la plus dérivée. Je crois que les appels _init_ imbriqués se produisent après cela.