2012-12-28 10 views
6

Considera un problema, in cui sto sviluppando un albero come Collection.Collezione Lazy in Java

Una delle funzionalità principale della mia raccolta è quello di tracciare tutti gli elementi memorizzati uno per uno e poi chiamare un data funzione per ogni elemento fino a dato criterio è stato raggiunto (Collection pigro).

Quindi la funzione dovrebbe avere le seguenti firme:

void Trace(function func, criteria crit) 
{ 
    item i = firstItem(); 
    while (i != endItem()) 
    { 
     i = nextItem(); 
     func(i); 
     if (crit(i)) 
      return; 
    } 
} 

in C++ puntatori a funzione può essere utilizzata per func e crit.
in C#, yield parola chiave è esattamente la soluzione a questo problema, credo.

Come posso ottenere la stessa cosa in Java?

+1

Il tuo ciclo ha un bug. Non elabora l'ultimo elemento. Considera una lista se lunghezza 1 - il ciclo while non verrebbe inserito. – Bohemian

+0

Good Point :) Ma fortunatamente, questo non ha molto a che fare con il problema :) – MBZ

risposta

1

Creare un'interfaccia che dichiari i metodi e richiedere un riferimento a un oggetto che implementa l'interfaccia come argomento. Il chiamante può creare l'oggetto usando una classe interna anonima.

2

In Java, si dovrebbe passare riferimenti a oggetti di classi che implementano funzioni applicabili, o utilizzare Commons Collezioni invece:

  • Usa Predicate implementazioni per la parte crit.
  • Utilizzare le implementazioni Closure per la parte func.

Ad esempio:

Closure c = new Closure() { 
    public void execute(Object obj) { 
     ... 
    } 
}; 

Predicate p = new Predicate() { 
    public boolean evaluate(Object obj) { 
     ... 
    } 
} 

Trace(c, p); 
+0

+1, e se si preferisce rimanere con il core Java, [Callable] (http://docs.oracle.com/javase/ 7/docs/api/java/util/concurrent/Callable.html) fornisce un'interfaccia generica ragionevole per cose simili alla chiusura che possono restituire un valore. –

+0

@RyanStewart, il mio problema con 'Callable' è che non fornisce l'API voluta dall'OP. Il metodo 'call()' non riceve parametri, il che significa che dovresti creare una nuova istanza dell'implementazione 'Callable' con ogni iterazione. – Isaac

2

Quello che stai cercando qui è il modello Strategy design.

L'obiettivo di questo modello è di astrarre l'implementazione di un algoritmo in un oggetto Strategia. Qui, i tuoi algoritmi sono le funzioni func e crit che stai cercando di passare.

Quindi, avresti un'interfaccia chiamata qualcosa come TraceStrategy. Passerai quindi le implementazioni di questa interfaccia alla tua raccolta. Il tuo codice sarebbe quindi simile a

void Trace(TraceStrategy traceStrategy) 
{ 
    item i = firstItem(); 
    while (i != endItem()) 
    { 
     i = nextItem(); 
     traceStrategy.func(i); 
     if (traceStrategy.crit(i)) 
      return; 
    } 
} 

e

interface TraceStrategy { 
    public boolean crit(item i); 

    public void func(item i); 
} 

Si sarebbe probabilmente vuole fare questo generica, in modo che non erano legati a item ... ma si ottiene l'idea.

+1

Il problema con questo approccio è che ci sarà sempre un accoppiamento tra 'crit' e' func'. Se avete 'x' possibilità per' crit' e 'y' possibilità per' func', potreste finire per creare implementazioni 'x * y' di' TraceStrategy' ... quindi, la parametrizzazione è un approccio leggermente migliore IMO. – Isaac

+0

@Isaac, punto eccellente. È possibile creare un 'TraceStrategyFactory' che utilizza gli oggetti' CritStrategy' e 'FuncStrategy' e restituisce un oggetto composito composto da quei due ... ma il punto su come accoppiare queste due funzioni è molto appropriato. – Dancrumb

+0

Sì. Concordo sul fatto che una combinazione di schemi strategici e parametrizzazione sarebbe la soluzione migliore per le esigenze dell'OP in generale. – Isaac

1

si può fare questo trace funzione funzionano bene in Java mediante la combinazione di un paio di tecniche:

  • Invece di "puntatori a funzione", i parametri func e crit dovrebbe essere istanze di oggetti che implementano una specifica interfaccia. È quindi possibile chiamare una funzione in questa interfaccia sull'oggetto i. In effetti, questo è un Pattern Vistor con due diversi parametri di vistor.
  • Hai anche bisogno di un modo per attraversare l'albero. È possibile implementare un Iterator - questo ti dà un buon modo per attraversare l'intera struttura. In alternativa è possibile effettuare la ricorsiva di trace (si chiama sui rami sinistro e destro dell'albero) e quindi non è necessario un iteratore.

La versione iteratore sarebbe simile a questa:

public void trace(IFunction func, ICriteria crit) { 
    for (T i: this) { 
     func.call(i); 
     if (crit.test(i)) return; 
    } 
} 

Qui T è il tipo di elemento della collezione, e call e test sono le definizioni di funzione nelle interfacce IFunction e ICriteria rispettivamente.