2011-08-10 8 views
6

Este es un problema de diseño que sigo encontrando, así que pensé que finalmente lo sacaría y vería cómo las personas se acercarían a él. El problema es el siguiente:OO design: manejo genérico de las subclases que introducen nuevos campos

Identifico una determinada clase que en su mayor parte describe todas las instancias de objetos que utilizaré, tanto en el comportamiento como en los datos. Eso es genial y funciona bien para objetos básicos. Luego surgen otros tipos de objetos que necesitan los mismos datos y el mismo comportamiento, pero también desean tener un campo adicional aquí o allá o una estructura de datos adicional.

Vamos a llamar a la clase algo :

public class Something { 
    private int id; 
    private String fieldA; 
    private String fieldB; 
    private List<Data> list; 

    // Then we have getters, setters, and some base methods 
} 

A veces tendrá que utilizar somethingelse y somethingdifferent. Son 90% como Algo en que los mismos datos y el comportamiento de los describe, sin embargo cada uno de ellos, además, han campos adicionales que deben ser utilizados por el resto del programa:

public class SomethingElse extends Something { 
    private String dataSpecificToSomethingElse; 
    // Then we have getters, setters, and some new-data specific methods 
} 

public class SomethingDifferentextends Something { 
    private List<DifferentData> dataSpecificToSomethingDifferent; 
    // Then we have getters, setters, and some new-data specific methods 
} 

me gustaría llegar con una forma decente de manejar la familia de objetos Something de forma genérica OO, ya que no me gustaría vincular el resto de mi aplicación a los detalles concretos de implementación (porque es posible que necesitemos agregar SomethingWacky más adelante). No quiero tratar directamente con las subclases, ya que eso rompe el polimorfismo, y probablemente incluirá la necesidad de abatir/hacer un cambio de tipo - ¡puaj!

Los enfoques que se me ocurre para resolver esto son las siguientes:

  1. crear una clase base abstracta que define todos los métodos para la Algo familia. Luego, los niños solo implementan el comportamiento que les preocupa proporcionar, dejando un NOP/anulación en blanco para los métodos que no son motivo de preocupación. Esto permite que todo se trate de la misma manera, pero introduce la hinchazón de la interfaz.
  2. Mueva la responsabilidad a la clase base a través de métodos de trabajador genéricos, siguiendo Tell, Don't Ask. Por ejemplo, esto podría ser cosas como display(), doWork(), persist(), getStateFromDisplay(), etc. Cada subclase tendría en cuenta sus datos al anular estos métodos base. Leí un artículo de Allen Holub recientemente que sugería que algo así podría ser una buena práctica. Esto parece estar transfiriendo demasiada responsabilidad a las preocupaciones externas de la clase.
  3. Crea un tipo de clase de datos que agrupe todos los datos/comportamientos adicionales de las subclases, y refiérase a eso en Algo. Esto no se siente muy parecido a OO.

He usado enfoque 1 durante un proyecto anterior -, pero en ese caso a pesar de que cada subclase única implementado/hizo caso omiso de los métodos que se preocupaba, las operaciones eran en realidad lo suficientemente genérica por lo que era posible que una clase podría coherentemente implementar todos o solo algunos.

Cada enfoque se siente sucio de una manera y realmente no me gusta ninguno. ¿Cuáles son mis alternativas? Tal vez estoy haciendo un uso indebido de la herencia o acercándome a esto de la manera equivocada por completo. Estoy abierto a cualquier sugerencia y me gustaría aprovechar las técnicas de OO para obtener diseños más limpios y desacoplados. Realmente me gustaría saber cómo las personas han resuelto problemas como este, y cualquier recurso al que me puedas remitir sería muy apreciado.

Gracias

+0

Re # 2: si su clase persiste, ¿no está violando la Responsabilidad individual? Necesito saber más sobre el contexto, pero me parece que está usando en exceso la herencia, si realmente necesita hacer cosas diferentes con 'SomethingElse', ¿debería ser realmente una subclase? – David

+0

No persiste. Solo estaba tratando de enumerar comportamientos de ejemplo que moverían las preocupaciones externas dentro de la clase, y violarían a SRP como usted dice. Las subclases en realidad no hacen nada diferente, solo tienen información adicional para cuidar ... –

Respuesta

3

Cuando la necesidad de añadir SomethingElse, SomethingDifferent, etc. cultivos, yo le preguntaría: ¿El SomethingElse realmente necesitan ser capaces de acceder individualmente a todos los datos y elementos de comportamiento de Something? ¿O su uso de Something se limita a unos pocos métodos?

Si es el último, generalmente es una buena idea encapsular el comportamiento específico de Something que se utiliza con frecuencia por otras clases, y luego use composition rather than inheritance.

+0

Gracias por la sugerencia. Intento usar la composición donde una clase es parcialmente útil, aunque las clases secundarias literalmente son las mismas que la base, la única diferencia es que se necesitan datos adicionales (campos) como un extra para los niños. –

+0

Es difícil de decir sin un ejemplo concreto, pero si 3 clases son 90% iguales, entonces ese 90% puede ser un buen candidato para separarse en una clase diferente. – oksayt

1

preguntas muy interesantes y tienen que admitir que me encuentro con el mismo problema a menudo demasiado.

Todo lo que dijiste que todos eran válidos y la idea de crear subclases es realmente abordar los problemas que acabas de describir. Usar ABC es un buen principio de OO, así que sugiero que lo analices un poco más. Sin embargo, a fin de definir todos los métodos de la interfaz en la clase base, deberá volver a evaluarlo. Primero, creo que debería dar un paso atrás y hacer una pregunta básica de OO sobre si todos los comportamientos se aplican o no al objeto de ese tipo. También creo que, de acuerdo con la volatilidad de su modelo de dominio, conocerá todos los comportamientos posibles por adelantado/en este momento o, de lo contrario, probablemente no tenga el problema que acaba de describir.

Creo que puede utilizar el enfoque anterior para refactorizar su árbol de herencia y en combinación con el patrón de diseño Decorator para desacoplar su implementación e introducir estabilidad en su árbol de herencia y permitir extensiones a través de las clases de decorador.

+0

¿Podría aclarar a qué se refiere al usar ABC? ¿Es un buen principio OO? –

+0

ABC = Clase base abstracta. La clase base siempre debe ser abstracta por varias razones, como la desacoplamiento entre la implementación y el modelo, y la clase base es a menudo una representación parcial del modelo del mundo real y no permitiremos que se cree abject para eso. –

0

usted podría tener un conjunto de interfaces de que describen los diferentes escenarios de uso que surgen en su aplicación. Me viene a la mente el interface segregation principle and the single responsibility principle. Creo que una mejor manera de lograr reutilizar el código es mantener sus clases lo más pequeñas y enfocadas posible y utilizar la composición en lugar de la herencia para delegar algunas de esas implementaciones de interfaz.

Cuestiones relacionadas