IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage Java Discussion :

Pseudo paramètres nommés


Sujet :

Langage Java

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre éprouvé
    Profil pro
    Inscrit en
    Mars 2006
    Messages
    90
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2006
    Messages : 90
    Par défaut Pseudo paramètres nommés
    Un étudiant a écrit une classe de nombre complexe permettant de simuler des paramètres nommés lors de l'appel du constructeur.

    Le code du fichier example/Test.java contenant main() est (nécessite JDK >= 5) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    package example;
     
    import static example.Complex.*;
    import static example.Complex.Angle.Unit.*;
     
    public final class Test {
     
    	public static final void main(final String[] args) {
    		Complex c = null;
     
    		c = new Complex(x(3), y(4));
     
    		System.out.println(c);
    		assert(c.getX()     == 3);
    		assert(c.getY()     == 4);
    		assert(c.getAngle() == Math.atan2(4, 3));
    		assert(c.getR()     == 5);
     
    		c = new Complex(y(3), x(4));
     
    		System.out.println(c);
    		assert(c.getX()     == 4);
    		assert(c.getY()     == 3);
    		assert(c.getAngle() == Math.atan2(3, 4));
    		assert(c.getR()     == 5);
     
    		c = new Complex(r(10), angle(60, DEG));
     
    		System.out.println(c);
    		assert(c.getX()     == 10 * Math.cos(Math.toRadians(60)));
    		assert(c.getY()     == 10 * Math.sin(Math.toRadians(60)));
    		assert(c.getAngle() == Math.toRadians(60));
    		assert(c.getR()     == 10);
     
    		try {
    			System.out.println(new Complex(x(10), angle(45, DEG)));
    			assert(false);
    		} catch (final Complex.ParameterException e) {
    			System.out.println(e);
    		}
    	}
     
    }
    Le code du fichier example/Complex.java est (nécessite JDK >= 5) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    package example;
     
    public class Complex {
     
    	public static interface IParameter {
     
    	}
     
    	public static final class X implements IParameter {
    		private final double value;
     
    		private X(final double value) {
    			this.value = value;
    		}
    	}
     
    	public static final X x(final double value) {
    		return new X(value);
    	}
     
    	public static final class Y implements IParameter {
    		private final double value;
     
    		private Y(final double value) {
    			this.value = value;
    		}
    	}
     
    	public static final Y y(final double value) {
    		return new Y(value);
    	}
     
    	public static final class Angle implements IParameter {
    		public static enum Unit { DEG, RAD; };
     
    		private final double value;
     
    		private Angle(final double value, final Unit unit) {
    			switch (unit) {
    			case DEG:
    				this.value = Math.toRadians(value);
    				break;
    			case RAD:
    				this.value = value;
    				break;
    			default:
    				throw new IllegalArgumentException(unit.toString());
    			}
    		}
    	}
     
    	public static final Angle angle(final double value, final Angle.Unit unit) {
    		return new Angle(value, unit);
    	}
     
    	public static final class R implements IParameter {
    		private final double value;
     
    		private R(final double value) {
    			this.value = value;
    		}
    	}
     
    	public static final R r(final double value) {
    		return new R(value);
    	}
     
    	@SuppressWarnings("serial")
    	public final class ParameterException extends RuntimeException {
     
    		public ParameterException(final String message) {
    			super(message);
    		}
     
    	}
     
    	private final double x, y;
     
    	/**
             * The parameters must be created with two of { <code>Complex.x(double)</code>, <code>Complex.y(double)</code>, <code>Complex.angle(double, Complex.Angle.Unit)</code>, <code>Complex.r(double)</code> }.<br>
             * Example : <code>new Complex(Complex.y(33), Complex.x(42));</code>.<br>
             * @param parameters set of exactly 2 parameters: (x and y) or (angle and r) ; the order doesn't matter.<br>
             * @throws ParameterException if:<ul>
             *      <li>parameters.length != 2;</li>
             *      <li>there is a duplicate parameter;</li>
             *      <li>there is not enough information to construct the object (missing parameters).</li>
             * </ul>
             */
    	public Complex(final IParameter... parameters) {
    		if (parameters.length != 2) throw new ParameterException("Exactly 2 parameters needed");
     
    		Double x = null;
    		Double y = null;
    		Double angle = null;
    		Double r = null;
     
    		for (final IParameter parameter : parameters) {
    			if (parameter instanceof X) {
    				if (x != null) throw new ParameterException("Duplicate parameter: x");
    				x = ((X)parameter).value;
    			}
    			if (parameter instanceof Y) {
    				if (y != null) throw new ParameterException("Duplicate parameter: y");
    				y = ((Y)parameter).value;
    			}
    			if (parameter instanceof Angle) {
    				if (angle != null) throw new ParameterException("Duplicate parameter: angle");
    				angle = ((Angle)parameter).value;
    			}
    			if (parameter instanceof R) {
    				if (r != null) throw new ParameterException("Duplicate parameter: r");
    				r = ((R)parameter).value;
    			}
    		}
     
    		if (x == null) {
    			if (angle == null) throw new ParameterException("Missing parameter: angle");
    			if (r     == null) throw new ParameterException("Missing parameter: r");
    			x = r * Math.cos(angle);
    		}
    		if (y == null) {
    			if (angle == null) throw new ParameterException("Missing parameter: angle");
    			if (r     == null) throw new ParameterException("Missing parameter: r");
    			y = r * Math.sin(angle);
    		}
     
    		this.x = x;
    		this.y = y;
    	}
     
    	public final double getX() {
    		return this.x;
    	}
     
    	public final double getY() {
    		return this.y;
    	}
     
    	/**
             * @return angle in radians.<br>
             */
    	public final double getAngle() {
    		return Math.atan2(this.y, this.x);
    	}
     
    	public final double getR() {
    		return Math.sqrt(this.x * this.x + this.y * this.y);
    	}
     
    	@Override
    	public final String toString() {
    		return "(x: " + this.getX() + "; y: " + this.getY() + "; angle: " + this.getAngle() + " rad; r: " + this.getR() + ")";
    	}
     
    }
    Je suis perplexe quant à cette façon de simuler des paramètres nommés, et je souhaiterais avoir l'avis d'autres programmeurs.

  2. #2
    Expert confirmé
    Avatar de djo.mos
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    4 666
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2004
    Messages : 4 666
    Par défaut
    Salut,
    Pas mal du tout
    Mais un peu lourd à implémenter quand même ...
    Quelques remarques:
    - ParameterException : pourquoi ne pas avoir utilisé IllegalArgumentException plutôt ?
    -
    public Complex(final IParameter... parameters) {
    if (parameters.length != 2) throw new ParameterException("Exactly 2 parameters needed");
    Euh ... ce ne serait pas beaucoup plus simple de faire:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    public Complex(final IParameter parameter1, final IParameter parameter2) {
    	:
    - Et je me répètes, même si c'est simple à l'emploi, c'est difficile et délicat à implémenter. Mais ça reste intéressant nénanmmoins

    Perso, j'ai utilisé un wrapper (avec le style fluent) autour d'un Map pour répondre à un problème pareil.

  3. #3
    Membre éprouvé
    Profil pro
    Inscrit en
    Mars 2006
    Messages
    90
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2006
    Messages : 90
    Par défaut
    @djo.mos : je suis d'accord sur tous les points.

    L'utilisation d'une ellipse dans le constructeur dans ce cas est discutable, mais dans le cas général cela permettrait d'avoir une meilleure flexibilité (pour un nombre de paramètres vraiment variable).

    Pour alléger un peu le constructeur il faudrait en faire plusieurs prenant des paramètres bien définis (ce qui n'est pas possible dans cet exemple à cause du nombre et du type des paramètres requis), ou bien faire des sous-fonctions.

    Cela reste effectivement très lourd, mais le code client y gagnerait en clarté pour des gros constructeurs (ceci dit, moi et les gros constructeurs ).

    Un autre aspect notable de cette technique est la nécessité d'avoir une documentation bien détaillée (cela peut être vu par un inconvénient par ceux qui estiment que les interfaces devraient être "intuitives").

    Citation Envoyé par djo.mos
    Perso, j'ai utilisé un wrapper (avec le style fluent) autour d'un Map pour répondre à un problème pareil.
    Pourrais-tu détailler stp ?

  4. #4
    Expert confirmé
    Avatar de djo.mos
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    4 666
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2004
    Messages : 4 666
    Par défaut
    Citation Envoyé par boromir73 Voir le message
    Pourrais-tu détailler stp ?
    J'avais eu besoin d'un truc pareil pour simplifier l'appel d'une méthode select d'un DAO qui devait être extrêmement flexible, dans la mesure ou elle devrait permettre de préciser :
    - la partie select (*, id, etc.)
    - la partie from
    - la partie where
    - group by
    - having,
    - etc.

    tout en restant simple à utiliser.
    l'overload me branchait pas tellement (6 ou 7 variantes d'une méme méthode ... non, merci).
    donc j'ai procédé comme suit:
    - implémenté select pour qu'elle prenne en paramètre un Map<String, String>
    - crée un MapBuilder

    Le tout s'utilise de cett façon:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    dao.select(new QueryBuilder().select("*").from("table1, table2").where("id>? and age=?").toMap());
    Le QueryBuilder est tout simple:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
     
    public class QueryBuilder {
      private Map<String, String> queryFragments = new HashMap()<..>;
     
      public QueryBuilder select(String select){
        queryFragments.put(Dao.SELECT_FRAGMENT, select);
        return this;
      }
     
      public QueryBuilder from(String from){
        queryFragments.put(Dao.FROM_FRAGMENT, from);
        return this;
      }
      :
      :
      public Map<String, String> toMap(){
         return queryFragments;
      }
     
    }
    et dans le DAO:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    public List<T> select(Map<String, String> queryFragments){
      String select = (queryFragments.get(Dao.SELECT.FRAGMENT)!=null? queryFragments.get(Dao.SELECT.FRAGMENT) : "*";
     
      :
      :
      :
    }
    Voilou !
    Le truc est d'utiliser des valeurs par défaut pour quelques paramètres (comme utiliser * pour le select si l'utilisateur ne spécifie rien pouyr le select, ou une table par défaut, etc.)

  5. #5
    Membre Expert
    Avatar de gifffftane
    Profil pro
    Inscrit en
    Février 2007
    Messages
    2 354
    Détails du profil
    Informations personnelles :
    Localisation : France, Loire (Rhône Alpes)

    Informations forums :
    Inscription : Février 2007
    Messages : 2 354
    Par défaut
    Moi je ne comprends pas à quoi servent ces paramètres nommés, et je n'ai jamais eu à utiliser quelque chose qui ressemble à ça.

    Dans l'hypothèse où cela serve tout de même à quelque chose quelque part, la solution de l'étudiant me semble correcte. Comme beaucoup des paramètres semblent être des double, on pourrait remplacer les X et Y par des énumérations, mais cela ne marcherait pas pour Angle. Alors l'étudiant à probablement fait le choix qui marche dans le plus grand nombre de cas.

    Peut être aussi serait-il intéressant d'utiliser un langage de script intégré au java ; il me semble qu'on peut faire ça assez directement en javascript et comme c'est dans le jre directement cela pourrait être une piste. Mais j'imagine que c'était sortir du contexte de l'exercice ?

  6. #6
    Expert confirmé
    Avatar de djo.mos
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    4 666
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2004
    Messages : 4 666
    Par défaut
    Lémulation des paramètres nommés est très utile lorsque t'as vraiment beaucoup de paramètres à passer à une méthoed, donc plusieurs sont optionnels.
    Dans l'exemple que j'ai donné, originellement, j'avais:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    public List<T> select(String select, String from, String where, String groupBy, String having, String orderBy, Object ... params);
    qui sont tous optionnels.
    Pur fair un simple orderBy dans ma requête, je dois faire:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    select(null, null, null null, null, "id asc");
    etc.
    Ce qui st vraiment dûr à maintenir.
    Pire encore si on essaie de faire de l'overload:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    public List<T> select(String select);
     
    public List<T> select(String select, String from);
     
    public List<T> select(String select, String from, String where, Object ... params);
     
    public List<T> select(String select, String from, String where, String groupBy, Object ... params);
    etc.
    etc. pour couvrir tous les cas d'utilisations eventuels.

Discussions similaires

  1. Paramètres nommés en Java
    Par Atatorus dans le forum Langage
    Réponses: 7
    Dernier message: 02/11/2010, 10h35
  2. fonction et paramètres nommés
    Par Papy214 dans le forum VBScript
    Réponses: 1
    Dernier message: 20/08/2010, 09h06
  3. délégué nommé à 3 paramètres
    Par SCAMARK dans le forum C#
    Réponses: 4
    Dernier message: 11/03/2009, 10h25
  4. Paramêtres nommés et Session.find
    Par Kyuden dans le forum Hibernate
    Réponses: 2
    Dernier message: 04/05/2007, 16h34

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo