2013-07-30 16 views
14

Non ho ancora trovato un modo elegante per risolvere questo problema. Ho una classe astratta che molte altre classi stanno ereditando con un metodo astratto che può contenere da zero a 4-5 argomenti di tipi diversi.Metodo astratto con lista variabile di argomenti

public abstract class Item { 
public abstract void use(); 
} 

Per esempio, ho una classe che eredita Prenota questo e non accetta argomenti quando sovrascrivendo uso(), ho una classe che eredita chiave e prende una stringa e una coda come argomenti quando si esegue l'override, ecc ..

Ho provato a utilizzare i generici, ma devo inserire il numero usato, come ad esempio Item, quando in realtà dipende dalla classe.

public abstract class Item<T,U> { 
public abstract void use(T arg1, U arg2); //Number of arguments/types could be more or less 
} 

Ho provato l'invio di un elenco di variabili di oggetti, ma i tipi di oggetti sono sempre variabile e non ho dubbi per quanto riguarda la sintassi di ricevere nelle classi che ereditano.

public abstract class Item<T> { 
public abstract void use(T... arguments); 
} 

public class Book extends Item<?> { 
public void use(?); 
} 

public class Book extends Item<String, Queue> { //Wrong number of arguments since I can't use Item<T...> 
public void use(String str, Queue q); //fails 
} 

Forse sto facendo qualcosa di sbagliato - qualcuno può offrire assistenza o intuizione?

+0

Se si esegue l'opzione 'Item it = new Book();', come si propone di chiamare il metodo 'Book.use (String, Queue);' su di esso se il compilatore non può sapere quali sono i tipi di argomenti corretti ? E se non ti aspetti di avere mai una sottoclasse di 'Item' in una variabile della classe genitore, perché il metodo deve essere astratto? – millimoose

risposta

12

ho lottato con la stessa domanda, e non c'è una risposta perfetta, ma posso darvi un paio di cose da considerare. In primo luogo, stai praticamente cercando di fare qualcosa che sia intrinsecamente contro la programmazione orientata agli oggetti, ovvero che stai cercando di creare un'interfaccia variabile. Il punto di un'interfaccia è quel codice che ottiene una versione astratta dell'oggetto (ad esempio l'Articolo piuttosto che il Libro), sa come invocare il metodo use(). Ciò significa che devono sapere cosa può essere passato al metodo use(). Se la risposta dipende dall'implementazione della classe astratta o dell'interfaccia, allora devi assicurarti che il codice che lo usa effettivamente sappia quale tipo di implementazione (Book, ecc.) Usi, altrimenti non saprà come richiamare l'uso() con i parametri appropriati comunque. Sembra che tu debba refactoring il tuo codice, in tutta onestà.

Tuttavia, c'è un modo per rispondere alla domanda come indicato senza refactoring dell'architettura. È possibile creare una classe in cui i dati sono tutti i diversi tipi di parametri che potrebbero essere passati al metodo use(), avere il codice chiamante che imposta i campi di quella classe e quindi passarlo al metodo use(). Per esempio:

public class UseParameters { 
    private String string; 
    private Queue queue; 
    // Any other potential parameters to use(...) 

    public void setString(String string) { 
     this.string = string; 
    } 

    public String getString() { 
     return string; 
    } 

    // All of the other accessor methods, etc. 
} 

Quindi, è possibile definire il metodo di utilizzo al punto in questo modo:

public abstract void use(UseParameters params); 

E qualsiasi codice che utilizza un elemento sarebbe necessario impostare i parametri dell'oggetto in modo appropriato:

Item item = // However you're going to get the item 
UseParameters params = new UseParameters(); 
params.setString("good string"); 
params.setQueue(new Queue()); 
item.use(params); 

Voglio solo far notare che se il codice di cui sopra conosce l'Oggetto è un Libro (che è come sa impostare String e Coda, allora perché non prendi semplicemente un Libro e salta la necessità di una classe astratta con una variazione ble use() del tutto? Ma sto divagando. In ogni caso, il libro sarebbe quindi implementare il metodo uso() in questo modo:

@Override 
public void use(UseParameters params) { 
    if(params.getString == null || params.getQueue() == null) 
     // throw exception 

    // Do what books do with strings and queues 
} 

penso che si ottiene ciò che si vuole, ma si dovrebbe prendere in considerazione il refactoring, credo.

+0

Grazie per la risposta dettagliata. Sono ancora in un punto in cui posso ridisegnare l'approccio, che sembra essere l'opzione migliore. Potrebbe esserci abbastanza somiglianza per raggruppare in diverse interfacce come menzionato in un post successivo. Altrimenti può essere sensato abbandonare l'interfaccia e assicurarsi che l'uso sia incluso da solo. – kaledev

5

Se i tipi da utilizzare come argomento sono sempre variabili, non vedo un motivo per utilizzare i generici. Basta usare pianura Tipo di oggetto:

public abstract class Item { 
    public abstract void use(Object ... arguments); 
} 

public class Book extends Item { 
    public void use(Object ... arguments) { ... } 
} 
1

L'approccio migliore che riesco a pensare è raggruppare gli articoli in base al comportamento del loro metodo use().

Esempio

public abstract class QueueableItem { 
    public abstract void use(String, Queue); 
} 

public abstract class OrdinaryItem{ 
    public abstract void use(String); 
} 

Se gli elementi raggruppati condividono un comportamento comune (comune come nella stessa firma del metodo & valore di ritorno), è possibile definire ed estendere una classe genitore che conterrà la definizione di questo comune comportamento.

8

Quello che vuoi è il Value Object Pattern.

Definire una classe che incapsula i vari tipi di parametro in un valore oggetto valore e che il metodo astratto accetti un parametro di questo tipo. Ogni variazione dei parametri che stavi considerando avrebbe una sua classe di valore.

Poi basta aggiungere un tipo generico alla classe e hanno il metodo astratto accettare un parametro di quel tipo:

public abstract class Item<V> { 
    public abstract void use(V v); 
} 

Per usarlo, si supponga MyItem ha bisogno di un oggetto valore di tipo MyValueClass:

public class MyItem extends Item<MyValueClass> { 
    public void use(MyValueClass v) { 
    } 
} 
0

Sì, possiamo fornire parametri al metodo astratto ma è necessario fornire lo stesso tipo di parametri ai metodi implementati che abbiamo scritto nelle classi derivate.

Problemi correlati