Repenser le modèle d'expression et de conception de flux Java8 Lambda - Modèle d'objet nul -

introduction

Dans cette série, nous examinerons comment implémenter des modèles de conception à l'aide de l'expression lambda / API Stream introduite dans Java 8. L'exemple de code est téléchargé sur GitHub.

Ce modèle

J'ai mentionné le modèle Null Object dans article précédent, mais j'ai omis l'explication, donc je vais la couvrir cette fois. Le modèle Null Object est un code sûr qui ne nécessite pas de vérification null en référençant unobjet qui implémente uneinterface spécifique au lieu d'une référence null mais ne fait rien (n'a pas d'effets secondaires). C'est une technique d'écriture.

Implémentation traditionnelle

Supposons une classe qui délègue le traitement aux interfaces suivantes.

Discount.java


public interface Discount {
    Integer calcDiscount(Integer totalAmount);
}

En supposant qu'une référence à une instance de la classe d'implémentation de Dicsount est donnée dans le constructeur ou le setter, la vérification de null suivante sera requise en tenant compte du cas de null lors de son utilisation.

OrderService.java(1)


    private Discount discount;

    public Integer getPrice(Integer amount) {
        if (discount != null) {
            amount -= discount.calcDiscount(amount);
        }
        return amount;
    }

Si vous utilisez la variable discount à plusieurs endroits, il est fastidieux de faire une vérification nulle à chaque fois. Par conséquent, définissez une instance qui ne fait rien (pour être exact, le traitement de manière à ne pas affecter le traitement côté utilisateur) comme suit, c'est-à-dire "Objet nul". «Null Object» est souvent défini comme une classe interne ou une classe interne anonyme comme indiqué ci-dessous.

OrderService.java(2)


    private Discount discount2 = new Discount() {
        @Override
        public Integer calcDiscount(Integer totalAmount) {
            return 0;
        }
    };

    public Integer getPrice2(Integer amount) {
        amount -= discount2.calcDiscount(amount);
        return amount;
    }

À propos, @ FunctionalInterface n'est pas ajouté à l'interface Discount, mais il peut être traité comme une interface fonctionnelle car il n'a qu'une seule méthode. Autrement dit, vous pouvez également définir un Null Object en utilisant une expression lambda comme celle-ci:

OrderService.java(3)


    private Discount discount3 = amount -> 0;

    public Integer getPrice3(Integer amount) {
        amount -= discount3.calcDiscount(amount);
        return amount;
    }

«Objet nul» fonctionnel

L'exemple suivant est un exemple d'utilitaire qui manipule les dates.

DateTimeBuilder.java


public class DateTimeBuilder {
    private LocalDateTime base;
    private Function<Temporal, Temporal> func;

    private DateTimeBuilder(LocalDateTime base) {
        this.base = base;
        func = t -> t;
    }

    private void compose(TemporalAdjuster next) {
        func = func.andThen(t -> next.adjustInto(t));
    }

    public DateTimeBuilder nextMonth() {
        compose(t -> t.plus(1, ChronoUnit.MONTHS));
        return this;
    }

    public DateTimeBuilder lastDay() {
        compose(TemporalAdjusters.lastDayOfMonth());
        return this;
    }

    public LocalDateTime get() {
        return (LocalDateTime) func.apply(base);
    }

    public static DateTimeBuilder of(LocalDateTime base) {
        DateTimeBuilder builder = new DateTimeBuilder(base);
        return builder;
    }
}

L'exemple d'utilisation est le suivant. Le code suivant recherche le dernier jour du mois suivant la date indiquée dans l'argument (2017/1 / 17).

Usage


        LocalDateTime base = LocalDateTime.of(2017, 1, 17, 0, 0);
        LocalDateTime dt = DateTimeBuilder.of(base)
                .nextMonth()
                .lastDay()
                .get();
        assertEquals(LocalDateTime.of(2017, 2, 28, 0, 0), dt);

Dans DateTimeBuilder, chaque fois que nextMonth () ʻou lastDay () ʻest appelé, la fonction qui convertit la date correspondante est synthétisée dans la variable d'instance func, et enfinget ()est La fonction composite est appliquée lorsqu'elle est appelée. Le code suivant est en cours de synthèse, mais pour le moment, le modèle Null Object est introduit afin qu'il ne soit pas nécessaire de considérer que NullPointerException ne se produit pas lorsque func est nul. Sera la motivation pour.

#méthode de composition


    private void compose(TemporalAdjuster next) {
        func = func.andThen(t -> next.adjustInto(t));
    }

Donc, dans le constructeur, définissez func sur une fonction qui ne fait rien. Le fait que le type Function <Temporal, Temporal> ne fasse rien signifie que l'objet de type Temporal reçu en argument est retourné tel quel (passé de droite à gauche). C'est «t-> t» lorsqu'il est écrit dans une expression lambda.

Fonction correspondant à NullObject


    private Function<Temporal, Temporal> func;

    private DateTimeBuilder(LocalDateTime base) {
        this.base = base;
        func = t -> t;
    }

Function # identity est l'élément unitaire

L'expression lambda ci-dessus t-> t doit utiliser la méthode statique ʻidentity ()définie dans l'interfacejava.util.Function`.

Function#identity()Utilisation de


    private DateTimeBuilder(LocalDateTime base) {
        this.base = base;
        //func = t -> t;
        func = Function.identity();
    }

Function # identity () retourne une fonction qui ne fait rien, c'est-à-dire une fonction qui renvoie l'argument d'entrée tel quel. Cette fonction peut être combinée avec une autre fonction de conversion (appelons-la «g») de gauche ou de droite pour obtenir une fonction de composition qui renvoie le même résultat que «g». D'un point de vue mathématique, cela peut être considéré comme un "élément unitaire" dans le système "Fonction <Temporel, Temporel>". En fait, la traduction anglaise de ʻunit element est ʻidentity element.

Résumé

Lors de la synthèse de fonctions, l'utilisation de Function # identity () comme élément d'unité comme valeur initiale semble réduire le code de contrôle nul compliqué et améliorer la visibilité du code.

Recommended Posts

Repenser le modèle d'expression et de conception de flux Java8 Lambda - Modèle d'objet nul -
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
Repenser les modèles de conception avec les expressions lambda Java8 et Stream --Builder pattern -
Flux Java8, résumé de l'expression lambda
Modèle de conception Java
[Java] Expression Lambda
Expression lambda Java
expression 1 de la table lambda neutre java
Variations d'expressions lambda Java
Fonctionnalité d'expression lambda Java 8
mémo d'expression java lambda
Etudier Java 8 (expression lambda)
Expression lambda Java à nouveau
Résumé du modèle de conception Java
Premiers pas avec les anciens ingénieurs Java (Stream + Lambda)
[Java] Interface fonctionnelle / expression lambda
[Design pattern] Bibliothèque de base Java
À propos de Lambda, Stream, LocalDate de Java8
Contenu d'apprentissage de base Java 9 (expression lambda)
Qu'est-ce qu'une expression lambda (Java)
Modèle de conception Java pour débutant (modèle de méthode d'usine)
Récapitulons maintenant l'expression lambda Java
De nos jours, les expressions Java Lambda et l'API de flux
[Java] Convertit null du type Object en type String
J'ai examiné l'expression lambda de Java, l'API de flux, six mois après avoir commencé Java.
Comment utiliser l'API Java avec des expressions lambda
Java 8 pour démarrer maintenant ~ for Each et expression lambda ~