2013-01-09 10 views
5

Ho una classe base ShapeManager con un elenco di forme che voglio enumerate(). Poi c'è una specializzazione ColoredShapeManager che vuole per elaborare specializzata ColoredShape s invece di Shape s:Condivisione di un elenco di tipo base con i bambini

+----------------+  +-------------------------------------+ 
| Shape   |  | ShapeManager      | 
|----------------|  |-------------------------------------| 
| + id: int  |  | # shapes: List<Shape>    | 
|    |  |          | 
|    |  | + ShapeManager() {     | 
|    |  |  shapes.add(new Shape());  | 
|    |  | }         | 
|    |  |          | 
|    |  | + abstract void enumerate() {  | 
|    |  |  for (Shape s: shapes) {   | 
|    |  |  // use s      | 
|    |  |  }        | 
|    |  | }         | 
+----------------+  +-------------------------------------+ 
     ^       ^
     |        | 
     +        + 
+----------------+  +-------------------------------------+ 
| ColoredShape |  | ColoredShapeManager     | 
|----------------|  |-------------------------------------| 
| + color: int |  | + ColoredShapeManager() {   | 
|    |  |  shapes.add(new ColoredShape()); | 
|    |  | }         | 
|    |  |          | 
|    |  | + abstract void enumerate() {  | 
|    |  |  for (Shape s: shapes) {   | 
|    |  |  // use (ColoredShaped) s  | 
|    |  |  // will fail for Shapes  | 
|    |  |  }        | 
|    |  | }         | 
+----------------+  +-------------------------------------+ 

Sono incerto se ShapeManager dovrebbero condividere shapes: List<Shape> con i suoi figli Questo sembra viziato dal ColoredShapeManager.enumerate() vuole processo ColoredShape s. Quindi getterebbe gli elementi, tuttavia alcuni elementi (quelli aggiunti dalla classe base) sono di tipo Shape e il cast fallirebbe.

cioè:

  • Entrambi i tipi di forma finiscono nella lista shapes.
  • enumerate() nel gestore figlio dovrebbe avere accesso a ColoredShape.

Devo dividere l'elenco e creare elenchi privati ​​in ciascuno dei due gestori? Quindi, enumerare il bambino dovrebbe solo scorrere il tipo "suo" di forme e chiamare lo enumerate() del genitore all'inizio/fine.

+0

http://www.asciiflow.com/#Draw ottimo strumento! –

+0

La soluzione migliore dipende da ciò che si sta facendo con le forme. Ad esempio, se intendi disegnare le forme nel ciclo in 'enumerate' (che è una scelta sbagliata per il codice che mostri, secondo me), se stai per disegnare, allora tutte le forme - colorate e non-- avrà un metodo di estrazione. La combinazione dei metodi di chiamata sulla classe 'Shape', che si applicano a tutte le istanze e ai metodi di chiamata sulla classe 'ColoredShape', che non si applicano alle istanze 'Shape', quella miscela può determinare il modo migliore per farlo. –

+0

L'intero punto della domanda è come usare le proprietà/proprietà specifiche di 'ColoredShape' (in questo esempio 'colori'). –

risposta

2

Perché non separare il comportamento e generare il contenitore?

contenitore:

public class ShapeManager<T extends Shape> { 
    private List<T> shapeList; 

    public void processShapes(ShapeProcessor processor){ 
     for (T shape : shapeList){ 
      processor.process(shape); 
     } 
    } 
} 

e il comportamento (si può anche avere una classe di fabbrica per fornire differenti implementazioni):

public class ShapeProcessor { 

    public void process(Shape shape) { 

    } 

    public void process(ColoredShape shape){ 

    } 
} 

Oppure, andare in piena Visitor Pattern:

public abstract class Shape { 
    public void accept(ShapeProcessor processor){ 
     processor.process(this); 
    } 
} 

public interface ShapeProcessor { 
    public void process(Shape shape); 
    public void process(ColoredShape shape); 
} 

public class ShapeManager { 
    private List<Shape> shapeList; 

    public void processShapes(ShapeProcessor processor){ 
     for (Shape shape : shapeList){ 
      shape.accept(processor); 
     } 
    } 
} 

Ciò consente di enumerare più tipi di forme e di avere diversi tipi di pr metodi applicati, anche quelli provenienti da diversi tipi di ShapeProcessors. Manager non si preoccupa di nessuno di loro.

+0

Quindi sostanzialmente quello che ho detto ieri;) – TedTrippin

+0

L'unico inconveniente che vedo è che devo prevedere tutte le future classi '* Shape' per definire' ShapeProcessor'. –

+0

@MichaWiedenmann Non esattamente, non è necessario posizionare tutti i tipi di metodi 'process()' nella stessa classe, 1. È possibile passare un 'ProcessorProvider' (una factory) per restituire un processore appropriato. 2. Come mostrato nel pattern visitatore per ogni nuova classe 'Shape', semplicemente costruisci una classe companion che implementa l'interfaccia' ShapeProcessor'. –

1

Penso che il problema sia che stai costruendo un singolo attributo - le forme delle liste - in due punti: in parte nel genitore e in parte nella classe del bambino. Questo ovviamente non è un buon progetto. Devi cambiarlo. Anche se non posso dire esattamente come, ma penso che una soluzione possa essere quella di fornire l'elenco come argomento ai costruttori (almeno per il costruttore genitore), invece di crearli all'interno di ogni costruttore. In questo modo, quando si utilizza una classe figlio come ColoredShapeManager, è possibile creare un elenco di ColoredShapes e passarlo al costruttore padre dall'interno del costruttore figlio. In questo modo, in tutti i tuoi metodi figli avrai a che fare solo con ColoredShape. Mentre, se stai usando un genitore, ci saranno solo forme (passate attraverso il costruttore).

+0

Avrei dovuto dire più chiaramente che Base può aggiungere elementi anche in altre funzioni membro. Quelli non possono essere castati nel bambino. –

6

È possibile aggiungere il tipo di forma come "parametro tipo" per le classi gestore. Quindi, in pratica, ColoredShapeManager può estendere ShapeManager<ColoredShape> e questo T sarà il tipo di struttura dati interna List. Inoltre, se il tuo ColoredShapeManager non fa nulla di specifico con lo ColoredShapes, direi che non ha nemmeno bisogno di una nuova classe. Ma poi di nuovo dipende dal modo in cui stai strutturando la tua app/design.

+0

Grazie, ho aggiornato la mia domanda per renderla più chiara. Voglio usare 'ShapeManager' come classe Base. Penso di non poter fare come suggerito se in seguito erediterò anche da 'ColoredShapeManager' (dato che questo bambino estenderebbe la base piuttosto che il primo figlio). Penso anche che il parametro type sia un dettaglio di implementazione e non debba essere pubblicato come parte del tipo di 'ShapeManager'. Forse usare una classe invece di un'interfaccia è un errore fatto da me. –

+0

Vedo il tuo punto, ma l'esposizione del parametro type non dovrebbe violare l'incapsulamento. Dopo tutto, lo stai esponendo nel nome della classe. Si può usare il parametro '' per limitare la classe a usare solo le forme - questo è effettivamente raccomandato per garantire che si stia effettivamente elaborando 'Shape's. – MathSquared

3

Il modello del visitatore ti soddisfa?

Forma

public void doSomething(ShapeManager) { 
    ... 
} 

ShapeManager

abstract void enumerate() { 
    for (Shape shape: shapes) { 
     shape.doSomething(this); 
    } 
} 

Poi andrei bisogno di conoscere il tipo ed ogni derivato forma potrebbe avere la propria implementazione.

+0

Ho premiato Singolarità perché la sua risposta è stata più facile da capire per me, eppure sono ancora grato a te e a tutte le persone che mi hanno aiutato. Spero che stia bene con te. –

1

Il progetto potrebbe funzionare ma è necessario verificare in fase di esecuzione se la forma s è effettivamente una forma o se è una ColorShape che utilizza l'istruzione instanceOf(class). Ciò potrebbe creare problemi se inizi ad aggiungere molti tipi diversi di forme. Il modo giusto per farlo sarebbe definire un'interfaccia che ogni forma dovrebbe implementare, in questo modo ogni forma potrebbe essere utilizzata allo stesso modo.

Perché non utilizzare il composite pattern ?

public interface IShape { public int enumerate(); }


public class Shape implements IShape { 

int id; 

public int enumerate() { 
    return id; 
} 

} 

public class ShapeColor extends shape { 

    int color; 

    public int enumerate() { 

     return //whatever you need; 
    } 

} 

import java.util.List; 

public class ShapeManager implements IShape { 

    List<IShape> shapes; 

    public int enumerate() { 
     for(IShape s : shapes){ 
      //do stuff 
     } 
     return 0; 
    } 

} 

Facendo in questo modo si prende il comportamento si vuole aggiungere in ColoredShapeManager e direttamente messo nella ColoredShape (che sembra più logico)

2

Penso che si possa utilizzare generico ShapeManager. Vorrei prendere in prestito i vostri diagrammi di classe, se non ti dispiace:

+----------------+  +---------------------------------------+  +------------------------------+ 
| Shape   |  | AbstractShapeManager<S extends Shape> |  | ShapeManager<Shape>   | 
|----------------|  |---------------------------------------|  |------------------------------| 
| + id: int  |  | # shapes: List<S>      |  | + Shape() {     | 
|    |  |          |  |  shapes.add(new Shape()); | 
|    |  | + abstract void enumerate() {   | < —— + | }       | 
|    |  |  for (S s: shapes) {    |  |        | 
|    |  |  /* use s */     |  | + void enumerate() {   | 
|    |  |  }         |  |  for (Shape s: shapes) { | 
|    |  | }         |  |  // use Shape   | 
+----------------+  +---------------------------------------+  |  }      | 
     ^       ^        | }       | 
     |        |        +------------------------------+ 
     +        + 
+----------------+  +-------------------------------------+ 
| ColoredShape |  | ColoredShapeManager<ColoredShape> | 
|----------------|  |-------------------------------------| 
| + color: int |  | + ColoredShapeManager() {   | 
|    |  |  shapes.add(new ColoredShape()); | 
|    |  | }         | 
|    |  |          | 
|    |  | + void enumerate() {    | 
|    |  |  for (ColoredShape s: shapes) { | 
|    |  |  // use ColoredShape   | 
|    |  |  }        | 
|    |  | }         | 
+----------------+  +-------------------------------------+ 

In questo modo, lo svantaggio è che non è possibile aggiungere qualsiasi forma alla lista perché non v'è una restrizione di tipo.

Problemi correlati