la prova03 evidenzia che nella classe printSlip la evaluate non sarebbe testabile perché usa lo standard output:
public void evaluate(Object aResource) {incidentalmente questa implementazione corrisponde al layout sintetico, descritto più avanti, che ha output tipo:
System.out.println(((Resource)aResource).name());
System.out.println(((Resource)aResource).salary());
}
"Francesco\n1000.00\n"
Esiste anche un layout più dettagliato, che viene chiesto di implementare:
Il layout dettagliato della busta paga di una risorsa ha il seguente formato: "Nome:
Abbiamo due obiettivi:
1)rendere testabile la printSlip,
2)poter gestire più di un layout.
Per il n.1, astraggo sulla modalità di gestione dell'output, quindi la "stampa" userà un generico OutputStreamer (interfaccia che creiamo poi, che prescrive un unico metodo "print") e posso usare un mio OutputStreamer "fittizio" (mock) solo per i test.
Per il n.2, rendo astratta la classe PrintSlip stessa, delegandone la concreta implementazione della evaluate (sintetica o analitica), che implementeranno i due di layout:
public abstract class PrintSlip implements Block {
protected OutputStreamer outputStreamer = new OutputStreamer()
{
public void print(Object object) {
System.out.print(object);
}
};
public void setOutputStreamer(OutputStreamer outputStreamer) {
this.outputStreamer=outputStreamer;
}
public OutputStreamer getOutputStreamer() {
return outputStreamer;
}
public abstract void evaluate(Object aResource);
}
Implementazine "sintetica":
public class PrintSlipImplSyntetic extends PrintSlip {
public void evaluate(Object aResource) {
outputStreamer.print(((Resource)aResource).name());
outputStreamer.print("\n");
outputStreamer.print(((Resource)aResource).salary());
}
}
Implementazione "analitica":
public class PrintSlipAnalitic extends PrintSlip{
public void evaluate(Object aResource) {
outputStreamer.print("Nome:"+((Resource)aResource).name()+",Salario:"+((Resource)aResource).salary()+"\n");
}
}
Per testare queste due novità mocko la evaluate in modo che l'output sia diretto in una stringa:
@Test
public void testPrintSlip() {
Contract nov1 = new Contract(nov1st2005());
PrintSlip printSlip = new PrintSlipImplSyntetic();
printSlip.setOutputStreamer(
new OutputStreamer() {
public void print(Object object) {
result += object;
}
}
);
MockedEmployee employee1 = new MockedEmployee("Francesco", nov1);
employee1.setPrintSlip(printSlip);
employee1.printSlip();
String results = printSlip.getOutputStreamer().getResult();
assertEquals("Francesco\n1024.89",results);
}
@Test
public void testPrintSlipAnaliticWithFactory() {
Contract nov1 = new Contract(nov1st2005());
PrintSlipFactory factory = new PrintSlipFactory();
factory.setCurrentLayout(PrintSlipFactory.ANALYTIC);
PrintSlip printSlip = factory.getPrintSlipWithCurrentLayout();
printSlip.setOutputStreamer(
new OutputStreamer() {
public void print(Object object) {
result += object;
}
}
);
MockedEmployee employee1 = new MockedEmployee("Francesco", nov1);
employee1.setPrintSlip(printSlip);
employee1.printSlip();
String results = printSlip.getOutputStreamer().getResult();
assertEquals(results, "Nome:Francesco,Salario:1024.89\n");
}
Btw sia in questa prova (prova03) che in quella precedente sembra che si suggerisca di incapsulare le PrintSlip e InForce per rendere alcuni oggetti meno... "tristi"
Io le ho spostate entrambe in Resource:
abstract public class Resource {
protected Block printSlip = new PrintSlipImplSyntetic();
public boolean isInForce()
{
InForce inForce = new InForce();
return inForce.is(this);
}
public void setPrintSlip(PrintSlip printSlip)
{
this.printSlip=printSlip;
}
public Block getPrintSlip()
{
return printSlip;
}
public Resource(String name, Contract contract) {
_name = name;
_contract = contract;
}
abstract public double salary();
public Contract lastContract() {
// Semplifichiamo :) la ricerca dell'ultimo contratto
return _contract;
}
public String name() {
return _name;
}
public String toString() {
return _name + ":" + _contract + ".";
}
private String _name;
private Contract _contract;
public void printSlip() {
printSlip.evaluate(this);
}
}
In questo modo il test precedente può diviene:
@Test
public void testPrintSlipDefaultWithFactory() {
Contract nov1 = new Contract(nov1st2005());
PrintSlipFactory factory = new PrintSlipFactory();
factory.setCurrentLayout(PrintSlipFactory.SINTETIC);
PrintSlip printSlip = factory.getPrintSlipWithCurrentLayout();
printSlip.setOutputStreamer(
new OutputStreamer() {
public void print(Object object) {
result += object;
}
}
);
MockedEmployee employee1 = new MockedEmployee("Francesco", nov1);
employee1.setPrintSlip(printSlip);
employee1.printSlip();
String results = printSlip.getOutputStreamer().getResult();
assertEquals(results, "Francesco\n1024.89");
}
posto, comunque, che abbia preso come decisione di adottare una factory che consenta di settare un layout, e restituire poi la printSlip relativa al layout così settato:
public class PrintSlipFactory {
public static String SINTETIC="SINTETIC";
public static String ANALYTIC="ANALYTIC";
private OutputStreamer currentOutputStreamer= new OutputStreamer() {
public void print(Object object) {
System.out.print(object);
}
};
private String currentLayout="SINTETIC";
private static HashMap mapLayouts=new HashMap();
static {
mapLayouts.put("SINTETIC",PrintSlipImplSyntetic.class);
mapLayouts.put("ANALYTIC",PrintSlipAnalitic.class);
}
public void setCurrentLayout(String layout) {
this.currentLayout = layout;
}
public void setCurrentOutputStreamer(OutputStreamer outputStreamer)
{
this.currentOutputStreamer=outputStreamer;
}
public PrintSlip getPrintSlipWithCurrentLayout() {
try {
PrintSlip toReturn = (PrintSlip)((Class)mapLayouts.get(currentLayout)).newInstance();
toReturn.setOutputStreamer(currentOutputStreamer);
return toReturn;
} catch (Exception e)
{
// unable to set layout
}
// default
return new PrintSlipImplSyntetic();
}
}
Ultima cosa, in relazione alla gestione dell'output su file di testo, penso ad una logica che sfrutti l'outputStreamer pensato per gestire il mock, e faccio un test allo scopo per vedere se può funzionare:
@Test
public void testPrintSlipAnaliticWithFactoryFileTextOutputStreamer() {
Contract nov1 = new Contract(nov1st2005());
PrintSlipFactory factory = new PrintSlipFactory();
factory.setCurrentLayout(PrintSlipFactory.ANALYTIC);
PrintSlip printSlip = factory.getPrintSlipWithCurrentLayout();
final MockedEmployee employee1 = new MockedEmployee("Francesco", nov1);
printSlip.setOutputStreamer(
new OutputStreamer() {
public void print(Object object) {
try {
File outputFile = new File("./" + employee1.name());
FileOutputStream outputStream = new FileOutputStream(outputFile);
outputStream.write(((String) object).getBytes());
outputStream.flush();
outputStream.close();
}
catch (Exception e) {
fail(e.toString());
}
}
}
);
employee1.setPrintSlip(printSlip);
employee1.printSlip();
File file = new File("./" + employee1.name());
assertTrue(file.exists());
file.delete();
}
Infine arricchisco la classe department (che contiene una collezione di cui esegue la printSlip rispetto a tute le risorse in forza), in modo che questa stessa classe essa stessa possa gestire i layout e l'OutputStreamer:
public class Department {
private PrintSlipFactory printSlipFactory = new PrintSlipFactory();
public void setPrintSlipLayout(String layout) {
printSlipFactory.setCurrentLayout(layout);
}
public void setPrintSlipOutputStreamer(OutputStreamer outputStreamer) {
printSlipFactory.setCurrentOutputStreamer(outputStreamer);
}
public Department(List resources){
_resources = resources;
}
public void printSlips() {
new OrderedCollection(_resources).select(new InForce()).forEachDo(printSlipFactory.getPrintSlipWithCurrentLayout());
}
private List _resources;
}
Mancano la parte R5 ed R6 della prova03, ed infine la prova4.
Al prossimo post, magari.
Bye.
Nessun commento:
Posta un commento