对象与静态方法设计

Ano*_*nym 10 java

如下所示,我可以通过两种简单的方式制作流式复印机(介绍Apache Commons或类似的).我应该选择哪一个,为什么?

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;
}
}
Run Code Online (Sandbox Code Playgroud)

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;
}
}
Run Code Online (Sandbox Code Playgroud)

Dan*_*ton 21

我将使用非静态(实例)版本,并将其作为与setter的显式依赖关系提供给消费者:

  • 因此,对单元测试进行嘲弄是微不足道的,因此对消费者的测试并没有与实现相结合;
  • 交换功能很简单,例如:使用子类;
  • 与依赖注入系统很好地配合.

编辑

回应一个(有用的!)评论"这有助于嘲笑?",这是它的工作方式:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

当我来测试时ThingThatUsesStreamCopier,我可以创建一个模拟对象版本的a StreamCopierThingThatUsesStreamCopier使用这个模拟实例化.

通过这样做,我可以完全控制我的模拟行为,所以我的测试与任何实际的实现分离StreamCopier.我只测试消费者,而不是消费者和消费者.

  • +1,嘲弄静态方法几乎不可能. (3认同)
  • 所以,这是我对这个论点的理解:对于实例版本,用户只会说`new StreamCopier().copy(in,out)`.如何更容易嘲笑或换出? (2认同)

dic*_*ice 11

我会选择静态版本,因为没有状态.

除非您需要用于继承目的(虚方法),否则无状态对象通常没有意义.

如果用户可能想要模拟功能,那么我更喜欢接口而不是具体实现 - 接口的实现者不能是静态的,所以在这种情况下你必须使用实例化的对象.

编辑:几年后,我现在希望谴责我以前的自我建议静态版本.这些天我会毫不犹豫地选择实例版本.

  • 测试依赖于静态方法的代码更加困难,因为覆盖静态方法的行为更加困难.根据我使用Java和C#的经验,能够模拟对象导致测试开发比使用库覆盖静态方法快得多. (2认同)

Gle*_*len 6

我会选择静态版本.

由于您没有存储状态,因此不需要对象,因此为什么要让调用者创建一个对象来调用方法.