Developpez.com - Rubrique Java

Le Club des Développeurs et IT Pro

Réflexion sur la mise en place du patron de conception Function Object en Java,

Un tutoriel de Yann Caron

Le 2013-05-17 15:00:44, par CyaNnOrangehead, Rédacteur
Bonjour à tous,

Je vous propose un petit article intitulé : « Fonction Object Design pattern - Tutoriel sur les foncteurs - En attendant les lambdas de Java 8 » disponible ici : http://caron-yann.developpez.com/tut...osures-java-8/

Ce tutoriel ce compose comme suit :
  • une présentation du design pattern « function object » ou plus connu sous le nom de foncteur ;
  • une implémentation de celui-ci en Java qui respecte le principe de signature des méthodes en s'appuyant sur les génériques (ce qui n'est pas tout à fait le cas de librairies telles que Guava) ;
  • des cas concrets d'utilisations comme la création de callbacks, l’ajout de méthodes fonctionnelles aux listes (each et map-filter-reduce), parcours depth-first, décorateur fonctionnel et conteneurs IOC.


Cette dernière partie est le résultat d'une discussion ouverte avec, entre autrez, Thierry, que je continuerai très volontiers avec vous.
Elle démontre que Java, depuis la version 5 de son framework, n'a rien à envier à des langages tels que Ruby. Et qu'il n'est pas nécessaire de ce mettre à Groovy pour faire du fonctionnel avec le langage Java.

Et n'oubliez pas de commenter cet article. Vos retours nous aident à améliorer nos publications.

Bonne lecture.
  Discussion forum
7 commentaires
  • professeur shadoko
    Membre chevronné
    Il me semblerait intéressant de compléter avec deux choses:
    - un véritable pattern Commande
    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    public interface CommandFor<T> {
        public  void invokeOn(T instance);
    }
    
    public class CommandForDoer  implements CommandFor<Doer>, Serializable {
        private final String aString ;
        private final int anInt ;
    
        public CommandForDoer(String aString, int anInt) {
            this.aString = aString;
            this.anInt = anInt;
        }
    
        @Override
        public void invokeOn(Doer instance) {
            instance.show(aString, anInt);
        }
    }
    avec utilisations

    - un exemple de dynamic Proxy
  • Envoyé par professeur shadoko
    Il me semblerait intéressant de compléter avec deux choses:
    - un véritable pattern Commande
    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    public interface CommandFor<T> {
        public  void invokeOn(T instance);
    }
    
    public class CommandForDoer  implements CommandFor<Doer>, Serializable {
        private final String aString ;
        private final int anInt ;
    
        public CommandForDoer(String aString, int anInt) {
            this.aString = aString;
            this.anInt = anInt;
        }
    
        @Override
        public void invokeOn(Doer instance) {
            instance.show(aString, anInt);
        }
    }
    avec utilisations
    Envoyé par professeur shadoko

    Oui très bonne idée.
    Envoyé par professeur shadoko

    - un exemple de dynamic Proxy
    Excellent, effectivement il y a moyen de faire des trucs pas mal en combinant les deux techniques.
    Je regarde dés que j'ai un moment !
  • Voici également la résolution d'un problème que je voulais ajouter :
    Compter le nombre de fois ou l'on trouve un mot dans un texte.

    Le principe est le suivant :
    Un premier map/reduce s'occupe du split "\n"
    Un second, à l'intérieur s'occupe de compter les mots "elements"

    Voilà ce que ça donne :
    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    	public void testCountWords() throws Exception {
    
    		String csv = "" +
    			"element 1.1, element 1.2, element 1.3, element 1.4\n" +
    			"element 2.1, element 2.2\n" +
    			"element 3.1, element 3.2, element 3.3\n" +
    			"element 4.1, element 4.2, element 4.3, element 4.4, element 4.5\n" +
    			"";
    
    		int count = split(csv, "\n").<Integer>map(new Function<Integer, Arguments2<String, Integer>>() {
    
    			@Override
    			public Integer invoke(Arguments2<String, Integer> arguments) {
    				// the line
    				System.out.println("Line : " + arguments.getArgument1());
    
    				return split(arguments.getArgument1(), " ").<Integer>map(new Function<Integer, Arguments2<String, Integer>>() {
    
    					@Override
    					public Integer invoke(Arguments2<String, Integer> arguments) {
    						// the element
    						System.out.println("Word :" + arguments.getArgument1());
    
    						if ("element".equalsIgnoreCase(arguments.getArgument1())) {
    							return 1;
    						} else {
    							return 0;
    						}
    					}
    				}).reduce(new Function<Integer, Arguments2<Integer, Integer>>() {
    
    					@Override
    					public Integer invoke(Arguments2<Integer, Integer> arguments) {
    						return arguments.getArgument1() + arguments.getArgument2();
    					}
    				});
    
    			}
    		}).reduce(new Function<Integer, Arguments2<Integer, Integer>>() {
    
    			@Override
    			public Integer invoke(Arguments2<Integer, Integer> arguments) {
    						return arguments.getArgument1() + arguments.getArgument2();
    			}
    		});
    
    		System.out.println("number of 'element' word in text : " + count);
    
    	}
  • LDPDC
    Membre régulier
    Merci pour cet article complet mais je ne vois pas très bien l'intérêt de la solution proposée: y-a-t-il un cas d'utilisation réel emblématique qui permettrait de bien comprendre l'utilité concrète de cette implémentation?
  • Envoyé par LDPDC
    Merci pour cet article complet mais je ne vois pas très bien l'intérêt de la solution proposée: y-a-t-il un cas d'utilisation réel emblématique qui permettrait de bien comprendre l'utilité concrète de cette implémentation?
    Salut LDPDC,
    Déjà, cet article est un ensemble de réflexions sur le design pattern Function Object.
    Le cas concret emblématique c'est la manipulation des listes qui est présentée dans le chapitre map / filter / reduce
    Cette approche est complètement différente de l'approche impérative classique de java (et autres).

    Le but en soi du fonctionnel à ce niveau est de rendre plus concis et plus lisible le code pour gérer des listes (évidement, avec les générique imposés par java, comme ça, ça ne saute pas aux yeux ).
    Guava, bien qu'un petit peu plus limité au niveaux des définitions des foncteurs, propose une très bonne implémentation de ce que j'expose.

    Ce week-end, j'ai essayé le langage Scala qui est fondé sur une fusion fonctionnel / objet, on y retrouve tout ça et bien plus. J'y ai écrit les exemples de cet article, et là pour le coup c'est le code source est vraiment concis (moins de 5 lignes pour faire la moyenne des âges chiens mâles d'une liste d'individus).
    5 lignes également pour le parcours d'un csv.

    L'autre intérêt de tout ceci, c'est surtout de cacher l'implémentation des manipulations de listes à l'utilisateur. Ca permet, entre autre, de faire joujou avec les threads à son insu de façon à optimiser de façon transparent pour l'utilisateur. Un exemple en annexe illustre ceci.
  • Nico02
    Membre expérimenté
    Un article vraiment intéressant !

    J'ai bien aimé le coté "réflexion à haute voix". Ça apporte à mon sens une facilité de lecture et on comprends tout de suite dans quoi on met les pieds.

    En bref, je vais me laisser le temps de digérer tout ça, et je pense bien rajouter ce pattern dans ma boite à outils.

    Cdt.
  • Envoyé par Nico02
    Un article vraiment intéressant !

    J'ai bien aimé le coté "réflexion à haute voix". Ça apporte à mon sens une facilité de lecture et on comprends tout de suite dans quoi on met les pieds.

    En bref, je vais me laisser le temps de digérer tout ça, et je pense bien rajouter ce pattern dans ma boite à outils.

    Cdt.
    Oui foncteur est un pattern ultra utile quand on y a goûter.

    Après mon implémentation est très puriste sur les arguments, leur nombre et leurs types, mais la contrepartie c'est qu'elle oblige à créer énormément d'objet Arguments et c'est pas idéal pour les performances.
    Un interface de ce type est plus rapide :

    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    public interface Function1 <R, A1> {
      R invoke (A1 arg1);
    }
    
    public interface Function2 <R, A1, A2> {
      R invoke (A1 arg1, A2 arg2);
    }