2011-11-07 33 views
6

Este es un problema desagradable, y es posible que el diseño sea simplemente malo.Heredó algunos malos genéricos de Java

Escribo un conjunto de componentes de gráficos simples (circular, barra & gráficos de líneas) y me estoy ahogando con algunos genéricos. De antemano, estoy seguro de que hay muchas API de Java para hacer exactamente lo que estoy tratando de hacer aquí (gráficos/informes/etc.), sin embargo, estoy interesado en esto como un problema general de genéricos; el hecho de que involucra gráficos & reportando componentes es trivial.

Cada tabla hereda de una clase base abstracta genérica Chart:

public abstract class Chart<T extends ChartComponent> 
{ 
    private List<T> components; 

    // ...rest of the Chart class 
} 

La razón por la que tenemos T extends ChartComponent es debido a que cada subclase gráfico se compone de 1+ llamados componentes de gráfico (barras, líneas , sectores de la gráfica, etc.):

public abstract class ChartComponent 
{ 
    private Color color; 

    // .. rest of ChartComponent class 
} 

public class PieWedge extends ChartComponent 
{ 
    double wedgeValue; 

    // ... rest of PieWedge class 
} 

poner este diseño en conjunto:

public class PieChart extends Chart<PieWedge> 
{ 
    // ... thus its list of ChartComponents is actually a List<PieWedge> 
} 

De esta forma, PieChart no es genérico (ni debería serlo) y siempre es del tipo Chart<PieWedge>.

Anteriormente tenía la misma configuración para gráficos de barras y líneas, que se definían como BarChart extends Chart<BarGroup> y LineChart extends Chart<Line> respectivamente (ya que un gráfico de barras consta de 1+ grupos de barras, y un gráfico de líneas consta de líneas 1+).

Ahora quiero abstraer los gráficos de barras y líneas aún más. Ambos cuadros se grafican contra un gráfico cartesiano (x, y) con ejes xey; esto es opuesto a un gráfico circular que no está trazado contra ninguno de esos ejes.

Lo ideal es que quería crear una nueva clase abstracta llamada CartesianChart que se extendía Chart, y luego tener BarChart y LineChart se extienden ambas CartesianChart. Este nuevo CartesianChart introduciría nuevas propiedades (xAxisLabel, gridTurnedOn, etc.) que lógicamente se aplican a los gráficos de barras/líneas, pero no a los gráficos circulares.

Además, para restringir CartesianChart de modo que sólo podría tener chartComponents de tipo BarGroup o Line (y no PieWedge), me gustaría para crear un nuevo tipo de componente de tabla como CartesianComponent extends ChartComponent, y luego tener BarGroup/Line extienden eso. Si lo hace, impediría código como el de la compilación:

LineChart lineChart = new LineChart(); 
lineChart.addLine(new PieWedge()); 

Desde Line extiende CartesianComponent, pero sólo se extiende PieWedgeChartComponent. Por lo tanto, antes de llegar a mi problema que tenemos la siguiente jerarquía de herencia:

Chart 
    CartesianChart 
     BarChart 
     LineChart 
    PieChart 

ChartComponent 
    CartesianComponent 
     BarGroup 
     Line 
    PieWedge 

PieChart extends Chart<PieWedge> 

CartesianChart extends Chart<CartesianComponent> 

BarGroup extends CartesianComponent 
Line extends CartesianComponent 

BarChart extends CartesianChart<BarGroup> 
LineChart extends CartesianChart<Line> 

El problema de esta configuración es que tanto en BarChart y LineChart da un error del compilador quejan de que CartesianChart no es genérico. Esto tiene mucho sentido, pero no estoy seguro de qué puedo hacer para solucionarlo.

Si trato de volver a definir CartesianChart:

public abstract class CartesianChart<T extends CartesianComponent> extends Chart<CartesianComponent> 
{ 
    // ... 
} 

me sale "discordancia de tipo" errores de compilación a través de toda mi código gráfico de barras/línea. En cada instancia del error, indica que está esperando argumentos de tipo List<CartesianComponent> pero en su lugar encontró List<BarGroup> o List<Line> y que no son sustitutos adecuados.

Afortunadamente, esta es una solución rápida en algún lugar de la definición de clase de CartesianChart y/o CartesianComponent. De lo contrario, tendré que volver a diseñar toda la biblioteca de gráficos. De cualquier manera, estoy interesado en todas las sugerencias, excepto como "Oye, ¿por qué no pruebas JFreeCharts o ...". Una vez más, estoy interesado en la solución aquí, ya que se relaciona con la solución de una amplia gama de problemas genéricos similares; el hecho de que esto implique informes/gráficos es trivial.

¡Gracias de antemano por cualquier ayuda!

+0

¿Qué intenta lograr haciendo 'Chart' genérico? En otras palabras, ¿por qué no simplemente definir los componentes 'class Chart {private List ; } '? –

+0

'clase abstracta pública CartesianChart extends Chart '? – digitaljoel

+0

Gracias por las sugerencias aquí. Voy a probar ambas sugerencias aquí. Por favor, vea mi comentario debajo de la respuesta de @nicholas.hauschild sobre mi temor de que la ampliación del Diagrama no impida que las subclases hagan un gráfico donde T no extiende ChartComponent. – IAmYourFaja

Respuesta

4

Su clase Chart contiene el List<T> la que usted habla, por lo que cuando se define el CartesianChart clase abstracta para extender Chart<CartesianComponent>, usted está diciendo que es realmente List<T>List<CartesianComponent>.

Realmente, lo que quiere es simplemente usar el genérico como lo definió en su clase abstracta (es decir, <T extends CartesianComponent>). Intentaría hacer esto y ver cómo funciona.

public abstract class CartesianChart<T extends CartesianComponent> extends Chart<T> 
{ 
    // ... 
} 
+0

Esa es una muy buena sugerencia (y ¡gracias!), Sin embargo, me preocupa que esto permita que las subclases se definan así: el Diagrama de clase pública extiende el Diagrama , donde Widget no es una subclase ChartComponent. Es importante que todos los tipos "T" hereden (en algún punto) de ChartComponent. – IAmYourFaja

+0

En realidad, no lo haría, porque ya ha definido 'T' en este ámbito para restringirlo a' CartesianComponent''s (por 'CartesianChart '). Pruebe mi sugerencia y luego intente agregarle un 'Widget' ... –

+0

Impresionante, impresionante, increíble. ¡Gracias de nuevo! – IAmYourFaja

0

Usar interfaces.

public interface IsAPieChart { 

} 

public interface IsACartesianChart { 

} 

Ni siquiera necesitan ningún método.

Su perfil método para AddLine() sería el siguiente:

public void addLine(IsACartesianChart cartesianChart); 

Sus clases abstractas leerían:

public class PieChart extends Chart<PieWedge> implements IsAPieChart 
{ 
    // ... thus its list of ChartComponents is actually a List<PieWedge> 
} 

Y el uso de IsACartesianChart para marcar CartesianChart de la misma manera. Ahora addLine() no aceptará nada de PieChart porque ningún PieChart implementa la interfaz IsACartesianChart, pero tomará cualquier cosa de una subclase de CartesianChart porque todas las subclases implementan IsACartesianChart.

El uso de interfaces como esta es una gran manera de reintroducir las distinciones que se han perdido cuando un grupo de clases se remonta a la misma superclase. Las superclases y las subclases forman una jerarquía estricta, mientras que las interfaces se pueden conectar donde sea que las necesite.

0

La razón por la que tenemos T extends ChartComponent se debe a que cada carta subclase estará compuesto por 1+ llamados componentes de gráfico (barras, líneas, sectores de la gráfica, etc.):

Ésta es su arenque rojo, no hay necesidad de usar un genérico aquí. Esto es un problema Composition, no un problema Generics.

Simplemente haga su lista aspecto:

private List<ChartComponent> components; 

Ésta es toda la seguridad de tipos que usted debe necesitar.

+0

Gracias! Voy a intentar esta sugerencia a finales de esta semana cuando tenga oportunidad. – IAmYourFaja