tisdag 17 mars 2009

Templatemethod

Fortsätter med strategier för att ta bort duplicerad kod. Förra var ett exempel på en abstract factory. Nu blir det en template method. Ett enkelt scenario där det är lätt att man börjar upprepa kod är när man ska använda en extern resurs, tex en fil. Man måste öppna filen för läsning och kanske läsa in den till minnet, man ska bearbeta filen och till sist ska man stänga filen. Avsett vad man ska göra med innehållet i filen så måste man utföra det första och sista steget. Följande metod är ett exempel:

public void handleContent(File file){
InputStream stream = getInputStreamFromFile(file)
processStreamData(stream);
closeStreamAndFile(stream, file);
}

Något förenklat säkert, men att skriva en variant på processStreamData utan att behöva använda upprepa anropen till getInputStreamFromFile(File file) och closeStreamAndFile(InputStream stream, File file) har sina sidor. Exemplet ovan ser inte så illa ut men det är nog inte varje metod som implementerar ett motsvarande mönster som har brutit ner det till tre rader.

Så hur komma ifrån duplicering om man vill skriva en varant på handleContent?

Vi vet att två av metoderna kommer att upprepas, öppna och stänga. Vi vet dessutom att vi vill stoppa in något mellan. Följande klass är första steget.

public abstract class TemplateMethodExample{

// Method för att öppna strömmen/filen
private InputStream getInputStreamFromFile(File file){ .... }

// Metod för att städa efter sig
private void closeStreamAndFile(InputStream stream, File file){ ....}

// Den metod som vi kommer att anropa och som öppnar
// processar och stänger
public handleContent(InputStream stream){
InputStream stream = getInputStreamFromFile(file)
processStreamData(stream);
closeStreamAndFile(stream, file);
}

// Den metod vi ska overrida med specifik funktionallitet
// i en konkret klass
protected abstract processStreamData(InputStream stream);
}

Tricket här är att vi gör en abstrakt klass med en abstrakt metod som där vi kan implementera den specifika funktionallitet vi önskar. När vi sedan vill använda vår specifika implementation så anropar vi handleContent(File file) som är klassens enda publika funktion och den i sin tur använder den implementation av processStreamData(InputStream stream) som har den specifika funktionallitet vi önskar.

Som extra vinst har vi dessutom effektivt hindrat att någon glömmer stänga filen efter sig.

Inga kommentarer:

Skicka en kommentar