2015-08-01 12 views
6

Cosa fare se le classi con la stessa interfaccia hanno una firma del metodo simile ma diversa?Cosa fare se le classi con la stessa interfaccia hanno una firma di metodo simile ma diversa?

Diciamo che ho un progetto per calcolare costi diversi (per ottenere un costo totale alla fine).

Nel mio programma, ci sono diverse classi di calcolatori, ovvero ACostCalculator, BCostCalculator e così via. Quando viene invocato un metodo calculate() per calcolare un costo, viene passato un contenitore di costi anche a tali calcolatori di costi. In un buon scenario, posso creare un'interfaccia CostCalculator per ogni calcolatrice di costi.

Tuttavia, il calcolo per costo diverso ha richiesto risorse diverse. Nel mio programma in corso, che fanno essere come:

//getResource() are costly method while several costs need this. So do it outside calculate() method. 
ResourceA resourceA = getResourceA(); 
ResourceB resourceB = getResourceB(); 

CostContainer costContainer = new CostContainer(); 
CostCalculator aCostCalculator = new ACostCalculator(); 
... 
CostCalculator eCostCalculator = new ECostCalculator(); 

aCostCalculator.calculate(costContainer); 
bCostCalculator.calculate(costContainer) 
cCostCalculator.calculate(costContainer, resourceA); 
dCostCalculator.calculate(costContainer, resourceA); 
eCostCalculator.calculate(costContainer, resourceA, resourceB); 

Se la firma è esattamente lo stesso, posso fare un ciclo favorevole per farlo in una sola volta. Tuttavia, poiché sono simili ma diversi, non riesco nemmeno a fare una buona interfaccia.

Non sono sicuro che ci siano buoni modi per farlo. Quello che mi viene in mente è generalizzare tutto calculate() metodo per in

calculate(CostContainer costContainer, List<Object> resources); 

Tutte le idee? Grazie per aver risposto.

+0

... o varargs anziché l'elenco. –

risposta

5

Se le risorse rimangono invariate per la durata di vita dei calcolatori: passare le risorse al costruttore delle calcolatrici.

ResourceA resourceA = getResourceA(); 
ResourceB resourceB = getResourceB(); 

CostContainer costContainer = new CostContainer(); 

CostCalculator aCostCalculator = new ACostCalculator(); 
CostCalculator bCostCalculator = new BCostCalculator(); 
CostCalculator cCostCalculator = new CCostCalculator(resourceA); 
CostCalculator dCostCalculator = new DCostCalculator(resourceA); 
CostCalculator eCostCalculator = new ECostCalculator(resourceA, resourceB); 

aCostCalculator.calculate(costContainer); 
bCostCalculator.calculate(costContainer); 
cCostCalculator.calculate(costContainer); 
dCostCalculator.calculate(costContainer); 
eCostCalculator.calculate(costContainer); 
+1

Questa è l'unica soluzione sicura per il tipo finora. E anche se gli argomenti non sono statici durante la vita dei calcolatori, è possibile implementare un meccanismo simile usando una classe wrapper. –

+0

Bene, sembra perfettamente valido e pulito, mentre si adatta alla mia custodia. –

+0

È già abbastanza bello e semplice. Ma vediamo se qualcuno può dare un'altra risposta geniale –

0

È possibile utilizzare variadic arguments:

public interface CostCalculatorInterface { 
    public void calculate(CostContainer container, Object... resources); 
} 

(o sostituire con un altro Object superclasse di ResourceA e ResourceB).

Nelle classi che implementano l'interfaccia, resources sarà un Object[] così si può fare riferimento a loro come resources[0], resources[1] e così via.

+2

Questo non è sicuro per il tipo però ... –

+0

se si passa il numero sbagliato di risorse può essere generata un'eccezione – ka4eli

+0

Grazie per l'asnwering. La sicurezza del tipo è uno dei problemi e anche per la leggibilità. Un altro programmatore potrebbe dover spendere qualche sforzo per scoprire cosa sia "risorse [0]". –

-1

Questo è qualcosa che effettivamente utilizzati con i generici:

Resource resourceA = new ResourceA(); 
    Resource resourceB = new ResourceB(); 
    CostContainer costContainer = new CostContainer(); 
    CostCalculator<Resource> costCalculatorA = new ACostCalculator(); 
    costCalculatorA.calculate(costContainer,resourceA,resourceB); 
    CostCalculator<Resource> costCalculatorB = new BCostCalculator(); 
    costCalculatorB.calculate(costContainer,resourceA); 


interface Resource { 
    //Your code 
} 

class ResourceA implements Resource { 
    //Your code 
} 

class ResourceB implements Resource { 
    //Your code 
} 

class CostContainer { 
    //Your code 
} 

interface CostCalculator<T extends Resource> { 
    void calculate(CostContainer costContainer, T... resources); 
} 

class ACostCalculator implements CostCalculator<Resource>{ 

    @Override 
    public void calculate(CostContainer costContainer, Resource... resources) { 
     System.out.println("Test"); 
    } 
} 

class BCostCalculator implements CostCalculator<Resource>{ 

    @Override 
    public void calculate(CostContainer costContainer, Resource... resources) { 
     System.out.println("Test2"); 
    } 
} 
+0

Grazie per aver risposto. Tuttavia ci sono difetti simili alla risposta di Glorfindel. –

2

Il problema di varia firme per un'interfaccia comune suona molto come il problema che le Adapter design pattern (object adapter variant) risolve:

Adapter pattern (GoF)

Applicato alla situazione, si utilizzano solo adattatori per le calcolatrici non conformi. Esistono solo due tipi di adattatori, Tipo1 per la firma (costContainer, resourceA) e Tipo2 per la firma (costContainer, resourceA, resourceB). Usando il tuo esempio:

Adapter pattern applied to your example

vantaggi di adattatore sono che è un design pattern noto (pubblicato da GoF nel 1995), consente eventuali calculate(...) metodi che hanno firme diverse. Gli adattatori possono essere aggiornati in modo dinamico se si verificano cambiamenti di contesto (ad esempio, le risorse cambiano).

Gli svantaggi sono ovviamente le classi aggiuntive, l'indiretto, ecc. È più complicato della risposta selezionata, ma è più flessibile in particolare se non è possibile modificare l'API degli adattamenti.

+0

Grazie per la risposta. La risposta di lesmana non funziona se il ciclo di vita della risorsa e il calcolatore dei costi non corrispondono, ad es. costo calulator è un singleton. È possibile applicare il modello di adattatore in tutte le situazioni? Suppongo di sì (presumendo che il calcolatore dei costi sia thread-safe)? –

+0

@ MichaelW.Patterns sono sempre soluzioni generali al livello del problema che intendono risolvere. Applicarlo al codice è l'unico modo per saperlo con certezza. Il codice cliente vede solo gli adattatori. Finché sono istanziati e gestiti correttamente, non dovrebbe importare se gli adattamenti sono singleton. È possibile aggiungere ad esempio, 'setResourceA()', se è necessario modificare il contesto degli adattatori in modo dinamico. Il modello fornisce solo un'interfaccia standard. Devi occuparti del resto dei (brutti) dettagli. – Fuhrmanator

+0

Ottenuto grazie. Fammi provare più tardi. –

Problemi correlati