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

avec Java Discussion :

Implémentation d'une classe Input et utilisation du strategy pattern


Sujet :

avec Java

  1. #1
    Membre à l'essai
    Homme Profil pro
    Lycéen
    Inscrit en
    Avril 2014
    Messages
    11
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Lycéen

    Informations forums :
    Inscription : Avril 2014
    Messages : 11
    Points : 17
    Points
    17
    Par défaut Implémentation d'une classe Input et utilisation du strategy pattern
    Bonjour,


    Tout d'abord, je tiens à vous informer que je suis un débutant complet en programmation Java. Je lis actuellement Programmer en Java de Claude Delannoy. Seulement voila. Dès le début nous apprenons qu'il n'y a pas de classe qui nous permet de récupérer directement une entrée utilisateur en un seul appel de fonction méthode comme souhaité. L'auteur de l'ouvrage nous propose donc dans son annexe une petite classe Clavier. Cependant, j'ai quelques petits soucis avec celle-ci

    Donc la voici :

    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
     
    // classe fournissant des fonctions de lecture au clavier
    import java.io.* ;
     
    public class Clavier
    { public static String lireString () // lecture d’une chaine
      { String ligne_lue = null ;
        try
        { InputStreamReader lecteur = new InputStreamReader (System.in) ;
          BufferedReader entree = new BufferedReader (lecteur) ;
          ligne_lue = entree.readLine() ;
        }
        catch (IOException err)
        { System.exit(0) ;
        }
        return ligne_lue ;
      }
     
      public static float lireFloat () // lecture d’un float
      { float x=0 ; // valeur a lire
        try
        { String ligne_lue = lireString() ;
          x = Float.parseFloat(ligne_lue) ;
        }
        catch (NumberFormatException err)
        { System.out.println ("*** Erreur de donnee ***") ;
          System.exit(0) ;
        }
        return x ;
      }
     
      public static double lireDouble () // lecture d’un double
      { double x=0 ; // valeur a lire
        try
        { String ligne_lue = lireString() ;
          x = Double.parseDouble(ligne_lue) ;
        }
        catch (NumberFormatException err)
        { System.out.println ("*** Erreur de donnee ***") ;
          System.exit(0) ;
        }
        return x ;
      }
     
      public static int lireInt () // lecture d’un int
      { int n=0 ; // valeur a lire
        try
        { String ligne_lue = lireString() ;
          n = Integer.parseInt(ligne_lue) ;
        }
        catch (NumberFormatException err)
        { System.out.println ("*** Erreur de donnee ***") ;
          System.exit(0) ;
        }
        return n ;
      }
    }

    Le problème ne vient pas du style l’indentation, que je trouve au passage... laid, horrible et illisible (enfin... chacun ses goûts ), il ne s'agit non plus d'un problème de compréhension.

    Citation Envoyé par un senior en programmation très patient qui aime aider de jeunes débutants
    Il est où alors ton problème, Ô pauvre débutant que tu es! Tu nous fait perdre notre temps, au revoir!
    Non, ne partez pas! Mon problème se trouve dans l’implémentation de cette classe.
    Ne dit-on pas qu'un bon développeur est un développeur bête et méchant fainéant ? (Le développeur et ses milles et une qualités ) Enfin, passons... Ici, les méthodes qui retournent des nombres sont quasiment identiques. Et je n'aime pas réécrire mon code (oui parce que réinventer la roue, y'a pas de problème ) Après quelques recherches je suis tombé sur une manière spécifique de concevoir une classe et qui au passage me permet de refactoriser mes méthodes, youpi : le strategy pattern. Donc, voici comment j'ai remanié tant bien que mal cette petite classe de rien du tout :

    main.Application
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    package main;
     
    import com.tools.input.*;
     
     
    public class Application
    {
            public static void main(String args[])
            {
                    System.out.println( Input.readNumber(new ReturnInt()) );
            }
    }

    com.tools.input.Input
    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
     
    package com.tools.input;
     
    import java.io.*;
     
     
    public class Input
    {
            public static String readString()
            {
                    String stringInput = null;
     
                    try
                    {
                            InputStreamReader inputReader = new InputStreamReader(System.in);
                            BufferedReader inputBuffer = new BufferedReader(inputReader);
                            stringInput = inputBuffer.readLine();
                    }
                    catch (IOException err)
                    {
                            System.err.println("An error occurred: " + err.getMessage());
                            System.exit(0);
                    }
     
                    return stringInput;
            }
     
            public static Number readNumber(InputReturnType returnType)
            {
                    Number numberInput = 0;
     
                    try
                    {
                            String str = Input.readString();
                            numberInput = returnType.parse(str);
                    }
                    catch (NumberFormatException err)
                    {
                            System.err.println("An error occurred: " + err.getMessage());
                            System.exit(0);
                    }
     
                    return numberInput;
            }
    }

    com.tools.input.InputReturnType
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    package com.tools.input;
     
     
    interface InputReturnType
    {
            public Number parse(String str);
    }

    com.tools.input.returnInt
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    package com.tools.input;
     
     
    public class ReturnInt implements InputReturnType
    {
            @Override
            public Number parse(String str) {
                    return Integer.parseInt(str);
            }
    }

    com.tools.input.returnDouble
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    package com.tools.input;
     
     
    public class ReturnDouble implements InputReturnType
    {
            @Override
            public Number parse(String str) {
                    return Double.parseDouble(str); 
            }
    }

    com.tools.input.returnFloat
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    package com.tools.input;
     
     
    public class ReturnFloat implements InputReturnType
    {
            @Override
            public Number parse(String str) {
                    return Float.parseFloat(str);
            }
    }
    Courage, c'est presque fini. Voici donc mes questions sans plus tarder :

    1. Est-ce que mon implémentation est mieux que l'originale?
    2. Ai-je bien utilisé le strategy pattern dans ce cas-ci?
    3. Je sais qu'il n'existe aucune implémentation parfaite mais est-ce qu'il y aurait un moyen d'optimiser celle que je vous propose en terme de performance ou de lisibilité? (peut-être en utilisant des Templates?)


    Cordialement,

    ~ Kairos

  2. #2
    Expert éminent sénior
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 481
    Points : 48 806
    Points
    48 806
    Par défaut
    Citation Envoyé par Kairos Voir le message
    Bonjour,


    Tout d'abord, je tiens à vous informer que je suis un débutant complet en programmation Java. Je lis actuellement Programmer en Java de Claude Delannoy. Seulement voila. Dès le début nous apprenons qu'il n'y a pas de classe qui nous permet de récupérer directement une entrée utilisateur en un seul appel de fonction méthode comme souhaité.
    Il est vrai que la classe Scanner n'existe que depuis bientôt 10 ans, je peux comprendre qu'il ne l'aie pas remarquée....
    Citation Envoyé par Kairos Voir le message
    L'auteur de l'ouvrage nous propose donc dans son annexe une petite classe Clavier. Cependant, j'ai quelques petits soucis avec celle-ci
    Tu m'étonne: indentation pourrie, inutile vu l’existence de la classe Scanner et, surtout, sujette à causer des erreurs: il crée un nouveau bufferedReader à chaque appel plutôt que de conserver le précédent => On peut perdre des données chargées par le buffer précédent mais pas encore lues: à ne pas faire. Brule ce livre avant d'y prendre de mauvaises habitudes.

    Citation Envoyé par Kairos Voir le message
    Ne dit-on pas qu'un bon développeur est un développeur bête et méchant fainéant ? (Le développeur et ses milles et une qualités ) Enfin, passons... Ici, les méthodes qui retournent des nombres sont quasiment identiques. Et je n'aime pas réécrire mon code (oui parce que réinventer la roue, y'a pas de problème ) Après quelques recherches je suis tombé sur une manière spécifique de concevoir une classe et qui au passage me permet de refactoriser mes méthodes, youpi : le strategy pattern. Donc, voici comment j'ai remanié tant bien que mal cette petite classe de rien du tout :
    Sur le principe, tu as bien travaillé, bien que je pense que ce soit de l'overkill dans ton cas


    Citation Envoyé par Kairos Voir le message
    1. Est-ce que mon implémentation est mieux que l'originale?
    On peut difficilement faire pire que l'originale En tout cas tu y a bien appliqué différents principes de la POO. Fais juste attention à bien mettre toujours en balance le design (que tu effectue correctement) et son utilité. sinon tu va nous concevoir de magnifiques architectures multimodulaires pour faire un truc qui pouvait se coder en trois lignes. C'est aussi mal que de tout mettre en flat avec du copier coller de code. Ici, tu aurais pu juste utiliser la classe Scanner.nextXXXX()

    Citation Envoyé par Kairos Voir le message
    3. Je sais qu'il n'existe aucune implémentation parfaite mais est-ce qu'il y aurait un moyen d'optimiser celle que je vous propose en terme de performance ou de lisibilité? (peut-être en utilisant des Templates?)
    Comme ceci: (au passage tu avais oublié le public sur l'interface )
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    interface InputReturnType<X extends Number>
    {
            public X parse(String str);
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    public class ReturnFloat implements InputReturnType<Float>
    {
            @Override
            public Float parse(String str) {
                    return Float.parseFloat(str);
            }
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public <X extends Number> X readNumber(InputReturnType<X> returnType)
            {
                    try
                    {
                            String str = Input.readString();
                            return returnType.parse(str);
                    }
                    catch (NumberFormatException err)
                    {
                            System.err.println("An error occurred: " + err.getMessage());
                    }
     
                    return null;
            }
    Dernière remarque: des System.exit() dans le parseur, jamais !!!!

  3. #3
    Expert éminent sénior
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 481
    Points : 48 806
    Points
    48 806
    Par défaut
    Tant que j'y pense, on peut faire plus propre avec des lambda

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    	  public Float readFloat() {
    		  return readNumber(it -> Float.parseFloat(it));
    	  }
    	  public Double readDouble() {
    		  return readNumber(it -> Double.parseDouble(it));
    	  }
    et plus besoin de classe ReturnFloat etc.

  4. #4
    Membre à l'essai
    Homme Profil pro
    Lycéen
    Inscrit en
    Avril 2014
    Messages
    11
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Lycéen

    Informations forums :
    Inscription : Avril 2014
    Messages : 11
    Points : 17
    Points
    17
    Par défaut
    Citation Envoyé par tchize_
    Brule ce livre avant d'y prendre de mauvaises habitudes.
    Ça marche! D'ailleurs, je reste assez septique quant aux livres d'auteurs qui font des ouvrages sur plusieurs langages. Claude Delannoy nous propose une joyeuse panoplie de livres pour apprendre le C, le C++ et le Java. Tant de gentillesse ce Claude . Après, il est possible qu'il explique bien la syntaxe du langage en général... Le comble de l'ironie, ce livre est proposé dans la catégorie Livres du site. Moi bête et méchant je m'y intéresse! J'ai eu raison d'un peu trop m'y intéresser au final

    Citation Envoyé par tchize_
    Il est vrai que la classe Scanner n'existe que depuis bientôt 10 ans, je peux comprendre qu'il ne l'aie pas remarquée....
    La 9ème édition sur Java 8 est sortie le mois dernier Après, je peux comprendre qu'il ne revoit pas chaque morceau de code de son livre... Il n'a peut-être pas le temps, il bosse sans doute sur un livre en C# .

    Citation Envoyé par tchize_
    [...] bien que je pense que ce soit de l'overkill dans ton cas.
    C'est tout moi! Je suis peut-être un peu trop perfectionniste

    Citation Envoyé par tchize_
    Citation Envoyé par Kairos
    3. Je sais qu'il n'existe aucune implémentation parfaite mais est-ce qu'il y aurait un moyen d'optimiser celle que je vous propose en terme de performance ou de lisibilité? (peut-être en utilisant des Templates?)
    Comme ceci: (au passage tu avais oublié le public sur l'interface )
    Le public sur l'interface? Mon programme marche correctement sans l'expliciter, et tu ne l'as pas mit dans ta correction. D'ailleurs, nous ne sommes pas censé implémenter d'autres classes à cette interface autre part que le package Input, n'est-ce pas? Sinon, merci pour cet exemple de Template, ça m'aide beaucoup!

    Citation Envoyé par tchize_
    Tant que j'y pense, on peut faire plus propre avec des lambda
    Ah, ces lambda... Il faut que je me penche sérieusement là-dessus!

    Citation Envoyé par tchize_
    Dernière remarque: des System.exit() dans le parseur, jamais !!!!
    Ah, ce bon vieux Claude... Je note!

    Sinon, merci beaucoup pour tes réponses tchize_

    PS:
    En fait, il est toujours possible de développer une petite classe offrant les services de base que
    sont la lecture d’un entier, d’un flottant ou d’un caractère. Vous trouverez une telle classe
    sous le nom Clavier.java parmi les fichiers source disponibles en téléchargement sur
    www.editions-eyrolles.com, ainsi que sa liste complète en annexe B. Il n’est pas nécessaire
    de chercher à en comprendre le fonctionnement pour l’instant.

  5. #5
    Membre à l'essai
    Homme Profil pro
    Lycéen
    Inscrit en
    Avril 2014
    Messages
    11
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Lycéen

    Informations forums :
    Inscription : Avril 2014
    Messages : 11
    Points : 17
    Points
    17
    Par défaut
    Re-bonjour,

    Je suis perdu
    J'essaye de comprendre comment marche les lambda, mais j'ai du mal... beaucoup de mal... à cause de petits détails que je ne comprends pas (je m'avance un peu trop dans mon apprentissage j'ai l'impression...)

    Déjà, d'après ce que j'ai compris, les lambda permettent de définir des closures (des méthodes anonymes).
    Essayons de faire une méthode readInteger(). En Java 7, ça nous donnerait ça (si je ne me trompe pas) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    public static int readInteger() {
            return readNumber(new InputReturnType<Integer>() {
                    @Override
                    public Float parse(String str) {
                            return Float.parseFloat(str);
                    }
            });
    }
    Au niveau de la syntaxe des lambda, nous avons (paramètre) -> expression ou bien (paramètre) -> { bloc }. Donc je peux comprendre ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    public static int readInteger() {
            return readNumber((String str) -> Integer.parseInt(str))
    }
    str est le paramètre qui est utilisé en argument à la méthode parseInt.

    Mais dans ce cas-ci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    public static int readInteger() {
            return readNumber((String) -> Integer.parseInt(String))
    }
    String est-il un nom de variable (j'en doute fortement), est-il juste considéré comme un type (pourquoi peut-on utiliser String en argument à parseInt alors?), ou bien s'agit-il d'une deuxième utilisation des lambda? Ou encore...
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    public static int readInteger() {
            return readNumber((choucroute) -> Integer.parseInt(choucroute))
    }
    Le type est-il implicitement donné?

    Voyez? Je suis perdu... mais attendez! Noël est pas fini!
    NetBeans me propose de convertir ma méthode anonyme en lambda, mais aussi en...
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    public static int readInteger() {
            return readNumber(Integer::parseInt);
    }
    C'est quoi ce... bidule? Mon IDE appelle cette chose, les deux ::, member reference. Moi ça me fait penser au namespaces du C++, je me doute bien évidemment qu'il ne s'agit pas de ça mais je ne comprends pas du tout la syntaxe... Comment k'on sait que parseInt attend un String?


  6. #6
    Expert éminent sénior
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 481
    Points : 48 806
    Points
    48 806
    Par défaut
    Citation Envoyé par Kairos Voir le message
    String est-il un nom de variable (j'en doute fortement)
    Oui, d'ailleurs c'est toujours une mauvaise idée de donenr à une variable le même non, casse pour casse que sa classe car ça amène à tout confondre
    Citation Envoyé par Kairos Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    public static int readInteger() {
            return readNumber((choucroute) -> Integer.parseInt(choucroute))
    }
    Le type est-il implicitement donné?
    oui, comment? Parce que le compilateur connait la signature de l'unique méthode de ton interface, il sait que InputReturnType::parse prend le paramètre de type String. Choucroute est donc de type string.


    Citation Envoyé par Kairos Voir le message
    Voyez? Je suis perdu... mais attendez! Noël est pas fini!
    NetBeans me propose de convertir ma méthode anonyme en lambda, mais aussi en...
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    public static int readInteger() {
            return readNumber(Integer::parseInt);
    }
    C'est quoi ce... bidule?
    Une lambda, en gros, c'est un méthode qui va faire des trucs. Du coup, comme c'est des gros malins chez oracle, il se sont dit: ben en fait, alors, un nom de méthode, c'est une lambda

    Donc pour résumer, InputReturnType<Integer> a une seule méthode, qui retourne un Integer et qui prend en paramètre un String. Du coup, toute méthode correspondant à ça est une valeur acceptable pour la lambda. Et donc, netbeans te raccourcis ta lambda au maximum en passannt Integer::parseInt comme lambda, qui dit "appeler la méthode statique parseInt sur la classe Integer"

    ceci:
    Integer::parseInt
    et ceci
    (x) -> Integer.parseInt(x)
    c'est fondamentalement la même chose.

    Si tu veux appeler une méthode non statique, genre tu veux faire une lambda qui prendrait un paramètre int et retournerait un String, tu pourrais faire:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    String maChaine = "blablabla";
    //....
    appelleLaLambda( (it)-> maChaine.substring(it));
    // équivalent à 
    appelleLaLambda( maChaine::substring);

  7. #7
    Membre à l'essai
    Homme Profil pro
    Lycéen
    Inscrit en
    Avril 2014
    Messages
    11
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Lycéen

    Informations forums :
    Inscription : Avril 2014
    Messages : 11
    Points : 17
    Points
    17
    Par défaut

    J'ai enfin compris!


    Merci beaucoup pour tes explications, je peux enfin dormir tranquille maintenant (enfin... pas avant d'avoir trouvé un bon livre Java . Je vais peut-être tout simplement continuer HeadFirst Java, il n'était pas si mal)

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

Discussions similaires

  1. Réponses: 0
    Dernier message: 12/08/2008, 17h12
  2. Implémentation d'une classe dérivant de std::ostream
    Par three minute hero dans le forum SL & STL
    Réponses: 1
    Dernier message: 08/07/2008, 14h07
  3. [visual studio] créer une classe en C++ utilisable en VB .net
    Par thune123 dans le forum Général Dotnet
    Réponses: 4
    Dernier message: 08/02/2008, 15h39
  4. Réponses: 2
    Dernier message: 04/04/2006, 23h22
  5. Réponses: 12
    Dernier message: 01/07/2004, 11h03

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