Creating Directories With Fluent Interface

A short story about refactoring of a simple utility method - from boolean parameters to the fluent interface. All in the name of making the API perfect. :)

The Original Function

Once upon a time there was this method in TestUtils class (Javadocs omitted but we really had one!):

public static String createTemporaryNonWritableDir(String dirName)
            throws IOException {
    String destDir = TMP_DIR + FILE_SEPARATOR + dirName;
    File nonWritableDir = new File(destDir);
    // remove it if existed (this happens in /tmp so it is not cleaned by mvn clean)
    nonWritableDir.delete();

    // create and make NOT writable
    nonWritableDir.createNewFile();
    nonWritableDir.setWritable(false);
    return destDir;
}

The function worked fine, and you used it like this:

String destDir = TestUtils.createTemporaryNonWritableDir("non_writable_dir");

All Your Booleans Are Belong To Us!

Today when I came to work I noticed the function has evolved into this:

public static String createTemporaryDir
        (String dirName, boolean readable, boolean writable) throws IOException {
    String destDir = TMP_DIR + FILE_SEPARATOR + dirName;
    File directory = new File(destDir);
    // remove it if existed (this happens in /tmp so it is not cleaned by mvn clean)
    directory.delete();

    // create
    directory.createNewFile();
    // and make it NON readable if flag set
    directory.setReadable(readable);
    // and make it NON writable if flag set
    directory.setWritable(writable);
    return destDir;
}

I have to say I do not like this version. When using it, it is not clear what kind of directory will be created (which boolean relates to writing permission?)

String destDir = TestUtils.createTemporaryDir("non_writable_dir", true, false);

You will find much more about writing good tests in my book
"Practical Unit Testing
with TestNG and Mockito"

P.S. An additional downside was, that the original Javadoc was left as it was... eh.... as we all know: "documentation lies, tests never do" :)

Fluent Interface

I was thinking if I could improve this. An obvious way would be to create multiple methods like:

createTemporaryNonReadableDir(String dirName);
createTemporaryNonWritableDir(String dirName);
createTemporaryNonReadableNonWritableDir(String dirName);

but this would bloat the API, which I do not like.

So I gave it another try by using the builder pattern, like this:

public class TmpDirBuilder {

    private String dirName;
    private boolean readable = true;
    private boolean writable = true;

    public TmpDirBuilder withDirName(String dirName) {
        this.dirName = dirName;
        return this;
    }

    public TmpDirBuilder notReadable() {
        this.readable = false;
        return this;
    }

    public TmpDirBuilder notWritable() {
        this.writable = false;
        return this;
    }

    public String create() throws IOException {
        String destDir = TMP_DIR + FILE_SEPARATOR + dirName;
        File directory = new File(destDir);
        // remove it if existed (this happens in /tmp so it is not cleaned by mvn clean)
        directory.delete();
        // now create and set flags
        directory.createNewFile();
        directory.setReadable(readable);
        directory.setWritable(writable);
        return destDir;
    }
}

You can use it now like this:

String destDir = new TmpDirBuilder()
   .withDirName("non_writable_dir").notWritable().create();

Of course other combinations are also possible (e.g. notWritable().notReadable() etc.), and if one day we want to enhance this functionality with new features, it will be still possible.

The Question

So the question is if I improved the code or made it worse? What do you think?

Resources

Below few links to get you started with DSLs:

BTW. Damn it, I have to spent additional 20 minutes at work now that have written this post! :) lol

What about an Enum

Hi Tomek, how is it going?

I totally agree about ugliness of String,boolean,boolean version.
Your refinement using bulider pattern is certianly better and definitely interesting, however seems a little bit overkill in this case.
How about introducing an Enum named AccesMode in place of boolean,boolean flags?

PS. I just love those comments on createNewFile(), setReadable() and setWritable() method invocations. They certainly boost the quality of code at least three times :)

Jakub Głuszecki

Hi, good idea

But, I don't like your design. The interface is included in the TestUtils which is a util tool set. So it is better to use the interface directly, not like your TmpDirBuilder. I think the best way is to create your own tool set which use the createTemporaryDir interface to implement createTemporaryNonReadableDir, createTemporaryNonWritableDir and createTemporaryNonReadableNonWritableDir. How do you think? - low blood pressure | stress and blood pressure | high blood pressure remedies

i'm lost :)

not sure I follow: paraphrasing Linus Torvalds: "enough talk - show me the code!" :)

so, could you please write some (pseudo)code so we can discuss it?

--
Cheers,
Tomek

Hi, I understand his meaning.

Hi, I understand his meaning. What he said is not to modify JDK's interface. That is in the JDK, there's one general interface, while in you own software, if you want to be easier to use the interface, you could implement three different interfaces based on the JDK's general interface. I think this is what he said. - throat cancer pictures | strep throat | throat cancer

Hi Tomek, of course

Hi Tomek,

of course FluentInterfaces make code more friendly for everyone and this is real essence of this practice.

You can improve your interfaces with some minor refactors and removing create() method (code will be more readable)

- how to achieve this?
I don't tell you but follow this source: http://www.infoq.com/articles/internal-dsls-java

thank you

Thank you, I will definitely read the article you suggest.

 
 
 
This used to be my blog. I moved to http://tomek.kaczanowscy.pl long time ago.

 
 
 

Please comment using