Générer des classes automatiquement

a marqué ce sujet comme résolu.

Bonjour, je me demandais s'il était possible de générer des classes automatiquement et les ajoutés à un programme qui tourne déjà.

En fait je voudrais écrire des classes sur demande de l'utilisateurs avec les paramètres qu'il me donne. Pour le code en lui même, je pense que le plus pratique c'est de préparer le code puis l'écrire dans un fichier proprement et plusieurs fichiers si besoin. Le problème concerne plutôt l'intégration à ce qui tourne déjà. Comment l'intégrer proprement ? Existe il des méthodes générale ou c'est spécifique au projet?

Merci d'avance et bonne journée

En fait c'est un projet inspiré du Javaquarium (un exercice sur le site). C'est un petit peu plus poussé mais c'est grossomodo la même chose. Et ce que nous aimerions faire c'est permettre à un utilisateur de créé ses espèces avec une interface "boite" (en fait il code pas quoi). Il choisit son algorithme de reproduction (oui c'est avec des poissons), son algo de vieillissement. Et il peut aussi choisir si ses entités ont tel ou tel caractéristique etc etc.

J'espère que tu vois un peu plus ce que je veux faire et pourquoi je veux le faire.

En fait je me demande d'abord si c'est possible, et ensuite que chercher sur google parce que j'ai regardé du côté de la metaprogrammation mais ça n'a pas donné grand chose en fait. Et s'il existe une méthode universelle bien sûr me la donner (ou des liens ça reviens au même)

Salut,

je vois pas trop où tu bloquerais la dedans ? une espece c'est un certains nombre de propriété (des valeurs d'attributs de classe), différents algorithme de reproduction ? c'est des methodes, avec de l'heritage…etc Caracteristique ? des attribut ou des methodes en fonction de ce que c'est ?

Il faut que tu fasses une classe qui regroupe tous les parametres possibles que tu initialiseras en fonction de tes besoins.

En réalité je n'ai pas qu'une seule classe a faire. Ce que nous avons fait nous oblige à décorer, hériter et faire joujou avec des paramètres afin que ce soit maintenable. A chaque fois je dois faire une factory, un héritage et un autre truc. Ecrire le code, c'est pas un problème. C'est plutôt au niveau de l'integration dans l'application. Comment on intègre quelque chose sans devoir relancer ?

Si on veut vraiment s'en donner les moyens, on peut le faire à peu près avec n'importe quel langage interprété ou semi-complilé. Mais en général ça ne sert à rien et ça reflète plutôt une mauvaise conception.

a mon avis le seul langage où tu ne peux pas faire ça aussi facilement, c'est en C/C++. Pour lui il faut penser en terme de plugin si tu veux réellement pouvoir exécuter du code / instancier des classes dont tu ne connais que l'interface de base à l'exécution.

En python, il doit y avoir une fonction qui fait office d'import dynamique. En lua, require ou dofile est c'est réglé. En Java, pour peu que tu aies javac pour compiler le code bien sûr, la réflexion fonctionne très bien; pour C# il y a aussi des API de réflexion si je ne m'abuse. Enfin bref c'est faisable, mais en général quand on en arrive à utiliser de la réflexion c'est vraiment quand on ne peut pas faire autrement; souvent c'est pas particulièrement efficace en plus…

+3 -0

@Grugru

Je met en spoiler des exemples et un link vers github pour te donner un véritable exemple

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
public class AgeEntityShark extends AgeEntity{
  
  public AgeEntityShark(Entity e, int life) {
      super(e, life);
  }

  @Override
  public void entityAges() {
      synchronized (this.getDecoratedEntity()) {
          this.substractLife(getChangeLifePoint());
      }
      
  }

  
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public class AgeShark extends Age{

  public AgeShark() {
      super();
  }
  
  @Override
  public void addEntity(Entity e) {
      this.addDecoratedEntity(new AgeEntityShark(e, getDefaultLife()));
      
  }

}

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
public class FAgeShark extends FAge{

  
  @Override
  public Component getComponent() {
      if(instance == null){
          instance = new AgeShark();
      }
      return instance;
  }
}

Il faudrait rajouter un système pour que les classes créés s'intègre au système, c'est surtout là le problème. Mais j'ai eu quelques idées. J'ai certaines classes static qui gèrent mes entités et component (2 des trois classes misent), il me suffirait donc de signaler qu'il y a un nouveau type à cette classe non ?

@QuentinC

En réalité c'est une simplement une idée qui m'est venue. C'est ce qui me paraissait le plus simple mais si tu as des propositions je suis tout à fait preneur. J'ai déjà pensé à utiliser des classes générique puis les spécialiser avec des paramètres (il doit bien y avoir un tas de pattern géniaux) sauf que j'ai besoin de minimum 1 classe par "espèce" (sous entendu ce que tu pourrais me proposer, donc la ça peut le faire) mais je dois nécessairement avoir des classes différentes pour chacun de leur component (et au minimum de 3). C'est assez difficile par écrit d'expliquer mais si tu as un exemple connu je pourrais regarder et voir si je peux m'en inspirer

@Andr0 Non je ne connaissais pas, mais je vais y jeter un oeil, même par curioisité !

Merci de vos réponses en tout cas :)

Edit : J'avais oublié le link

+0 -0

J'ai déjà pensé à utiliser des classes générique puis les spécialiser avec des paramètres (il doit bien y avoir un tas de pattern géniaux) sauf que j'ai besoin de minimum 1 classe par "espèce" (sous entendu ce que tu pourrais me proposer, donc la ça peut le faire) mais je dois nécessairement avoir des classes différentes pour chacun de leur component (et au minimum de 3). C'est assez difficile par écrit d'expliquer mais si tu as un exemple connu je pourrais regarder et voir si je peux m'en inspirer.

Je ne fouille pas assez dans les codes des autres pour trouver des exemples très connus. Mais à te lire, il me semble que tu pourrais utiliser le pattern strategy, une fois par type d'élément différent.

Quand j'ai essayé de résoudre le javaquarium, j'ai utilisé le pattern strategy un peu à ma sauce. En gros j'avais ça (reconstitution de mémoire, j'ai pas le code ici) :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
interface Reproduction {
public boolean isFemale (Fish thisFish);
public boolean canReproduce (Fish thisFish) ;
public boolean canReproduce (Fish thisFish, Fish anotherFish);
public Fish reproduce (Fish thisFish, Fish parthner);
public void update (Fish thisFish);
}

interface Eating {
public boolean isHungry (Fish thisfish) ;
public boolean canEat (Fish thisFish, Fish target);
public void eat (Fish thisFish, Fish target);
}

interface Life {
public boolean isAlive (Fish thisFish);
public void update (Fish thisFish);
}

class Fish {
final Reproduction reproduction;
final Eating eating;
final Life life;

public Fish (Reproduction r, Eating e, Life l) {
//...
}

//...
}

Dans une autre partie, j'avais ça :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public static final Reproduction
NORMAL = new Reproduction(){
// Définition de la reproduction normale, i.e. seuls un mâle et une femelle de la même espèce peuvent se reproduire
},
OPPORTUNISTIC = new Reproduction(){
// Définition de la reproduction opportuniste, i.e. le poisson cible change de sexe juste au moment de l'accouplement si nécessaire
},
HALF_LIFE = new Reproduction(){
// Définition de la reproduction demi-vie, i.e. le poisson change de sexe à un âge prédéfini
};

// Même genre de truc pour la politique de gestion de la vie, et pour le régime alimentaire
``


Et puis encore ailleurs :

```java
class ClownFish extends Fish {
public ClownFish () {
super(OPPORTUNISTIC, MEAT_EATER, NORMAL_FISH_LIFE);
}

}

Avec un peu de recul et en lisant ce qu'on fait les autres, j'ai remarqué que j'avais fait des erreurs de conception et ai constaté plusieurs choses :

  • Si les politiques n'étaient pas final, j'aurais eu la capacité de changer dynamiquement le comportement du poisson au cours de sa vie
  • J'ai bien fait de ne pas faire des classes internes car sinon ça pouvait être difficile d'ajouter des politiques par la suite, hors programme principal, au cas où on a plus la main sur le code de Fish
  • En fait la classe ClownFish et ses conseurs ne servent à rien, j'aurais dû faire un système de factory à la place

Pourquoi je raconte tout ça ? En fait, j'ai vaguement l'impression que les classes que tu veux construire dynamiquement, c'est dans le genre de ma classe ClownFish ci-dessus. Tu peux bien sûr faire une sous-classe par combinaison de propriétés existantes, mais ce n'est pas flexible. Et effectivement dans ce mode de pensée, si tu veux donner à l'utilisateur le choix des propriétés, tu es obligé de créer des nouvelles classes dynamiquement. Alors que si les propriétés que tu définis sous forme d'interfaces sont bien construites, tu n'as pas besoin. Tu instancies Fish directement, et tu passes seulement les bonnes propriétés; pas besoin de sous-classes dynamiques.

Tu as besoin d'un nouveau mode de reproduction, admettons que certains poissons peuvent être homosexuels ? pas de problème, une nouvelle instance de Reproduction fera l'affaire. Et si à un moment donné tu veux ajouter un tout nouveau comportement pas encore envisageable à l'aide des interface que tu as déjà, la mort naturelle des poissons, ou la pousse des écailles par exemple, tu définis une nouvelle gamme de propriétés (une nouvelle interface) plutôt que des sous-classes. Au final, tu n'utiliss que la classe Fish et c'est très flexible, tout le reste est défini dans des classes de comportements complètement séparées.

L'évolution suivante beaucoup plus généralisée de mon système, c'est l'entity component system (ECS). Je n'ai pas encore eu le courage de m'y atteler sérieusement moi-même, mais d'autres membres ont déjà commencé et partagé sur ce sujet. Une recherche ici même ou ailleurs pourrait t'être instructive.

+0 -0

En fait c'est presque ça

La différence c'est qu'avec un ECS on ne peut pas jouer aussi facilement. Peut être que notre conception est mauvaise (tout à fait possible bien sûr !). Nous nous sommes obligé à rajouter une nouvelle classe par component par espèce. Ca pourrait paraître useless mais le but c'est d'avoir quelque chose de très maintenable et evolutif. Ainsi si on veut changer un petit bout il nous suffit de décorer une classe et changer de factory. Alors qu'avec une méthode un peu plus classique comme tu proposes, ce n'est pas maintenable si on veut faire évoluer l'espèce.

J'ai dû mal à piger comment/pourquoi tu utilises de la décoration. Tu n'aurais pas un exemple ?

Parce que si tu utilises des décorations pour remplacer complètement une méthode par une autre, c'est pas vraiment l'objectif du pattern.

+0 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte