Bonjour, (désolé pas trop de place pour le titre
)
Voilà en fait je me demande c'est quoi la meilleur méthode pour modifier des valeurs de classes dérivées en java.
Par exemple j'ai une classe Monstre qui est abstraite et possède un attribut Point de Vie (entier).
Et par exemple j'étend monstre et je fais une classe "LoupGarou".
Je veux que tous les loups garoux aient 300 pv, tout en forçant le fait que chaque classe qui étend monstre doit FORCEMENT avoir un nombre de PV défini dans le constructeur pour que les programmeurs qui étendent cette classe oublient pas de mettre des PV (même si je suis tout seul
)
Comment on fait ?
Si je met l'attribut PV final dans la classe mère, il faudra affecter la valeur dans la classe abstraite donc ça marche pas
Si je le fait dans le constructeur direct, y'a aucune vérification.
Ce que je fais d'habitude c'est une methode abstraite (qui doit donc être overridé là) protected abstract int setPVCourant();
Et dans le constructeur d'Ennemi je fais
PV = setPVCourant();
et dans la classe LoupGarou je fais
@Override
protected int setPVCourant(){
Return PV_COURANT;
}
où PV_COURANT est un final static int de 300
Y'a pas un moyen plus propre faire ça ? ![]()
ça me paraît dégueulasse ![]()
(sachant que dans le constructeur de LoupGarou j'appelle bien sur le constructeur de Ennemi, mais que le constructeur de LoupGarou est vide parce que je veux pas que des LoupGarou aient des PV différents et que je veux garder leur constructeur simple parce qu'il faudra créer plusieurs loups garous différents mais qui auront le même comportement et nb de PV)
Voilà j'espère être assez clair ![]()
"Je veux que tous les loups garoux aient 300 pv, tout en forçant le fait que chaque classe qui étend monstre doit FORCEMENT avoir un nombre de PV défini dans le constructeur pour que les programmeurs qui étendent cette classe oublient pas de mettre des PV (même si je suis tout seul
) "
Je pense que c'est ici ta faiblesse dans la conception. Pourquoi ne pas avoir une classe qui gère la création de tes monstres ?
LoupGarou lg = Createur.createLoupGarou();
Avec dans createur par exemple
final int PV_LOUP_GAROU = 300;
...
createLoupGarou(){return new LoupGarou(PV_LOUP_GAROU);}
createVampire(){return new Vampire(PV_VAMPIRE);
Merci, mais j'ai pensé à faire une factory comme ça mais au final ça fait des trucs hyper moche quand même car dans l'exemple réelle il y a beaucoup plus de trucs sur les PV (y'a les MP, la vitesse, l'or, l'experience donné à la mort, etc etc)
et du coup les constructeurs deviennent méga trop long et c'est totalement illisible si je veux remodifier des trucs après ![]()
En plus après on sait pas trop quelle valeur vaut quoi si on connait pas l'ordre alors faut vérifier à chaque fois et compter le nombre d'arguments qu'on passe au constructeurs pour trouver la bonne valeur à modifier
En fait il me faudrait un moyen pour faire des constructeurs plus lisible ![]()
de mémoire mais alors là c'est à prendre avec des pincettes, j'avais vu de très vieux jeux qui mettais les stats de base des persos dans des fichiers, du coup le constructeur se contentait (je suppose) de parser la feuille de stats
Pas d'autres solutions ?
Je pensais que c'était un problème commun de vouloir remplir les données d'une classe mère via une classe qui l'étend ![]()
les pro du java doivent être en vaccances ![]()
Tu peux toujours poser ton problème sur stackoverflow, c'est généralement très fiable
Stackoverflow j'y vais pour trouver des réponses mais j'avoue j'ai jamais ôsé poser une question là bas ![]()
j'vais voir merci ![]()
(enfin j'attends encore un peu ici quand même
)
"les pro du java doivent être en vaccances"
on est au moins deux à s'être dit qu'on avait pas envie de répondre.
En fait, le titre me fait fuir à des kilomètres (et pas à cause des accents manquants). Ma réaction a froid a été la suivante : "flemme de répondre encore une fois qu'il faut arrêter d'abuser de la POO quand on ne maîtrise rien".
Enfin, je vais essayer d'être un peu plus constructif...
"Si je met l'attribut PV final dans la classe mère, il faudra affecter la valeur dans la classe abstraite donc ça marche pas "
en effet.
"Si je le fait dans le constructeur direct, y'a aucune vérification. "
en effet. Mais maintenant je vais te poser une question : tu crois vraiment que tu pourras contrer toute la malveillance du monde à l'aide de fonctionnalités du langage ?
"Ce que je fais d'habitude c'est une méthode abstraite"
ça me parait cohérent avec ton besoin. C'est lourd-dingue, mais ça fait partiellement ce que tu veux... (au détail près que je me passerai volontier de PV_COURANT en fait). Cela dit, il faut être conscient que ça n'empêchera jamais un développeur malicieux de faire autre chose dans le constructeur de sa propre classe héritée.
Tous les attribut qui sont defini dans une classse sont rempli par celle ci. Je ne suis pas convaincu dans ta modelisation que tu as vraiment besoin d'un type LoupGarou. Est ce que ce n'est pas plus tot un type "Entity" avec les valeures qui vont bien qui sont inserer par le constructeur quand tu lui passes un parametre "loupgarou"?
As tu besoin de faire un "traitement special" dans le cas d'un loupgarou. Et quelque chose de fondamentallement complexe et pas juste une "si les dommage sont de type argent, alors double les dommages"
J'ai supposé que loup-garou faisait référence à une classe de monstres distincts.
Si ce n'est pas le cas, le fameux paramètre à 300 devrait en effet être un argument du constructeur et basta.
"'il faut arrêter d'abuser de la POO quand on ne maîtrise rien"
Je comprend pas cette remarque, je gère bien la POO quand même, je demande juste si y'a des astuces plus élégantes (comme le coup des Factory par exemple à ce que je fais pour justement palier à une éventuelle lacune et à priori non y'a pas vraiment de meilleurs solutions donc voilà c'est tout
Concernant les vérifications, en fait c'est pas vraiment par soucis de contourner mais plus par un soucis de clarté, c'est juste que j'ai pas envie de me retrouver avec des constructeurs qui appellent un constructeur de la classe mère qui fait genre super(truc1, truc2, truc3, truc4, truc5, truc6, truc7, truc8, truc9... truc 20), parce que c'est illisible
Quand tu dis que tu te passerais de PV_COURANT tu veux dire que t'écrirais directement la valeur en dur ? Genre return 300 ?
godrik
Oui j'ai besoin de créer une classe LoupGarou car ils ont des réactions bien spécifiques (c'est surtout au niveau de l'IA en fait) et des animations spécifiques aussi en fonction de ce qu'ils font, etc
Le coup du types de dommages c'est géré par la classe mère
Bref merci, je vais continuer avec ce qui marche et voilà ![]()
"tu veux dire que t'écrirais directement la valeur en dur ? Genre return 300 ? "
oui, car cette valeur ne fait sens qu'à cet endroit (d'après ce que j'ai compris de ton problème).
"plus élégantes (comme le coup des Factory [...])"
alors là, je ris.
Surtout quand tu enchaînes avec un manifique "j'ai pas envie de me retrouver avec des constructeur [...] super(truc1, truc2, truc3, truc4, truc5, truc6, truc7, truc8, truc9... truc 20), parce que c'est illisible"
Déjà, ça reste relativement lisible (et même très efficace si tu as moins de 7 arguments). Et ensuite, c'est marrant de voir comment quelqu'un comme toi peut se jeter sur du soi-disant design pattern élégant tout en ignorant les notions de tableau et de structure.
Ne le prends pas pour toi, c'est valable pour tout le monde. Abuser de la POO, c'est néfaste pour vos programmes.
Oui c'est fait que dans un endroit, ok
Mais je connais les tableaux, mais je vais pas faire un tableau d'entier alors que les valeurs ont rien à voir avec elles ![]()
Et j'utilise une classe pour certains attributs (genre en vrai c'est stats.pv) mais j'ai pas détaillé au max le problème pour pas embrouiller le problème initial
Mais merci quand même ![]()
Et pourquoi pas une classe abstraite qui aurait comme méthodes abstraites celles que l'utilisateur de ton API "Monstre" doit absolument implémenter ? C'est le plus propre pour forcer un comportement.
Tu peux également passer par du chaînage d'objets d'objets immuables :
werewolf = monster.withHealth(300).withVulnerability(Material
s.silver).withHealthRegeneration(0.5);
C'est très modulaire.
"je vais pas faire un tableau d'entier alors que les valeurs ont rien à voir avec elles "
elles finissent côte à côte en mémoire une fois que le constructeur de ta classe à fait son office. Tu maintiens qu'elles n'ont rien à voir entre elles ?
Raspberry-Pi :
"Et pourquoi pas ... ?"
c'est ce qu'il fait, non ?
"Tu peux également passer par du chaînage"
viens me dire en face que c'est plus lisible que de passer simplement les valeurs dans un constructeur. ![]()
"Tu peux également passer par du chaînage"
viens me dire en face que c'est plus lisible que de passer simplement les valeurs dans un constructeur.
Moi j'achete ca en fait, c'est plus clair dnas le sens ou tu ne precise que les attribut qui ne sont pas a leur valeure par default.
Chris_27
Non, si les méthodes concernant ses valeurs qui-ont-absolument-besoin d'être initialisées sont abstraites, le développeur de l'API sera obligé de les implémenter et ne risque pas de les oublier, il n'y aurait donc pas de problème et OP n'aurait pas posé la question :D
PS : Non, ce n'est pas plus lisible (bien que...), mais plus modulaire. Il y a pas mal de frameworks orientés routage/filtrage qui fonctionnent selon ce principe.
Sinon, il peut toujours utiliser des décorateurs.
L'avantage est également que tu décris une nouvelle instance de façon assez visuelle. L'inconvénient du constructeur, c'est qu'il doit se décliner dans tous les cas que tu vas rencontrer et que tu n'as pas prévu (ce qui arrive souvent lorsque tu débute sur un projet dont les specs ne sont pas spécialement figées ou dans le cas présent où l'auteur aura de nouvelles idées au fil du temps).
godrik
Je rencontre cette technique de plus en plus. Si tu proposes un éditeur graphique, tu vas pouvoir de cette façon assez facilement combiner nouvelles propriétés et nouveaux comportements à tes objets étant donné que ça n'implique qu'un appel à une méthode et de réaffecter ton objet à son retour ![]()
raspberry, ce qui m'echappe c'est "pourquoi le faire immutable dans ce cas la? L'immutabilite c'est souvent assez penible si tu as d'autre pointeur (dans le sens large du terme, reference, indirection,...) vers ton objet.
Sinon pour l'aspect "tu ajoutes que ce que tu veux", je dois dire que j'ai commencer a faire toutes mes interfaces a base de map string->string avec une specialisation manuelle pour les avoir des vrai type apres.
genre character.set ("HP", "4"); avec la fonction set qui interprete ca comme hp = 4; quand je commence a voir que ca coute cher de caster des string.