Интерфейс для печати (String) - PullRequest
4 голосов
/ 02 июля 2019

Я хотел бы написать код 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());

Или тот факт, что PrintStream и PrintWriter не используют такой интерфейс, следует рассматривать как указание на то, что они не 't взаимозаменяемы в отношении предоставления способа печати строк?(Вместо того, чтобы быть каким-то историческим упущением, когда эти классы были указаны.)

Ответы [ 3 ]

2 голосов
/ 02 июля 2019

Вот способ, которым вы могли бы сделать это и, по крайней мере, оставить клиенту 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();
  }
}
2 голосов
/ 02 июля 2019

Самый простой способ - просто перегрузить метод версией, которая принимает PrintWriter, и версией, которая принимает PrintStream:

public void sayHello(PrintStream outThingy) {
    outThingy.println("Hello World");
    outThingy.println();
};
public void sayHello(PrintWriter outThingy) {
    outThingy.println("Hello World");
    outThingy.println();
};
1 голос
/ 02 июля 2019

Вас может заинтересовать другой, более функциональный подход.Вместо того, чтобы беспокоиться о том, что предлагает каждый тип, и о том, как найти общий interface между ними, вы можете добиться того же с меньшим количеством кода, используя Consumer и Runnable в качестве представлений методов 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();
  }
}

И вы можете использовать это так:

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);
...