Wir werden eine neue Implementierungsmethode für Entwurfsmuster unter Verwendung der Stream-API betrachten, einem in Java8 eingeführten Lambda-Ausdruck. Im vorherigen Artikel (http://qiita.com/yonetty/items/a6afb566c4079a6cf34c) haben wir uns überlegt, wie das Builder-Muster mithilfe von Lambda-Ausdrücken implementiert werden kann.
Lassen Sie uns diesmal über das Muster "Vorlagenmethode" nachdenken, das auf dem "FizzBuzz-Problem" basiert.
Um allgemeines Verhalten (Logik) wiederzuverwenden, definieren Sie eine Vorlagenmethode in der abstrakten Klasse mit "final" und definieren Sie den variablen Teil als Hook-Methode "abstract". Durch die Implementierung der Hook-Methode in der konkreten Klasse ist es möglich, individuelles Verhalten auszudrücken und dabei das gemeinsame Verhalten beizubehalten. (Das Definieren einer Vorlagenmethode "final" verhindert, dass Unterklassen das allgemeine Verhalten überschreiben.)
Die abstrakte Klasse sieht folgendermaßen aus:
count (int, int)
ist die Vorlagenmethode.
AbstractCounter.java
public abstract class AbstractCounter {
public final void count(final int from, final int to) {
for (int i = from; i <= to; i++) {
final String expression;
if (condition1(i)) {
expression = condition2(i) ? onBoth(i) : onCondition1(i);
} else {
expression = condition2(i) ? onCondition2(i) : String.valueOf(i);
}
System.out.print(expression + ",");
}
}
abstract boolean condition1(int num);
abstract boolean condition2(int num);
abstract String onCondition1(int num);
abstract String onCondition2(int num);
abstract String onBoth(int num);
}
Eine Implementierung von FizzBuzz. Überschreiben Sie 5 "abstrakte" Methoden.
FizzBuzzCounter.java
public class FizzBuzzCounter extends AbstractCounter {
@Override
boolean condition1(int num) {
return num % 3 == 0;
}
@Override
boolean condition2(int num) {
return num % 5 == 0;
}
@Override
String onCondition1(int num) {
return "Fizz";
}
@Override
String onCondition2(int num) {
return "Buzz";
}
@Override
String onBoth(int num) {
return "FizzBuzz";
}
}
Eine andere Implementierung, der Nabeats-Zähler, gibt ein "!", Anstatt das Dumme zu sagen. (!!
für Vielfache von 3 mit 3)
NabeatsuCounter.java
public class NabeatsuCounter extends AbstractCounter {
@Override
boolean condition1(int num) {
return num % 3 == 0;
}
@Override
boolean condition2(int num) {
return String.valueOf(num).contains("3");
}
@Override
String onCondition1(int num) {
return String.valueOf(num) + "!";
}
@Override
String onCondition2(int num) {
return String.valueOf(num) + "!";
}
@Override
String onBoth(int num) {
return String.valueOf(num) + "!!";
}
}
Anwendungsbeispiel:
Usage
System.out.println("FizzBuzz : ");
FizzBuzzCounter counter = new FizzBuzzCounter();
//1,2,Fizz,4,Buzz,Fizz,7,8,Fizz,Buzz,11,Fizz,13,14,FizzBuzz,16,17,Fizz,19,Buzz,
counter.count(1, 20);
System.out.println();
System.out.println("Nabeatsu : ");
NabeatsuCounter counter2 = new NabeatsuCounter();
//21!,22,23!,24!,25,26,27!,28,29,30!!,31!,32!,33!!,34!,35!,36!!,37!,38!,39!!,40,
counter2.count(21, 40);
Die Variablenverarbeitung sollte als Funktion übergeben werden, anstatt die Hook-Methode der übergeordneten abstrakten Klasse zu überschreiben. Für die Lesbarkeit des Codes auf der Benutzerseite wird das vorherige Builder-Muster angewendet. Wenn es jedoch einfach geschrieben wird, wird es als Argument der Vorlagenmethode übergeben. Da es sich um einen Beispielcode handelt, wird die Überprüfung, ob die Voraussetzungen von "Builder" erfüllt sind, weggelassen.
Counter.java
public class Counter {
private Predicate<Integer> condition1;
private Predicate<Integer> condition2;
private Function<Integer, String> converter1;
private Function<Integer, String> converter2;
private Function<Integer, String> converterBoth;
private int from;
private int to;
private Counter() {
}
public Counter first(Predicate<Integer> condition1, Function<Integer, String> converter1) {
this.condition1 = condition1;
this.converter1 = converter1;
return this;
}
public Counter second(Predicate<Integer> condition2, Function<Integer, String> converter2) {
this.condition2 = condition2;
this.converter2 = converter2;
return this;
}
public Counter onBoth(Function<Integer, String> converterBoth) {
this.converterBoth = converterBoth;
return this;
}
public Counter fromTo(int from, int to) {
this.from = from;
this.to = to;
return this;
}
public static void count(final Consumer<Counter> consumer) {
Counter counter = new Counter();
consumer.accept(counter);
counter.doCount();
}
private void doCount() {
String result = IntStream.rangeClosed(from, to)
.mapToObj(this::map)
.collect(Collectors.joining(","));
System.out.println(result);
}
private String map(int num) {
if (condition1.test(num)) {
return condition2.test(num) ? converterBoth.apply(num) : converter1.apply(num);
} else {
return condition2.test(num) ? converter2.apply(num) : String.valueOf(num);
}
}
}
Die eigentliche Montage und Ausführung von FizzBuzz und Nabeats ist wie folgt.
Usage
//1,2,Fizz,4,Buzz,Fizz,7,8,Fizz,Buzz,11,Fizz,13,14,FizzBuzz,16,17,Fizz,19,Buzz
Counter.count(c -> c.first(num -> num % 3 == 0, num -> "Fizz")
.second(num -> num % 5 == 0, num -> "Buzz")
.onBoth(num -> "FizzBuzz")
.fromTo(1, 20));
//21!,22,23!,24!,25,26,27!,28,29,30!!,31!,32!,33!!,34!,35!,36!!,37!,38!,39!!,40
Counter.count(c -> c.first(num -> num % 3 == 0, num -> String.valueOf(num) + "!")
.second(num -> String.valueOf(num).contains("3"), num -> String.valueOf(num) + "!")
.onBoth(num -> String.valueOf(num) + "!!")
.fromTo(21, 40));
Der Vorteil dieser Methode ist, dass ** Sie keine bestimmte Klasse erben müssen, um die Logik zu verwenden **. Es gibt auch eine objektorientierte Idee, dass Vererbung zum Zwecke der Wiederverwendung der Implementierung vermieden werden sollte. Natürlich ist die herkömmliche Implementierungsmethode oft einfacher zu sehen und zu verwalten. Ich denke, es wäre schön, wenn sie von Fall zu Fall entworfen werden könnte.
Tips
Wenn Sie die Hook-Methode als Funktion an das Argument der Template-Methode übergeben, ist der Code schlecht sichtbar, wenn zu viele vorhanden sind. In diesem Fall ist es erforderlich, beispielsweise das "Builder" -Muster wie in diesem Beispiel anzuwenden.
Wenn die Typen der einzelnen Argumente mit "Prädikat