Domanda

Sto lavorando con un registro di eventi in cui ci sono circa 60 tipi diversi di "tipi". di eventi. Ogni evento condivide circa 10 proprietà, quindi ci sono sottocategorie di eventi che condividono varie proprietà extra.

Il modo in cui lavoro con questi eventi dipende dal loro tipo o dalle interfacce categoriche che implementano.

Ma sembra portare a un gonfiamento del codice. Ho molta ridondanza nei metodi della sottoclasse perché implementano alcune delle stesse interfacce.

È più appropriato utilizzare una singola classe di eventi con un tipo "quot" " logica di proprietà e scrittura che controlla il tipo e mantiene una certa organizzazione di categorie di tipi (ad esempio un elenco di tipi di eventi che sono categoria a, un secondo elenco che sono categoria b, ecc.)? O il design della sottoclasse è più appropriato in questo caso?

Primo approccio:

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 ...;
}

Secondo approccio:

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

La mia opinione personale è che sembra che un singolo evento sia ciò che è appropriato, perché, se ci sto pensando correttamente, non è necessario utilizzare l'ereditarietà per rappresentare il modello perché è davvero solo il comportamento e il mio condizioni che cambiano in base al tipo.

Devo avere un codice che funzioni come:

if(event instanceof Category1) {
  ...
}

Questo funziona bene nel primo approccio in quanto invece di istanza di posso semplicemente chiamare il metodo sull'evento e implementare "lo stesso codice" in ciascuna delle sottoclassi appropriate.

Ma il secondo approccio è molto più conciso. Quindi scrivo cose come:

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

E tutta la mia "logica di elaborazione" può essere organizzato in una singola classe e nessuna di esse è distribuita in modo ridondante tra le sottoclassi. Quindi è un caso in cui, sebbene OO appaia più appropriato, sarebbe meglio non troppo?

È stato utile?

Soluzione

Dipende dal fatto che ogni tipo di evento abbia intrinsecamente un comportamento diverso che l'evento stesso può eseguire.

I tuoi oggetti Event hanno bisogno di metodi che si comportano diversamente per tipo? In tal caso, utilizzare l'ereditarietà.

In caso contrario, utilizzare un enum per classificare il tipo di evento.

Altri suggerimenti

Vorrei andare con l'oggetto per soluzione del tipo di evento, ma raggrupperei invece combinazioni di interfacce comunemente utilizzate in classi (probabilmente astratte) che forniscono le loro implementazioni scheletriche . Ciò riduce notevolmente il gonfiore del codice generato dall'avere molte interfacce, ma, d'altra parte, aumenta il numero di classi. Ma, se usato correttamente e ragionevolmente, porta a un codice più pulito.

L'ereditarietà può essere limitante se si decide di estendere una classe base astratta di a particolare interfaccia della categoria, perché potrebbe essere necessario implementare anche un'altra categoria.

Quindi, ecco un approccio suggerito: Supponendo che sia necessaria la stessa implementazione per un particolare metodo di interfaccia di Categoria (indipendentemente dall'evento), è possibile scrivere una classe di implementazione per ciascuna interfaccia di Categoria.

Quindi avresti:

public class Category1Impl implements Category1 {
    ...
}

public class Category2Impl implements Category2 {
    ...
}

Quindi, per ciascuna delle tue classi di eventi, specifica semplicemente le interfacce di categoria implementate e mantieni un'istanza di membro privato della classe di implementazione di categoria (in modo da usare la composizione, piuttosto che l'ereditarietà). Per ciascuno dei metodi dell'interfaccia Categoria, è sufficiente inoltrare la chiamata del metodo alla classe di implementazione Categoria.

Dato che non ho davvero ottenuto le risposte che stavo cercando, sto fornendo la mia ipotesi migliore sulla base della mia esperienza di apprendimento non desiderabile.

Gli eventi stessi in realtà non hanno comportamenti, sono i gestori degli eventi che hanno comportamenti. Gli eventi rappresentano solo il modello di dati.

Ho riscritto il codice per trattare gli eventi come matrici di oggetti di proprietà in modo da poter utilizzare i nuovi argomenti variabili di Java e le funzionalità di auto-boxing.

Con questa modifica, sono stato in grado di eliminare circa 100 gigantesche classi di codice e realizzare gran parte della stessa logica in circa 10 righe di codice in una singola classe.

Lezioni apprese: non è sempre saggio applicare paradigmi OO al modello di dati. Non concentrarti sulla fornitura di un modello di dati perfetto tramite OO quando lavori con un dominio ampio e variabile. Il design OO avvantaggia il controller più del modello a volte. Non concentrarti anche sull'ottimizzazione in anticipo, perché di solito una perdita di prestazioni del 10% è accettabile e può essere recuperata con altri mezzi.

Fondamentalmente, stavo progettando troppo il problema. Si scopre che questo è un caso in cui il corretto design OO è eccessivo e trasforma un progetto di una notte in un progetto di 3 mesi. Certo, devo imparare le cose nel modo più duro!

Il semplice fatto di avere un gran numero di file .java non è necessariamente male. Se riesci a estrarre in modo significativo un piccolo numero (2-4 o giù di lì) di interfacce che rappresentano i contratti delle classi e quindi impacchettare tutte le implementazioni, l'API che presenti può essere molto pulita, anche con 60 implementazioni.

Potrei anche suggerire di usare alcune classi delegate o astratte per ottenere funzionalità comuni. I delegati e / o gli aiutanti astratti dovrebbero essere tutti pacchetti-privati ??o di classe-privati, non disponibili al di fuori dell'API esposta.

Se ci sono notevoli mescolamenti e corrispondenze di comportamenti, prenderei in considerazione l'uso della composizione di altri oggetti, quindi fare in modo che il costruttore dell'oggetto specifico tipo di evento crei tali oggetti o utilizzare un builder per creare l'oggetto.

forse qualcosa del genere?

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() );
   }
}

Probabilmente ci sono alcuni miglioramenti che potrebbero essere fatti, ma che potrebbero farti iniziare.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top