Repenser les modèles de conception avec les expressions lambda Java8 et Stream --Builder pattern -

introduction

Avec l'introduction des expressions lambda et de l'API Stream en Java8, le langage Java a enfin apporté le paradigme de la programmation fonctionnelle. En conséquence, les «normes» de conception changent également. Vous devriez vous renseigner sur l'API Stream et les expressions lambda dans le bon livre "Functional Programming with Java" publié par O'Reilly. , Le chapitre 4 «Conception avec Lambda» de ce livre présente de nouvelles idées de conception utilisant Lambda. Dans cet article, l'auteur, inspiré par les livres ci-dessus, examine et envisage de nouvelles façons d'implémenter des modèles de conception à l'aide d'expressions lambda et de flux.

Ce modèle

Considérez le modèle Builder, l'un des modèles pour la génération GoF. Veuillez vous référer à cette page pour l'implémentation du modèle Builder en Java.

Amélioration de l'interface fluide

Les techniques suivantes sont présentées dans "Programmation fonctionnelle avec Java".

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

L'exemple d'utilisation de «Builder» ci-dessus est le suivant.

FluentBuilder-Usage


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

Les avantages par rapport au constructeur conventionnel qui s'assemble avec l '"interface fluide" sont les suivants.

――Parce qu'il n'utilise pas le mot-clé new, il est plus lisible et plus fluide.

problème

L'un des inconvénients de l'utilisation d'un "Builder" de type "interface fluide" est lorsque vous souhaitez changer d'appels de méthode en fonction des conditions, ou lorsque vous souhaitez appeler des méthodes à plusieurs reprises. En raison des spécifications du langage Java, il n'est pas possible d'incorporer la syntaxe de contrôle dans une chaîne de méthodes reliée par des points ., donc la chaîne de méthodes est divisée au milieu et contrôlée à l'aide d'instructions if et for, ce qui entraîne Je ne suis plus "courant". Ne pouvez-vous pas bien résoudre ce problème?

Incorporer le contrôle conditionnel

Ajoutez une méthode qui appelle Consumer <T> uniquement si l'expression conditionnelle est true.

MoreFluentBuilder


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

Bien que l'expression lambda soit imbriquée, nous avons pu incorporer un contrôle conditionnel sans rompre la chaîne de méthodes.

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!");
        });

Incorporer un contrôle itératif

Cette fois, c'est un peu compliqué car nous devons prendre deux types de fonctions comme arguments. Tout d'abord, il reçoit l'objet à répéter dans le type ʻIterable . Et le deuxième argument est «BiConsumer <T, U>». Le but est d'utiliser BiConsumer <T, U>au lieu deConsumer car il est nécessaire de recevoir et de traiter la référence de l'élément répétitif (une instance de typeT) et de l'instance Builder`. ..

MoreFluentBuilder2


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

J'ai pu intégrer des contrôles itératifs sans rompre la chaîne de méthodes, comme indiqué ci-dessous.

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!");
        });

Résumé

En faisant bon usage des expressions lambda, vous pouvez écrire du code plus propre et plus clair. Cette fois, j'ai examiné comment rendre plus fluide l'implémentation du modèle «Builder» du type «interface fluide».

Recommended Posts

Repenser les modèles de conception avec les expressions lambda Java8 et Stream --Builder pattern -
Repenser le modèle d'expression et de conception de flux Java8 Lambda - Modèle d'objet nul -
Java8 Lambda Expression & Stream Design Pattern Repenser - Modèle de chaîne de responsabilité -
Repenser le modèle d'expression et de conception de flux Java8 Lambda - Modèle de commande -
Repenser les modèles d'expression et de conception de flux Java8 - Modèle de méthode
Gérez les exceptions avec fraîcheur avec les expressions lambda Java 8 et l'API Stream
Modèles de conception à utiliser avec les bibliothèques Java fréquemment utilisées - Modèle d'usine
De nos jours, les expressions Java Lambda et l'API de flux
Modèle de conception ~ Constructeur ~
Modèle de conception (2): constructeur
Modèles de conception Java
Comprendre les expressions lambda Java 8
À propos des expressions Java lambda
Expliquer les expressions lambda Java 8
Modèle de générateur (Java effectif)
Résumé du modèle de conception Java
Utiliser des expressions Java lambda en dehors de l'API Stream
Premiers pas avec les anciens ingénieurs Java (Stream + Lambda)
Introduction aux modèles de conception (Builder)
[Design pattern] Bibliothèque de base Java
Flux Java8, résumé de l'expression lambda
[Java] Introduction à l'expression lambda
[Java] Résumé des modèles de conception
[Java 8] Suppression en double (et vérification en double) avec Stream
[Introduction à Java] À propos des expressions lambda
À propos de Lambda, Stream, LocalDate de Java8
[Java] Vérification de l'existence des éléments avec Stream
Modèle de conception Java pour débutant (modèle de méthode d'usine)
L'origine des expressions Java lambda
Comment utiliser les expressions Java lambda
Conversion de liste Java8 avec Stream map
J'ai essayé de résumer les expressions Java lambda
AWS Lambda (Lambda) Partie 1 avec Java pour démarrer maintenant
[Modèle de conception] Logique commune avec la méthode de modèle
Gérez d'énormes JSON avec Java Lambda
Facile à parcourir avec les expressions régulières Java