Non partecipando, comunque ho deciso di affrontare il problema, per il momento commento come avrei affrontato la prova01:
Il codice e' versionato nel repos. aziendale.
prima di fare il refactoring richiesto, che chiede di sfruttare la somiglianza tra evaluate e forEachDo, creo dei test appositi per essi, e poi rifattorizzo, e verifico che i test passano anche dopo il refactoring.
Per testare la forEachDo:
mi appoggio ad una implementazione di Block che implementa la evaluate concatenando i toString degli oggetti processati in una unica stringa globale.
Se applico la evaluate con questa implementazione di Block, ad una collezione contenente "first" e "second" mi aspetto che dopo il test la stringa globale abbia concatenato "first" e "second".
Il test e' il seguente:public class BlockImplForTest implements Block {
public static String references="";
public static void reset() {
references="";
}
public void evaluate(Object object)
{
references+=object.toString();
}
}
Per rendere piu' evidente che Il Block che uso deve essere "fresco", lo reperisco tramite questo metodo che fa anche un reset dello stato:
@Test
public void testSelect() {
OrderedCollection col = new OrderedCollection();
col.add("first");
col.add("second");
Block stringRefAppender=getFreshBlockImplForTest();
col.forEachDo(stringRefAppender);
assertEquals(BlockImplForTest.references,"first"+"second");
assertFalse(BlockImplForTest.references.equals("wrong"));
}
BlockImplForTest getFreshBlockImplForTest() {
BlockImplForTest.reset();
return new BlockImplForTest();
}
Forse, come design, e' discutibile aver usato una variabile globale, ma allo scopo del test va piu' che bene.
Il test del select crea una classe anonima che implementa il PredicateBlock in modo che che la is restituisca true solamente se la stringa vale "second".
In questo modo, mi aspetto che la lista filtrata attraverso questo PredicateBlock contenga solamente la stringa second. Sfrutto parte della logica del precedente test per scandagliare la collezione restituita e verificare che contenga solamente "second".
Il test ci assicura che l'attuale implementazione dei metodi di OrderCollection fanno quello che ci aspettiamo, ed ora la si rifattorizza cercando di sfruttare la somiglianza tra forEachDo e select.
@Test
public void testSelector() {
OrderedCollection col = new OrderedCollection();
col.add("first");
col.add("second");
Block stringRefAppender=getFreshBlockImplForTest();
assertEquals("",BlockImplForTest.references);
PredicateBlock getOnlySecond= new PredicateBlock()
{
public boolean is(Object object) {
return "second".equals(object);
}
};
OrderedCollection ordCol = col.select(getOnlySecond);
ordCol.forEachDo(stringRefAppender);
assertEquals("second",BlockImplForTest.references);
}
Entrambe eseguono la scansione della collezione, solo che una esegue una certa operazione, e lo fa su tutti gli elementi, senza restituire nulla, l'altra restituisce una sottocollezione di tutti i membri della collezione che soddisfano un certo predicato.
Ragionando un attimo, dal punto di vista logico possono essere due casi particolari di una unica operazione che scandisce gli elementi , valuta il predicato, esegue l'operazione ed (eventualmente) restituisce gli elementi processati.
Il caso particolare della forEachDo e' che il predicato restituisce sempre true, che non ci interessa utilizzare il valore restituito, mentre il caso particolare della select e' che la evaluate non esegue nulla.
La rifattorizziamo dunque creando due implementazioni di Block e PredicateBlock in accordo con questi due casi particolari:
1) Creiamo il Block e il PredicateBlock corrispondente ai casi particolari appena discusi:
private static final PredicateBlock passesAll =
new PredicateBlock() {
public boolean is(Object object) {
return true;
}
};
private static final Block neuterBlock =
new Block() {
public void evaluate(Object object) { }
};
2) Aggiungiamo un metodo "evaluate and select":
3) Trasformiamo la ForEach e la Select in modo che siano appunto casi particolari dell'utilizzo di evaluateAndSelect sfruttando le implementazioni "neutre" di Block ed PredicateBlock:
private OrderedCollection evaluateAndSelect(PredicateBlock pb, Block b)
{
OrderedCollection result = new OrderedCollection();
Iterator iterator = _items.iterator();
while(iterator.hasNext()) {
Object object = iterator.next();
if (pb.is(object)) {
b.evaluate(object);
result.add(object);
}
}
return result;
}
public OrderedCollection select(PredicateBlock aBlock) {
return evaluateAndSelect(aBlock,neuterBlock);
}
public void forEachDo(Block aBlock) {
evaluateAndSelect(passesAll,aBlock);
}
Nessun commento:
Posta un commento