Java – Object vs static method design

java

As shown below, there are two straightforward ways I could make a stream copier (bar introducing Apache Commons or similar). Which one should I go for, and why ?

public class StreamCopier {
private int bufferSize;

public StreamCopier() {
    this(4096);
}

public StreamCopier(int bufferSize) {
    this.bufferSize = bufferSize;
}

public long copy(InputStream in , OutputStream out ) throws IOException{
    byte[] buffer = new byte[bufferSize];
    int bytesRead;
    long totalBytes = 0;
    while((bytesRead= in.read(buffer)) != -1) {
        out.write(buffer,0,bytesRead);
        totalBytes += bytesRead;
    }

    return totalBytes;
}
}

vs

 public class StreamCopier {

 public static long copy(InputStream in , OutputStream out)
     throws IOException {
     return this.copy(in,out,4096);
 }

 public static long copy(InputStream in , OutputStream out,int bufferSize)
     throws IOException {
     byte[] buffer = new byte[bufferSize];
     int bytesRead;
     long totalBytes = 0;
     while ((bytesRead= in.read(buffer)) != -1) {
         out.write(buffer,0,bytesRead);
         totalBytes += bytesRead;
     }

     return totalBytes;
}
}

Best Solution

I'd go with the non-static (instance) version, and supply it to consumers as an explicit dependency with a setter:

  • mocking it out for unit testing is then trivial, so tests of consumers aren't coupled to the implementation;
  • swapping out functionality is straightforward, eg: using a subclass;
  • works nicely with dependency injection systems.

Edit

In response to a (useful!) comment of "how does this help mocking?", here's how it might work:

class ThingThatUsesStreamCopier {

    // our copier instance. set in constructor, but might equally use
    // a setter for this:
    private StreamCopier copier;

    public ThingThatUsesStreamCopier(StreamCopier copier) {
        this.copier = copier;
    }

    public void makeCopy(Stream in, Stream out) {
        // probably something a little less trivial...
        copier.copy(in, out);
    }
}

When I come to test ThingThatUsesStreamCopier, I can create a mock object version of a StreamCopier and instantiate the ThingThatUsesStreamCopier using this mock.

By doing so, I have full control over the behaviour of my mock, so my test is decoupled from any real implementations of StreamCopier. I am only testing the consumer, not the consumer plus the consumed.

Related Question