Question

Je travaille avec un journal des événements contenant environ 60 types différents "types". d'événements. Chaque événement partage environ 10 propriétés, puis il existe des sous-catégories d’événements partageant diverses propriétés supplémentaires.

La façon dont je travaille avec ces événements dépend de leur type ou des interfaces catégoriques qu'ils implémentent.

Mais cela semble conduire à un gonflement du code. J'ai beaucoup de redondance dans les méthodes de la sous-classe parce qu'elles implémentent certaines des mêmes interfaces.

Est-il plus approprié d’utiliser une seule classe d’événements avec un "type"? la propriété et la logique d’écriture qui vérifie le type et maintient l’organisation des catégories de types (par exemple, une liste des types d’événements appartenant à la catégorie a, une deuxième liste correspondant à la catégorie b, etc.)? Ou bien la conception de la sous-classe est-elle plus appropriée dans ce cas?

Première approche:

public interface Category1 {}
public interface Category2 {}

public abstract class Event {
 private base properties...;
}

public class EventType1 extends Event implements Category1, Category2 {
 private extra properties ...;
}

public class EventType2 extends Event implements Category3, Category4 {
 private extra properties ...;
}

Deuxième approche:

public enum EventType {TYPE1, TYPE2, TYPE3, ...}
public class Event {
 private union of all possible properties;
 private EventType type;
}

Mon opinion personnelle est qu’il semble approprié de choisir un seul objet d’événement car, si j’y réfléchis correctement, il n’est pas nécessaire d’utiliser l’héritage pour représenter le modèle, car c’est uniquement le comportement et mon comportement. conditions qui changent en fonction du type.

J'ai besoin d'un code qui fait des choses comme:

if(event instanceof Category1) {
  ...
}

Cela fonctionne bien dans la première approche car au lieu d’instance of, je peux simplement appeler la méthode sur l’événement et mettre en œuvre "le même code". dans chacune des sous-classes appropriées.

Mais la deuxième approche est tellement plus concise. Ensuite, j'écris des choses comme:

if(CATEGORY1_TYPES.contains(event.getEventType()) {
 ...
}

Et toute ma "logique de traitement" peut être organisé en une seule classe et aucune d’elle n’est répartie de manière redondante entre les sous-classes. Alors, s’agit-il d’un cas où, bien que OO semble plus approprié, il serait préférable de ne pas trop?

Était-ce utile?

La solution

Cela dépend si chaque type d'événement a de manière inhérente un comportement différent que l'événement lui-même peut exécuter.

Vos objets Event ont-ils besoin de méthodes qui se comportent différemment selon le type? Si c'est le cas, utilisez l'héritage.

Sinon, utilisez une énumération pour classifier le type d'événement.

Autres conseils

Je choisirais la solution de type objet par événement, mais je regrouperais plutôt des combinaisons d'interfaces couramment utilisées dans des classes (probablement abstraites) fournissant leurs implémentations squelettiques . Cela réduit considérablement le volume de code généré par le nombre d'interfaces, mais augmente en revanche le nombre de classes. Mais si utilisé correctement et raisonnablement, cela conduit à un code plus propre.

L'héritage peut être limitant si vous décidez d'étendre une classe de base abstraite d'un interface de catégorie particulière, car vous devrez peut-être également implémenter une autre catégorie.

Donc, voici une approche suggérée: En supposant que vous ayez besoin de la même implémentation pour une méthode d'interface Catégorie particulière (quel que soit l'événement), vous pouvez écrire une classe d'implémentation pour chaque interface Catégorie.

Vous auriez donc:

public class Category1Impl implements Category1 {
    ...
}

public class Category2Impl implements Category2 {
    ...
}

Ensuite, pour chacune de vos classes Event, spécifiez simplement les interfaces de catégorie implémentées et conservez une instance de membre privé de la classe d'implémentation Category (vous utilisez donc la composition plutôt que l'héritage). Pour chacune des méthodes d'interface Category, transmettez simplement l'appel de méthode à la classe d'implémentation Category.

Étant donné que je n'ai pas vraiment obtenu les réponses que je cherchais, je fournis mes propres hypothèses en me basant sur une expérience d'apprentissage peu souhaitable.

Les événements eux-mêmes n’ont en fait pas de comportement, ce sont les gestionnaires des événements qui ont des comportements. Les événements ne représentent que le modèle de données.

J'ai réécrit le code uniquement pour traiter les événements comme des tableaux d'objets de propriétés, afin de pouvoir utiliser les nouveaux arguments variables et les fonctionnalités de box-box de Java.

Grâce à ce changement, j'ai pu supprimer une centaine de classes de code gigantesques et accomplir la même logique en environ 10 lignes de code dans une même classe.

Leçon (s) apprise (s): Il n’est pas toujours sage d’appliquer les paradigmes OO au modèle de données. Ne vous concentrez pas sur la fourniture d'un modèle de données parfait via OO lorsque vous travaillez avec un grand domaine variable. La conception OO profite parfois plus au contrôleur qu'au modèle. Ne vous concentrez pas non plus sur l'optimisation initiale, car une perte de performance de 10% est acceptable et peut être récupérée par d'autres moyens.

En gros, j’ai trop travaillé sur le problème. Il s’avère que c’est un cas où la conception d’OA est excessive et qui transforme un projet d’une nuit en un projet de 3 mois. Bien sûr, je dois apprendre les choses à la dure!

Le simple fait d'avoir un grand nombre de fichiers .java n'est pas nécessairement mauvais. Si vous pouvez extraire de manière significative un petit nombre d'interfaces (environ 2 à 4) représentant les contrats des classes, puis regrouper toutes les implémentations, l'API que vous présentez peut être très propre, même avec 60 implémentations.

Je pourrais aussi suggérer d'utiliser des classes déléguées ou abstraites pour intégrer des fonctionnalités communes. Les délégués et / ou assistants abstraits doivent tous être des paquets privés ou des classes privées, non disponibles en dehors de l’API que vous exposez.

S'il y a un mélange et une correspondance de comportement considérables, je considérerais d'utiliser la composition d'autres objets, puis demandez au constructeur de l'objet de type d'événement spécifique de créer ces objets ou utilisez un générateur pour créer l'objet.

peut-être quelque chose comme ça?

class EventType {

  protected EventPropertyHandler handler;

  public EventType(EventPropertyHandler h) {
     handler = h;
  }

  void handleEvent(map<String,String> properties) {
    handler.handle(properties);
  }
}

abstract class EventPropertyHandler {
   abstract void handle(map<String, String> properties);
}
class SomeHandler extends EventPropertyHandler {
   void handle(map<String, String> properties) {
      String value = properties.get("somekey");
      // do something with value..
   }
}

class EventBuilder {
   public static EventType buildSomeEventType() {
      // 
      EventType e = new EventType( new SomeHandler() );
   }
}

Certaines améliorations pourraient probablement être apportées, mais cela pourrait vous aider à démarrer.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top