Tuesday, February 16, 2010

Single responsibility principle

Single responsibility principle

Recently i needed to compile our all jasper-report files during application start-up.
I was modifying existing code-base to enable precompiling xml reports to binary .jreport files.

It was simple enough, traverse all *.jrxml files at a certain directory and use net.sf.jasperreports.engine.JasperCompileManager to do the actual compiling.

Here is how i traversed all files under a directory.

    private void visitAllFiles(File dir)  {
if (dir.isDirectory()) {
String[] children = dir.list();
for (int i = 0; i < children.length; i++) {
visitAllFiles(new File(dir, children[i]));
}
} else {
//doSomething();
}
}


Here is what is wrong with this version.

The infrastructure logic to traverse the files and business logic to compile reports resides in same class.
If i needed traverse files for something else (for example to backup old compiled reports) i would need to duplicate infrastructure logic.
It seems this class is trying to do too many things. I separated this class to a reusable FileCrawler and FileVisitor.
 
public interface FileVisitor {
void visit(File fileToProcess) ;
}

FileCrawler takes a visitor and calls visit method for each file it encounters.

     
public FileCrawler(final File root, final FileVisitor visitor) {
this.visitor = visitor;
}

private void visitAllFiles(File dir) {
if (dir.isDirectory()) {
String[] children = dir.list();
for (int i = 0; i < children.length; i++) {
visitAllFiles(new File(dir, children[i]));
}
} else {
visitor.visit(dir);
}
}

Usage is as follows:

  new FileCrawler(new File(this.cacheFilePath), new ReportCompiler()).start();


ReportCompiler implements FileVisitor and looks like as follows:


public void visit(File fileToProcess)  {
String name = fileToProcess.getName();
if (name.endsWith(".jrxml")){
compileReport(fileToProcess.getParent(),name);
}
}

Now i can  use anonymous inner classes to create simple reusable directory traversing logic.

new FileCrawler (new File (root), new FileVisitor() {
public void visit(File fileToProcess) {
String name = fileToProcess.getName();
if (name.endsWith(".jreport")){
fileToProcess.delete();
}
}
});


No comments: