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

Java Discussion :

Redondance de méthode dans des énumérations


Sujet :

Java

  1. #1
    Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2013
    Messages
    43
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Aisne (Picardie)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mai 2013
    Messages : 43
    Points : 42
    Points
    42
    Par défaut Redondance de méthode dans des énumérations
    Bonsoir,

    Je suis entrain de refactoriser un gros projet et un problème de redondance me casse la tête. Je m'en remets à vous pour me donner des idées.

    J'ai constaté que des méthodes sont présentes à l'identique dans plusieurs de mes énumérations, je voudrais logiquement les avoir en un seul exemplaire. Le problème est qu'il n'est pas possible de faire hériter une énumération et que les interfaces ne m'aident pas.

    Un exemple sera peut être plus parlant :

    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
    public enum A
    {
    	TRUC("coucou"),
    	MACHIN("hello");
     
    	private String label;
     
    	private A(String label)
    	{
    		this.label = label;
    	}
     
    	public static A parse(String str)
    	{
    		for (A e : A.values())
    		{
    			if (e.label.equalsIgnoreCase(str))
    				return e;
    		}
     
    		return null;
    	}
    }
    Et j'ai d'autres énumérations, disons B et C, qui fonctionnent avec le même principe de label avec la même fonction parse.

    J'ai essayer plusieurs approches en exploitant Enum<T> ou des interfaces mais je ne trouve pas de solution.

    Toutes les suggestions sont les bienvenues !

  2. #2
    Modérateur
    Avatar de joel.drigo
    Homme Profil pro
    Ingénieur R&D - Développeur Java
    Inscrit en
    Septembre 2009
    Messages
    12 430
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur R&D - Développeur Java
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2009
    Messages : 12 430
    Points : 29 131
    Points
    29 131
    Billets dans le blog
    2
    Par défaut
    Salut,

    Je ne comprends pas trop pourquoi assigner un label à une enum qui a elle-même déjà un nom (et pour lequel on pourrait mettre en place cette méthode parse pour avoir un équivalent non sensible à la casse de valueOf(), mais quoiqu'il en soit tu peux faire comme ça :

    1. Déclarer une interface pour l'accès commun au label :
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      public interface ILabelable {
       
      	String getLabel();
       
      }
    2. Déclarer ton enum pour qu'elle implémente l'interface :
      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
      public enum MyEnum implements ILabelable {
       
      	VALUE1("label1"),
      	VALUE2("label2"),
      	;
       
      	private final String label;
      	private MyEnum(String label) {
      		this.label=label;
      	}
       
      	@Override
      	public String getLabel() {
      		return label;
      	}
       
      }
    3. Déclarer dans une classe utilitaire la méthode de parsing (avec l'exemple d'usage joint) :
      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
      public class Utils {
       
      	public static <T extends Enum<T>&ILabelable> T parse(Class<T> enumClass, String label) {
       
      		for(T value : enumClass.getEnumConstants()){
      			if ( value.getLabel().equalsIgnoreCase(label) ) {
      				return value;
      			}
      		}
      		throw new IllegalArgumentException();
       
      	}
       
      	// test-déom d'usage
      	public static void main(String[] args) {
       
      		System.out.println(Utils.parse(MyEnum.class, "LaBeL1"));
       
      		MyEnum value = Utils.parse(MyEnum.class, "Label2");
       
      	}	
       
      }
    L'expression "ça marche pas" ne veut rien dire. Indiquez l'erreur, et/ou les comportements attendus et obtenus, et donnez un Exemple Complet Minimal qui permet de reproduire le problème.
    La plupart des réponses à vos questions sont déjà dans les FAQs ou les Tutoriels, ou peut-être dans une autre discussion : utilisez la recherche interne.
    Des questions sur Java : consultez le Forum Java. Des questions sur l'EDI Eclipse ou la plateforme Eclipse RCP : consultez le Forum Eclipse.
    Une question correctement posée et rédigée et vous aurez plus de chances de réponses adaptées et rapides.
    N'oubliez pas de mettre vos extraits de code entre balises CODE (Voir Mode d'emploi de l'éditeur de messages).
    Nouveau sur le forum ? Consultez Les Règles du Club.

  3. #3
    Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2013
    Messages
    43
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Aisne (Picardie)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mai 2013
    Messages : 43
    Points : 42
    Points
    42
    Par défaut
    Merci c'est exactement ce que je voulais.
    J'avais pensé à cette solution mais je n'avais réussi à implémenté la méthode parse générique, la gestion des types est vraiment tordue dans ce genre de cas.

    Pour ce qui est du pourquoi, j'en suis arrivé à faire cette méthode parse car je veux être capable de gérer le cas suivant :

    J'ai plusieurs modes d'exécution pour mon application (par exemple : lente, normale rapide) qui changent le comportement de celle-ci. Et je veux pouvoir lancer mon application de cette façon "java -jar appli.jar lente". Au lancement je dois bien convertir la chaîne de caractère en élément de mon énumération. Dans la même idée je veux aussi récupérer des informations depuis une base de données, le problème est le même.
    Mon approche du problème est peut être critiquable, si c'est le cas je veux bien que tu me dises comment tu aurais fait.

  4. #4
    Modérateur
    Avatar de joel.drigo
    Homme Profil pro
    Ingénieur R&D - Développeur Java
    Inscrit en
    Septembre 2009
    Messages
    12 430
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur R&D - Développeur Java
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2009
    Messages : 12 430
    Points : 29 131
    Points
    29 131
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par Opsse Voir le message
    Dans la même idée je veux aussi récupérer des informations depuis une base de données, le problème est le même.
    Mon approche du problème est peut être critiquable, si c'est le cas je veux bien que tu me dises comment tu aurais fait.
    Dans ce cas, oui, il est nécessaire d'associer un nom différent de celui de la constante d'enumération. Mais attention à la problématique de l'instanciation : il faudra toujours faire appel à l'enum forcément après la connexion à la base, (sans parler des problèmes si la connexion à la base plante), donc c'est assez hasardeux. J'aurais simplement fait une fabrique. Dans ta base tu stockes ton label et le nom de la constante dans le même record (ou un système de double codes pour pouvoir faire évoluer l'enum sans toucher à la base) et tu fais une fabrique :
    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
    public class EnumFactory<T extends Enum<T>> {
     
    	private final Map<String, T> map;
     
    	public EnumFactory(Class<T> enumClass, String table, Connection connection) throws SQLException {
    		map = Collections.unmodifiableMap(loadMap(enumClass, table, connection));
    	}
     
    	private static <T extends Enum<T>> Map<String, T> loadMap(Class<T> enumClass, String table, Connection connection) throws SQLException {
    		final Map<String, T> map = new HashMap<>();
    		try(ResultSet set = retrieve(connection, table) ) {
                            while( set.next() ) {
    			   final String label = set.getString("label").toLowerCase();
    			   final String name = set.getString("code");
    			   for(T value : enumClass.getEnumConstants()) {
    				if ( value.name().equalsIgnoreCase(name) ) {
    					map.put(label, value);
    					break;
    				}
    			   }
                            }
    		}
    		return map;
    	}
     
    	private static ResultSet retrieve(Connection connection, String table) throws SQLException {
    		/*...*/
    	}
     
    	public T parse(String label) {
    		label = label.toLowerCase(); // pour l'insensibilité à la casse
    		if ( map.containsKey(label) ) {
    			return map.get(label);
    		}
    		throw new IllegalArgumentException();
    	}
     
    }


    Citation Envoyé par Opsse Voir le message
    Et je veux pouvoir lancer mon application de cette façon "java -jar appli.jar lente". Au lancement je dois bien convertir la chaîne de caractère en élément de mon énumération. Dans la même idée je veux aussi récupérer des informations depuis une base de données, le problème est le même.
    Tu peux procéder également de la même façon, en préparant une map avec tes valeurs (en particulier si elles sont dynamiques), ou en déclarant des variables statiques. Si tes variables sont statiques, on peut également faire 2 enum :

    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
    public enum Enum1 {
     
    	VAL1,
    	VAL2;
     
    	private enum DecodeEnum {
     
    		LABEL1(VAL1),
    		LABEL2(VAL2),
    		;
     
    		private final Enum1 val;
    		private DecodeEnum(Enum1 val) {
    			this.val=val;
    		}
     
    	}
     
            // cette méthode peut être aussi rendue générique (et placer dans une classe utilitaire), avec un peu de réflexion pour aller chercher la DecodeEnum
    	public static Enum1 decode(String label ) {
    		return DecodeEnum.valueOf(label.toUpperCase()).val;
    	}
     
    }
    NB la méthode avec la map peut être appliquée également dans l'exemple de mon précédent post, pour avoir un cache, si beaucoup de constantes d'enum
    L'expression "ça marche pas" ne veut rien dire. Indiquez l'erreur, et/ou les comportements attendus et obtenus, et donnez un Exemple Complet Minimal qui permet de reproduire le problème.
    La plupart des réponses à vos questions sont déjà dans les FAQs ou les Tutoriels, ou peut-être dans une autre discussion : utilisez la recherche interne.
    Des questions sur Java : consultez le Forum Java. Des questions sur l'EDI Eclipse ou la plateforme Eclipse RCP : consultez le Forum Eclipse.
    Une question correctement posée et rédigée et vous aurez plus de chances de réponses adaptées et rapides.
    N'oubliez pas de mettre vos extraits de code entre balises CODE (Voir Mode d'emploi de l'éditeur de messages).
    Nouveau sur le forum ? Consultez Les Règles du Club.

  5. #5
    Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2013
    Messages
    43
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Aisne (Picardie)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mai 2013
    Messages : 43
    Points : 42
    Points
    42
    Par défaut
    Je ne suis pas très à l'aise avec les fabriques, il faudrait que je prenne le temps de regarder ca.

    J'ai vu que la classe Enum avait la méthode name(), je pourrais utiliser ca plutôt que d'ajouter les labels. Penses tu que c'est une bonne idée ?

  6. #6
    Modérateur
    Avatar de joel.drigo
    Homme Profil pro
    Ingénieur R&D - Développeur Java
    Inscrit en
    Septembre 2009
    Messages
    12 430
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur R&D - Développeur Java
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2009
    Messages : 12 430
    Points : 29 131
    Points
    29 131
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par Opsse Voir le message
    Je ne suis pas très à l'aise avec les fabriques, il faudrait que je prenne le temps de regarder ca.
    Il n'y a rien de spécialement complexe à ce sujet.

    Citation Envoyé par Opsse Voir le message
    J'ai vu que la classe Enum avait la méthode name(), je pourrais utiliser ca plutôt que d'ajouter les labels. Penses tu que c'est une bonne idée ?
    C'était bien mon premier conseil, pour faire quelque chose comme ça :
    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
    public enum Exemple {
     
    	VAL1,
    	VAL2,
    	;
     
    	public static Exemple of(String val) {
    		for(Exemple value : values()) {
    			if ( value.name().equalsIgnoreCase(val) ) {
    				return value;
    			}
    		}
    		throw new NoSuchElementException();
    	}
     
    }
    Ou dans une classe utilitaire, avec une méthode plus générique :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    public static <T extends Enum<T>> T of(Class<T> enumClass, String val) {
    		for(T value : enumClass.getEnumConstants()) {
    			if ( value.name().equalsIgnoreCase(val) ) {
    				return value;
    			}
    		}
    		throw new NoSuchElementException();
    	}
    Attention, si ton idée est de redéfinir name() pour que ça renvoit autre chose que le nom (ce qui serait une mauvaise idée de toute manière), tu ne pourras pas : elle est final.

    Autre solution, sinon, la réflexion :

    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
    public enum Exemple {
     
    	VAL1,
    	VAL2,
    	;
     
    	public static final Exemple LABEL1 = VAL1;
    	public static final Exemple LABEL2 = VAL2;
     
    	public static Exemple of(String val) {
    		for(Field field : Exemple.class.getFields()) {
    			if ( Modifier.isStatic(field.getModifiers()) && field.getType()==Exemple.class && field.getName().equalsIgnoreCase(val) ) {
    				try {
    					return (Exemple)field.get(null);
    				} catch (IllegalAccessException e) {
    					throw new IllegalStateException(e);
    				}
    			}
    		}
    		throw new NoSuchElementException();
    	}
     
    	public static void main(String[] args) {
     
    		System.out.println(Exemple.of("label1"));
    		System.out.println(Exemple.of("val1"));
     
    	}
     
    }
    La méthode peut être sortie et rendue générique de la même manière que l'autre cas (et on pourrait même la faire fonctionner pour autre chose qu'une enum).
    L'expression "ça marche pas" ne veut rien dire. Indiquez l'erreur, et/ou les comportements attendus et obtenus, et donnez un Exemple Complet Minimal qui permet de reproduire le problème.
    La plupart des réponses à vos questions sont déjà dans les FAQs ou les Tutoriels, ou peut-être dans une autre discussion : utilisez la recherche interne.
    Des questions sur Java : consultez le Forum Java. Des questions sur l'EDI Eclipse ou la plateforme Eclipse RCP : consultez le Forum Eclipse.
    Une question correctement posée et rédigée et vous aurez plus de chances de réponses adaptées et rapides.
    N'oubliez pas de mettre vos extraits de code entre balises CODE (Voir Mode d'emploi de l'éditeur de messages).
    Nouveau sur le forum ? Consultez Les Règles du Club.

  7. #7
    Expert éminent sénior
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Points : 23 190
    Points
    23 190
    Billets dans le blog
    1
    Par défaut
    Salut,

    Citation Envoyé par Opsse Voir le message
    J'ai vu que la classe Enum avait la méthode name(), je pourrais utiliser ca plutôt que d'ajouter les labels. Penses tu que c'est une bonne idée ?
    Heu... Dans ce cas tout est déjà fait automatiquement :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    public enum Exemple {
     
    	VAL1,
    	VAL2;
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Exemple exemple = Exemple.valueOf("VAL1"); // Exemple.VAL1

    a++

  8. #8
    Modérateur
    Avatar de joel.drigo
    Homme Profil pro
    Ingénieur R&D - Développeur Java
    Inscrit en
    Septembre 2009
    Messages
    12 430
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur R&D - Développeur Java
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2009
    Messages : 12 430
    Points : 29 131
    Points
    29 131
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par adiGuba Voir le message
    Heu... Dans ce cas tout est déjà fait automatiquement :
    C'est sensible à la casse, alors que dans le premier code de @Opsse, il utilise equalsIgnoreCase, d'où la supposition de ma part qu'il cherche quelque chose de non sensible à la casse. Bien sûr, si la convention est sure d'être respectée, on pourrait tout simplement faire Exemple.valueOf( machin.toUpperCase() ).
    L'expression "ça marche pas" ne veut rien dire. Indiquez l'erreur, et/ou les comportements attendus et obtenus, et donnez un Exemple Complet Minimal qui permet de reproduire le problème.
    La plupart des réponses à vos questions sont déjà dans les FAQs ou les Tutoriels, ou peut-être dans une autre discussion : utilisez la recherche interne.
    Des questions sur Java : consultez le Forum Java. Des questions sur l'EDI Eclipse ou la plateforme Eclipse RCP : consultez le Forum Eclipse.
    Une question correctement posée et rédigée et vous aurez plus de chances de réponses adaptées et rapides.
    N'oubliez pas de mettre vos extraits de code entre balises CODE (Voir Mode d'emploi de l'éditeur de messages).
    Nouveau sur le forum ? Consultez Les Règles du Club.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Eviter la redondance dans des fichiers
    Par Baptiste Wicht dans le forum ANT
    Réponses: 2
    Dernier message: 05/06/2009, 14h39
  2. Utilisation des interfaces dans des méthodes
    Par kyrilkarlier dans le forum Windows Forms
    Réponses: 7
    Dernier message: 26/05/2009, 14h29
  3. Récupération des résultats des méthodes dans un service WCF
    Par est09 dans le forum Windows Communication Foundation
    Réponses: 16
    Dernier message: 02/04/2009, 16h44
  4. [JAR] arguments des méthodes dans un JAR
    Par Satch dans le forum Eclipse Java
    Réponses: 2
    Dernier message: 20/11/2007, 22h18
  5. Réponses: 1
    Dernier message: 10/05/2006, 19h45

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