Überdenken des Java8-Lambda-Ausdrucks- und Stream-Entwurfsmusters - Muster der Vorlagenmethode -

Einführung

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.

Dieses Thema

Lassen Sie uns diesmal über das Muster "Vorlagenmethode" nachdenken, das auf dem "FizzBuzz-Problem" basiert.

Konventionelle Montagemethode

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

Implementierung mit Lambda-Ausdruck

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

Erwägung

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 " oder "Funktion <T, R>" ausgerichtet sind, ist die Verantwortung der Hook-Methode in der Regel schwer zu verstehen. Wagen Sie es daher, sie einzeln als Funktionstyp-Schnittstelle zu definieren. Es gibt auch Raum für Überlegungen bei der ** Klärung der Absicht **.

Recommended Posts

Überdenken des Java8-Lambda-Ausdrucks- und Stream-Entwurfsmusters - Muster der Vorlagenmethode -
Überdenken des Entwurfsmusters mit Java8 Lambda-Ausdruck & Stream - Befehlsmuster -
Überdenken von Entwurfsmustern mit Java8-Lambda-Ausdrücken und Stream - Null-Objektmuster -
Überdenken des Java8-Lambda-Ausdrucks- und Stream-Entwurfsmusters - Muster der Verantwortungskette -
Entwurfsmuster ~ Vorlagenmethode ~
Java8-Stream, Zusammenfassung des Lambda-Ausdrucks
Entwurfsmuster nach Ruby Template-Methode Musternotiz
C # gekautes Designmuster: TemplateMethod
Java-Anfänger-Entwurfsmuster (Factory-Methodenmuster)
Überdenken von Entwurfsmustern mit Java8-Lambda-Ausdrücken und Stream - Builder-Muster -
Java-Entwurfsmuster
Muster der Vorlagenmethode
Muster der Vorlagenmethode
[Java] Lambda-Ausdruck
Java Lambda Ausdruck
Entwurfsmuster ~ Fabrikmethode ~
Java Neutral Lambda Tabellenausdruck 1
Variationen von Java-Lambda-Ausdrücken
Java 8 Lambda-Ausdruck Feature
Java Lambda Ausdruck Memo
Java Lambda Ausdruck [Notiz schreiben]
Java 8 studieren (Lambda-Ausdruck)
Überprüfen Sie java8 ~ Lambda Typ ~
Zusammenfassung des Java-Entwurfsmusters
[Java] Funktionsschnittstelle / Lambda-Ausdruck
[Entwurfsmuster] Java-Kernbibliothek
Über Lambda, Stream, LocalDate von Java8
Java Basic Learning Content 9 (Lambda-Ausdruck)
Vergleich der Thread-Implementierungsmethoden in Java und der Lambda-Ausdrucksmethode
Lassen Sie uns nun den Java-Lambda-Ausdruck rekapitulieren
Java-Methode
Java-Methode
Ich habe Javas Lambda-Ausdruck Stream-API ein halbes Jahr nach dem Start von Java überprüft.
[Java] -Methode
[Java] -Methode
Welches ist schneller, Methodenreferenz oder Lambda-Ausdruck
Verwendung der Java-API mit Lambda-Ausdrücken
Java 8 startet jetzt ~ für jeden und Lambda-Ausdruck ~