Überdenken von Entwurfsmustern mit Java8-Lambda-Ausdrücken und Stream - Builder-Muster -

Einführung

Mit der Einführung von Lambda-Ausdrücken und der Stream-API in Java8 hat die Java-Sprache endlich das Paradigma der funktionalen Programmierung gebracht. Infolgedessen ändern sich auch die Designstandards. Informationen zu Stream-API- und Lambda-Ausdrücken finden Sie in dem guten Buch "Functional Programming with Java", das von O'Reilly veröffentlicht wurde. In Kapitel 4 "Entwerfen mit Lambda" in diesem Buch werden neue Designideen vorgestellt, die Lambda verwenden. In diesem Artikel untersucht und erwägt der Autor, inspiriert von den oben genannten Büchern, neue Möglichkeiten zur Implementierung von Entwurfsmustern mithilfe von Lambda-Ausdrücken und Streams.

Dieses Muster

Betrachten Sie das Builder-Muster, eines der Muster für die GoF-Generierung. Informationen zur Implementierung des Builder-Musters in Java finden Sie unter diese Seite.

Verbesserung der fließenden Schnittstelle

Die folgenden Techniken werden in "Funktionale Programmierung mit Java" vorgestellt.

FluentBuilder


public class MailBuilder {

    private String fromAddress = "";
    private String toAddress = "";
    private List<String> ccAddresses = new ArrayList<>();
    private String subject = "";
    private String body = "";

    private MailBuilder() {
    }

    public MailBuilder from(String address) {
        this.fromAddress = address;
        return this;
    }

    public MailBuilder to(String address) {
        this.toAddress = address;
        return this;
    }

    public MailBuilder cc(String address) {
        this.ccAddresses.add(address);
        return this;
    }

    public MailBuilder subject(String subject) {
        this.subject = subject;
        return this;
    }

    public MailBuilder body(String body) {
        this.body = body;
        return this;
    }

    private void doSend() {
        StringBuilder sb = new StringBuilder();
        sb.append("TO:").append(toAddress).append("\r\n");
        if (!ccAddresses.isEmpty()) {
            sb.append("CC:").append(String.join(",", ccAddresses)).append("\r\n");
        }
        sb.append("FROM:").append(fromAddress).append("\r\n");
        sb.append("SUBJECT:").append(subject).append("\r\n");
        sb.append("BODY:").append(body).append("\r\n");
        System.out.println(sb.toString());
    }

    public static void send(final Consumer<MailBuilder> consumer) {
        final MailBuilder mailer = new MailBuilder();
        consumer.accept(mailer);
        mailer.doSend();
    }

Das Verwendungsbeispiel von "Builder" oben ist wie folgt.

FluentBuilder-Usage


        MailBuilder.send(mailer -> {
            mailer.from("[email protected]")
                    .to("[email protected]")
                    .subject("Greeting")
                    .body("Hello, Mr. President!");
        });

Die Vorteile gegenüber dem herkömmlichen Builder, der mit der "fließenden Schnittstelle" zusammengebaut wird, sind folgende.

――Da das Schlüsselwort "new" nicht verwendet wird, ist es lesbarer und fließender.

Problem

Eine der Unannehmlichkeiten bei der Verwendung eines Builders vom Typ "fließende Schnittstelle" besteht darin, dass Sie Methodenaufrufe abhängig von den Bedingungen wechseln oder Methoden wiederholt aufrufen möchten. Aufgrund der Java-Sprachspezifikationen ist es nicht möglich, die Steuerungssyntax in eine durch Punkte "." Verbundene Methodenkette einzubetten. Daher wird die Methodenkette in die Mitte unterteilt und mithilfe von if- und for-Anweisungen gesteuert Ich bin nicht mehr fließend. Können Sie dieses Problem nicht gut lösen?

Bedingte Kontrolle einbinden

Fügen Sie eine Methode hinzu, die "Consumer " nur aufruft, wenn der bedingte Ausdruck "true" ist.

MoreFluentBuilder


    public MailBuilder doIf(boolean condition, final Consumer<MailBuilder> consumer) {
        if (condition) {
            consumer.accept(this);
        }
        return this;
    }

Obwohl der Lambda-Ausdruck verschachtelt wäre, konnten wir die bedingte Kontrolle integrieren, ohne die Methodenkette zu unterbrechen.

MoreluentBuilder-Usage


        MailBuilder.send(mailer -> {
            mailer.from("[email protected]")
                    .to("[email protected]")
                    .doIf(someCondition(), m -> m.cc("[email protected]"))
                    .subject("Greeting")
                    .body("Hello, Mr. President!");
        });

Integrieren Sie iterative Kontrolle

Diesmal ist es etwas kompliziert, da wir zwei Funktionstypen als Argumente verwenden müssen. Zunächst empfängt es das Objekt, das im Typ "Iterable " wiederholt werden soll. Und das zweite Argument ist "BiConsumer <T, U>". Es geht darum, "BiConsumer <T, U>" anstelle von "Consumer " zu verwenden, da die Referenz des sich wiederholenden Elements (eine Instanz vom Typ "T") und der "Builder" -Instanz empfangen und verarbeitet werden muss. ..

MoreFluentBuilder2


    public <T> MailBuilder foreach(Iterable<T> iterable, final BiConsumer<MailBuilder, T> consumer) {
        iterable.forEach(t -> consumer.accept(this, t));
        return this;
    }

Ich konnte iterative Steuerelemente einbetten, ohne die Methodenkette zu unterbrechen, wie unten gezeigt.

MoreFluentBuilder2-Usage


        final List<String> ccAddresses = Arrays.asList("[email protected]", "[email protected]");
        MailBuilder.send(mailer -> {
            mailer.from("[email protected]")
                    .to("[email protected]")
                    .foreach(ccAddresses, (m, ccAddress) -> m.cc(ccAddress))
                    .subject("Greeting")
                    .body("Hello, Mr. President!");
        });

Zusammenfassung

Wenn Sie Lambda-Ausdrücke gut nutzen, können Sie Code schreiben, der sauberer und klarer ist. Dieses Mal habe ich untersucht, wie die Implementierung des Builder-Musters vom Typ "Fluent Interface" fließender gestaltet werden kann.

Recommended Posts

Überdenken von Entwurfsmustern mit Java8-Lambda-Ausdrücken und Stream - Builder-Muster -
Überdenken von Entwurfsmustern mit Java8-Lambda-Ausdrücken und Stream - Null-Objektmuster -
Überdenken des Java8-Lambda-Ausdrucks- und Stream-Entwurfsmusters - Muster der Verantwortungskette -
Überdenken des Entwurfsmusters mit Java8 Lambda-Ausdruck & Stream - Befehlsmuster -
Überdenken des Java8-Lambda-Ausdrucks- und Stream-Entwurfsmusters - Muster der Vorlagenmethode -
Behandeln Sie Ausnahmen kühl mit Java 8-Lambda-Ausdrücken und der Stream-API
Entwerfen Sie Muster für häufig verwendete Java-Bibliotheken - Fabrikmuster
Heutzutage Java Lambda Expressions und Stream API
Entwurfsmuster ~ Builder ~
Entwurfsmuster (2): Builder
Java-Entwurfsmuster
Verstehen Sie Java 8 Lambda-Ausdrücke
Über Java-Lambda-Ausdrücke
Erläutern Sie Java 8-Lambda-Ausdrücke
Builder-Muster (effektives Java)
Zusammenfassung des Java-Entwurfsmusters
Verwenden Sie Java-Lambda-Ausdrücke außerhalb der Stream-API
Erste Schritte mit älteren Java-Ingenieuren (Stream + Lambda)
Einführung in Entwurfsmuster (Builder)
[Entwurfsmuster] Java-Kernbibliothek
Java8-Stream, Zusammenfassung des Lambda-Ausdrucks
[Java] Einführung in den Lambda-Ausdruck
[Java] Zusammenfassung der Entwurfsmuster
[Java 8] Doppelte Löschung (& doppelte Überprüfung) mit Stream
[Einführung in Java] Über Lambda-Ausdrücke
Über Lambda, Stream, LocalDate von Java8
[Java] Elementexistenzprüfung mit Stream
Java-Anfänger-Entwurfsmuster (Factory-Methodenmuster)
Der Ursprung von Java-Lambda-Ausdrücken
Verwendung von Java-Lambda-Ausdrücken
Java8-Listenkonvertierung mit Stream Map
Ich habe versucht, Java-Lambda-Ausdrücke zusammenzufassen
AWS Lambda (Lambda) Teil 1 mit Java startet jetzt
[Entwurfsmuster] Allgemeine Logik mit der Vorlagenmethode
Behandeln Sie große JSON mit Java Lambda
Einfach mit regulären Java-Ausdrücken zu stolpern