println(String)的接口

4
我想编写Java代码(比如一个方法),用于打印一些行。调用者提供要打印的对象。我希望我的代码不关心该对象的具体内容,只需调用该对象的println()或println(String)方法即可。它应该适用于java.io.PrintStream(例如System.out)或java.io.PrintWriter(例如由调用者使用new PrintWriter(System.out)或new PrintWriter(new ByteArrayOutputStream())构造的对象)。如果“printlineable”对象的潜在类共享某个接口,强制执行println()和println(String)方法,则这将很容易。但是它们没有。那么我在签名中放什么来接收这样的对象,而不违反DRY原则(当我简单地重载该函数时必须交换实现的类型)?
public void sayHello( ??? outThingy) {
    outThingy.println("Hello World");
    outThingy.println();
    // This just a minimal example.
    // The real implementation might be more involved
    // and non-trivial, so that avoiding duplication
    // becomes a real concern.
};


// sayHello should be usable like this:

sayHello(System.out);


// but also like this:

baos = new ByteArrayOutputStream();
pw = new PrintWriter(baos)

sayHello(pw);

pw.flush();
System.out.println(baos.toString());

或者说,PrintStreamPrintWriter没有共享这样一个接口的事实,应该被视为它们在提供打印行的方式上不可互换的指示吗?(而不是在这些类被指定时存在某种历史疏忽。)

3个回答

2
最简单的方法就是重载方法,分别提供一个接受PrintWriter和一个接受PrintStream的版本:
public void sayHello(PrintStream outThingy) {
    outThingy.println("Hello World");
    outThingy.println();
};
public void sayHello(PrintWriter outThingy) {
    outThingy.println("Hello World");
    outThingy.println();
};

有没有一种方法可以不违反DRY原则?对于我在问题中使用的简短示例代码,这不是问题,但我的真实函数更加复杂。 - das-g
@das-g 是的:void sayHello(PrintStream out) { PrintWriter pw = new PrintWriter(out); sayHello(pw); pw.flush(); },然后实现 void sayHello(PrintWriter out) 方法。 - Andreas
嘿,我目前的代码(我想重构)实际上有点像这样,@Andreas:它当前接受一个OutputStream并始终将其包装在PrintWriter中。 - das-g
我的问题的动机是我在想,我的代码是否应该使用PrintStream进行包装。 (如果它已经是PrintStream,则可能根本不需要包装对象?)我的结论是最好由调用者决定。因此,我想以一种方式重构我的代码,使得调用者已经提供了一个通用的“东西”,我的代码可以在其上println()而无需任何包装,但也许这是不可能的,除非对调用者施加新接口,当前Java标准库类没有实现。 - das-g

2

以下是一种方法,您可以通过它至少使 outThingy 的客户端保持DRY。但是,您将进行有效地拥有几个WET类的折衷。尽管如此,代码量很小。


// Printer allows for a common interface
interface Printer {
  void println(String line);
  void println();
}

// Used with PrintStream
class StreamPrinter implements Printer {
  private PrintStream ps;

  public StreamPrinter(PrintStream ps) {
    this.ps = ps;
  }

  @Override
  public void println(String line) {
    ps.println(line);
  }

  @Override
  public void println() {
    ps.println();
  }
}

// Used with PrintWriter
class TypeWriter implements Printer {
  private PrintWriter pw;

  public TypeWriter(PrintWriter pw) {
    this.pw = pw;
  }

  @Override
  public void println(String line) {
    pw.println(line);
  }

  @Override
  public void println() {
    pw.println();
  }
}

class Talker {
  // This class doesn't care!
  void sayHello(Printer printer) {
    printer.println("Hello world");
    printer.println();
  }
}

1
你可能对一种不同的、更实用的方法感兴趣。与其担心每种类型提供了什么和如何找到它们之间的共同接口,你可以通过使用ConsumerRunnable作为println方法的表示来用更少的代码实现相同的功能。
// This is the common class
class FuncPrinter {
  private Consumer<String> consumer;
  private Runnable runnable;

  public FuncPrinter(PrintWriter writer) {
    consumer = writer::println;
    runnable = writer::println;
  }

  public FuncPrinter(PrintStream stream) {
    consumer = stream::println;
    runnable = stream::println;
  }

  public void println(String line) {
    consumer.accept(line);
  }

  public void println() {
    runnable.run();
  }
}

class Talker {    
  void sayHello(FuncPrinter fp) {
    fp.println("Hello World");
    fp.println();
  }
}

And you could use it like so:

Talker t = new Talker();

FuncPrinter fp = new FuncPrinter(System.out);
t.sayHello(fp);

ByteArrayOutputStream ostream = new ByteArrayOutputStream();
PrintWriter pw = new PrintWriter(ostream);
fp = new FuncPrinter(pw);
t.sayHello(fp);

fp = new FuncPrinter(
  line -> System.out.println(line),
  () -> System.out.println(42));
t.sayHello(fp);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接