Repenser les modèles d'expression et de conception de flux Java8 - Modèle de méthode

introduction

Nous examinerons une nouvelle méthode d'implémentation des modèles de conception à l'aide de l'API Stream, qui est une expression lambda introduite dans Java8. Dans l'article précédent (http://qiita.com/yonetty/items/a6afb566c4079a6cf34c), nous avons examiné comment implémenter le modèle Builder à l'aide d'expressions lambda.

Ce thème

Cette fois, pensons au modèle Template Method basé sur le problème FizzBuzz.

Méthode de montage conventionnelle

Afin de réutiliser un comportement commun (logique), définissez une méthode modèle dans la classe abstraite avec final, et définissez la partie variable comme une méthode hook ʻabstract. En implémentant la méthode hook dans la classe concrète, il est possible d'exprimer un comportement individuel tout en conservant un comportement commun. (La définition d'une méthode de modèle final` empêche les sous-classes de remplacer le comportement commun)

La classe abstraite ressemble à ceci: count (int, int) est la méthode du modèle.

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

Une implémentation de FizzBuzz. Remplace 5 méthodes «abstraites».

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

Une autre implémentation, le Nabeats Counter, donnera un «!» Au lieu de dire au stupide. (!! pour les multiples de 3 avec 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) + "!!";
    }

}

Exemple d'utilisation:

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

Implémentation à l'aide d'une expression lambda

Le traitement des variables doit être passé en tant que fonction plutôt que de remplacer la méthode hook de la classe abstraite parente. Pour la lisibilité du code côté utilisateur, le modèle précédent Builder est appliqué, mais s'il est écrit simplement, il sera passé comme argument de la méthode modèle. De plus, puisqu'il s'agit d'un exemple de code, la vérification de la satisfaction des conditions préalables de «Builder» est omise.

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

L'assemblage et l'exécution réels de FizzBuzz et Nabeats sont les suivants.

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

Considération

L'avantage de cette méthode est que ** vous n'avez pas à hériter d'une classe spécifique pour utiliser la logique **. Il existe également une idée orientée objet selon laquelle l'héritage dans le but de réutiliser l'implémentation doit être évité. Bien sûr, il existe de nombreux cas où la méthode de mise en œuvre conventionnelle a une meilleure visibilité et est plus facile à gérer, donc je pense qu'il serait bon qu'elle puisse être conçue au cas par cas.

Tips Si vous passez la méthode hook en tant que fonction à l'argument de la méthode template, le code sera mal visible s'il y en a trop. Dans ce cas, il est nécessaire de concevoir comme appliquer le modèle «Builder» comme dans cet exemple. Aussi, si les types de chaque argument sont alignés avec Predicate <T> ou Function <T, R>, la responsabilité de la méthode hook a tendance à être difficile à comprendre, alors osez la définir individuellement comme une interface de type de fonction. Il est également possible d'envisager ** de clarifier l'intention **.

Recommended Posts

Repenser les modèles d'expression et de conception de flux Java8 - Modèle de méthode
Repenser le modèle d'expression et de conception de flux Java8 Lambda - Modèle de commande -
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é -
Modèle de conception ~ Méthode de modèle ~
Flux Java8, résumé de l'expression lambda
Modèle de conception par la méthode Ruby Template Mémo de modèle
Modèle de conception mâché C #: TemplateMethod
Modèle de conception Java pour débutant (modèle de méthode d'usine)
Repenser les modèles de conception avec les expressions lambda Java8 et Stream --Builder pattern -
Modèle de conception Java
Modèle de méthode de modèle
Modèle de modèle de méthode
[Java] Expression Lambda
Expression lambda Java
Modèle de conception ~ Méthode d'usine ~
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
expression lambda java [écriture de notes]
Etudier Java 8 (expression lambda)
Évaluer java8 ~ Type Lambda ~
Résumé du modèle de conception Java
[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)
Comparaison des méthodes d'implémentation de thread en Java et de la méthode d'expression lambda
Récapitulons maintenant l'expression lambda Java
Méthode Java
Méthode Java
J'ai examiné l'expression lambda de Java, l'API de flux, six mois après avoir commencé Java.
[Java] méthode
[Java] méthode
Qui est plus rapide, référence de méthode ou expression lambda
Comment utiliser l'API Java avec des expressions lambda
Java 8 pour démarrer maintenant ~ for Each et expression lambda ~