Pregunta

Estoy trabajando con un registro de eventos donde hay unos 60 tipos " " de eventos. Cada evento comparte aproximadamente 10 propiedades, y luego hay subcategorías de eventos que comparten varias propiedades adicionales.

La forma en que trabajo con estos eventos depende de su tipo o de las interfaces categóricas que implementan.

Pero parece estar llevando al código inflado. Tengo mucha redundancia en los métodos de subclases porque implementan algunas de las mismas interfaces.

¿Es más apropiado usar una sola clase de evento con un " tipo " propiedad y lógica de escritura que verifica el tipo y mantiene alguna organización de categorías de tipos (por ejemplo, una lista de tipos de eventos que son categoría a, una segunda lista que son categoría b, etc.) ¿O es el diseño de subclase más apropiado en este caso?

Primer enfoque:

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

Segundo enfoque:

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

Mi opinión personal es que parece que un objeto de evento único es lo que es apropiado, porque, si lo estoy pensando correctamente, no es necesario usar la herencia para representar el modelo porque realmente es solo el comportamiento y mi Condiciones que se alteran en función del tipo.

Necesito tener un código que haga cosas como:

if(event instanceof Category1) {
  ...
}

Esto funciona bien en el primer enfoque, ya que en lugar de la instancia de I, simplemente puedo llamar al método en el evento e implementar " el mismo código " en cada una de las subclases apropiadas.

Pero el segundo enfoque es mucho más conciso. Luego escribo cosas como:

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

Y toda mi " lógica de procesamiento " se puede organizar en una sola clase y ninguna de ellas se distribuye de manera redundante entre las subclases. Entonces, ¿es este un caso en el que, aunque la OO parece más apropiada, sería mejor que no?

¿Fue útil?

Solución

Depende de si cada tipo de evento tiene inherentemente un comportamiento diferente que el propio evento puede ejecutar.

¿Sus objetos de evento necesitan métodos que se comporten de manera diferente por tipo? Si es así, usa la herencia.

Si no, usa una enumeración para clasificar el tipo de evento.

Otros consejos

Iría con la solución de tipo de objeto por evento, pero en su lugar agruparía las combinaciones de interfaces comúnmente utilizadas en clases (probablemente abstractas) que brindan sus implementaciones esqueléticas . Esto reduce en gran medida el aumento de código generado por tener muchas interfaces, pero, por otro lado, aumenta el número de clases. Pero, si se usa de manera adecuada y razonable, conduce a un código más limpio.

La herencia puede ser limitante si decides extender una clase base abstracta de un Interfaz de Categoría en particular, porque es posible que también necesite implementar otra Categoría.

Entonces, aquí hay un enfoque sugerido: Suponiendo que necesita la misma implementación para un método de interfaz de Categoría en particular (independientemente del Evento), podría escribir una clase de implementación para cada interfaz de Categoría.

Entonces tendrías:

public class Category1Impl implements Category1 {
    ...
}

public class Category2Impl implements Category2 {
    ...
}

Luego, para cada una de sus clases de Evento, solo especifique las interfaces de Categoría que implementa, y mantenga una instancia de miembro privado de la clase de implementación de Categoría (para que use composición, en lugar de herencia). Para cada uno de los métodos de interfaz de Categoría, simplemente reenvíe la llamada de método a la clase de implementación de Categoría.

Como realmente no obtuve las respuestas que buscaba, ofrezco mi mejor estimación basada en mi experiencia de aprendizaje poco deseable.

Los eventos en sí mismos en realidad no tienen comportamientos, son los controladores de los eventos que tienen comportamientos. Los eventos solo representan el modelo de datos.

Reescribí el código para tratar los eventos como matrices de objetos de propiedades, de modo que pueda usar los nuevos argumentos variables de Java y las funciones de boxeo automático.

Con este cambio, pude eliminar alrededor de 100 clases de código gigantescas y lograr gran parte de la misma lógica en aproximadamente 10 líneas de código en una sola clase.

Lección (s) aprendida (s): No siempre es aconsejable aplicar paradigmas OO al modelo de datos. No se concentre en proporcionar un modelo de datos perfecto a través de OO cuando trabaje con un dominio grande y variable. El diseño OO beneficia al controlador más que el modelo a veces. No se centre en la optimización por adelantado también, porque generalmente una pérdida de rendimiento del 10% es aceptable y puede recuperarse por otros medios.

Básicamente, estaba sobre-diseñando el problema. Resulta que este es un caso en el que el diseño OO correcto es excesivo y convierte un proyecto de una noche en un proyecto de 3 meses. ¡Por supuesto, tengo que aprender cosas de la manera más difícil!

El simple hecho de tener una gran cantidad de archivos .java no es necesariamente malo. Si puede extraer de manera significativa un pequeño número (2-4 o más) de Interfaces que representan los contratos de las clases y luego empaquetar todas las implementaciones, la API que presente puede ser muy limpia, incluso con 60 implementaciones.

También podría sugerir el uso de algunas clases delegadas o abstractas para obtener una funcionalidad común. Todos los delegados y / o ayudantes abstractos deben ser de paquete privado o de clase privada, no disponibles fuera de la API que exponga.

Si hay una mezcla y una combinación de comportamientos considerables, consideraría usar la composición de otros objetos, y luego hacer que el constructor del tipo de evento específico cree esos objetos, o use un generador para crear el objeto.

¿Quizás algo como esto?

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

Probablemente hay algunas mejoras que podrían hacerse, pero que podrían ayudarte a comenzar.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top