Exercices sur la syntaxe

Retour à la syntaxe :

Il n’est pas nécessaire de réaliser tous les exercices pendant la durée du cours. Par contre nous vous conseillons de tous les réaliser pendant une période de révision.

La plupart de ces codes représentent des situations d’erreurs courantes (la plupart sont des erreurs de syntaxe -sauf celles marquées "erreur de runtime"-). Tentez de détecter ces erreurs (simplement en lisant les codes!) et d’apporter rapidement un correctif.

public class TestMeth1 {
   private int val ;

   public void setVal(int valeur) {
      this.val = valeur ;
   }

   public int getVal() {
      return valeur ;
   }

}
public class TestMeth2 {
   private int val ;

   public void setVal(int valeur) {
      this.val = valeur ;
   }

   public int getVal() {
      return this.val ;
   }

   public int modify(int valeur) {
      int oldval = this.val ;
      this.val = valeur ;
   }

}
//
public class TestMeth3 {
   private int val ;

   public void setVal(int valeur) {
      this.val = valeur ;
   }

   public int getVal() {
      return this.val ;
   }

   public int modify(int valeur) {
      int oldval = this.val ;
      this.val = valeur ;
      return oldval ;
   }

   public static void main(String[] args) {
      TestMeth3 obj = new TestMeth3() ;
      obj.setVal(3) ;
      modify(7) ;
   }

}
// aussi bizarre que ça puisse paraître
// un code de ce type a été un jour
// écrit par un stagiaire!
public class TestMeth5 {
   private double val ;

   public void setVal(double valeur) {
      this.val = valeur ;
   }

   public double getVal() {
      return this.val ;
   }

   public double modify(double valeur) {
      double oldval = this.val ;
      this.val = valeur ;
      return oldval ;
   }

   public static void main(String[] args) {
      TestMeth5 obj = new TestMeth5() ;
      public static double sin(double a = 3) {
         return 0.0523359 ;
      }
      obj.setVal(sin) ;
      obj.modify(7.) ;
   }

}

(celui-ci n’est pas un problème de syntaxe: utilisation erronée d’un méthode)

//
// syntaxe ok mais erreur d'écriture de code quand même!!!!
// doc de BigDecimal dans package java.math
//
public class TestMeth6 {

   public static void main(String[] args) {
      java.math.BigDecimal value = new java.math.BigDecimal("10") ;
      java.math.BigDecimal anotherValue = new java.math.BigDecimal("20") ;
      value.add(anotherValue) ;

      System.out.println(value) ; //à comprendre
      System.out.println(anotherValue) ;

   }

}
public class TestM {


   private java.util.Random luck = new java.util.Random() ;



   public char pickCharIn(String aString) {
      private int length = aString.length() ;
      private int index = luck.nextInt(length) ;
      return aString.charAt(index) ;
   }
}
public class TestC1 {
   private int valeur ;

   public TestC1(int val) {
      this.valeur = val ;
   }

   public static void main(String[] args) {
      TestC1 instance = new TestC1() ;
      instance.valeur = 9 ;
   }
}
public class TestC2 {
   private int valeur ;


   public static void main(String[] args) {
      TestC2 instance = new TestC2(9) ;
   }
}
public class TestC3 {
   private int valeur ;

   public void TestC3(int val) {
      this.valeur = val ;
   }

   public static void main(String[] args) {
      TestC3 instance = new TestC3(9) ;
   }
}

(problème d’utilisation non détecté par le compilateur, mais les IDE le détecte )

// problème écriture
//
public class TestC4 {
   private String string ;

   public  TestC4(String string) {
      string = string ;
   }

   public int length() {
      return string.length() ;
   }

   public static void main(String[] args) {
      TestC4 instance = new TestC4("aString") ;
      System.out.println(instance.length()) ;
   }
}

codes

une classe simple

Ecrire un classe simple qui décrive une Voiture vendue par un concessionaire.

  • une Voiture a une référence, un modèle, un prixHT et une couleur (valeur par défaut: gris - java.awt.Color.LIGHT_GRAY -).

  • écrire deux constructeurs (une avec une référence, un modèle et un prix, un autre avec en plus une couleur).

  • appliquer l’encapsulation des données et écrire les méthodes correspondantes.

  • écrire une méthode double getPrixTTC() qui multiplie le prix par la T.V.A et l’arrondit.

  • écrire un test simple.

design

Comment manipuler des données de manière cohérente?

(Pour révisions et approfondissements)

Ce n’est pas vraiment un exercice de cours mais une invitation à réfléchir sur les raisons d'être de la programmation "à objets"

Deux programmeurs (Marius et Olive) participent à l'écriture d’une application bancaire.

Marius est en charge du code qui gère la notion de Compte (un "compte en banque").

Pour simplifier la description des données nous allons supposer que pour gérer les données lièes à cette notion de "Compte" nous allons utiliser une combinaison de données (un "aggrégat") spécifique à chaque Compte en Banque.

Dans ce cas un Compte (au sens de l’application) réunit:

      Identifiant unique
propriétaire du Compte (Client)
      solde
      découvert autorisé
      + autres données qui seront analysées et prises en compte plus tard.

Il y a des règles de gestion -règles "métier"- associées à ces combinaisons de données:

  • Quand on crée un Compte il doit avoir un Identifiant (et cet identifiant ne peut changer ultérieurement)

  • Quand on crée un Compte il doit avoir un client (qui peut toutefois changer) - c’est un décision formulée par les propriétaires de la banque!-

  • Quand on crée un Compte le solde et le découvert autorisé sont à zéro

  • Le découvert autorisé peut changer (mais doit toujours être un nombre positif ou nul)

  • Lorsqu’on réalise des opérations de dépôt ou de retrait le solde évolue. Il peut devenir négatif (mais ne peut dépasser le découvert autorisé)

  • …. (etc., etc. autres règles ici: on notera que certaines de ces règle peuvent évoluer pendant la durée de vie de l’application).

Marius est responsable de la bonne application de ces règles. Il les connait toutes (ce qui n’est pas le cas des autres programmeurs).

Olive est responsables d’autres codes mais il va utiliser le code Compte qui "appartient" à Marius. Il n’est pas supposé connaître toutes les règles des gestion liées à Compte (séparation des problèmes, division du travail).

Donc:

  • Comment Marius peut-il forcer Olive à créer un Compte correctement? On doit avoir une combinaison de données cohérente et conforme au spécifications.

  • Une fois que cette donnée est créée comment Marius peut il "forcer" Olive à l’utiliser en conformité avec les règles de gestion -par exemple en empéchant qu’on retire trop d’argent du compte … certes Olive peut le savoir mais peut aussi être distrait parce que son attention est focalisée ailleurs.

  • A partir de ces contraintes que peut-on déduire sur la situation relative des données et du code "métier" qui les manipulent dans le détail? La réalisation et le bien fondé de ce code sont sous la responsabilité exclusive de Marius.

  • Comment ces codes peuvent ils être utilisés par Olive? A quoi peut-il accéder?

  • Plus tard les règles métier vont évoluer (par exemple: si le montant d’un dépôt dépasse un certain montant il va falloir faire une déclaration pour permettre la détection de "l’argent sale"). Qui va écrire ce code et où va t-il se trouver? Est ce que les codes écrits par Olive doivent être modifiés?

Design d’une application (simplifié)

Au cours de cet exercice vous n’allez pas écrire de code: vous allez tenter de faire une conception simple en utilisant le concept d'objet. Le code sera écrit ultérieurement

L’idée générale est la suivante: Oubangui.com est une chaîne de librairies (au sens de magasin vendant des livres). Le concept s’inspire de grandes librairies américaines dans lesquelles il y a des nombreuses pièces dans lesquelles on peut consulter des livres en rayon.

Dans chacune de ces pièces il y a un, ou plusieurs, terminaux qui permettent de rechercher un livre, voir quelques informations -comme par exemple l’endroit où il se trouve en rayon- et permettent aussi de commander un livre (que l’on vient de consulter). A la sortie on pourra retrouver ce livre dans un "panier" tout préparé pour passer à la caisse.

Pour que cet exercice soit le plus simple possible vous aurez à noter de manière informelle vos suggestions pour deux classes : Livre et Panier (Le "panier" virtuel dans lequel le client met les livres qu’il veut acheter).

Une autre classe pour cette partie de l’application devrait être la classe Client mais pour nos besoins du moment nous adopterons une définition provisoire ultra-simplifiée: la classe contiendra juste une chaîne pour le "nom" du client.

Nous ne nous préoccuperons pas d’aspects complexes de la vie du Panier comme le paiement, etc.. Nous voulons simplement "remplir" le panier virtuel correctement.

  • Pour chaque classe essayer de noter une liste d’attributs et de services que l’on peut demander aux objets. (Pour le moment ne pas se préoccuper de définir des "constructeurs")

  • Essayez de noter si les attributs sont obligatoires et quels sont ceux qui sont "immuables" (qui ne peuvent pas changer une fois l’objet créé).

Quelques suggestions:

Maquette de l’application "Oubangui"

(exercice de révision à réaliser ultérieurement)

Dans un package (nommé par exemple com.oubangui.simplebiz) réaliser une première version des codes qui implantent les spécifications de l’application de gestion de librairies.

  • Coder la classe Livre et y ajouter une méthode public String toString() (rend une chaîne lisible pour un être humain et qui repésente de manière simplifiée l'état de l’instance).

  • Utiliser ce code pour la classe Client.

package com.oubangui.simplebiz;

/**
 * Un client minimum pour commencer!

 */
public class Client {
        // MEMBRES :  CHAMPS
      // immuables, obligatoire
   private String nom ;

   // NON MEMBRES : CONSTRUCTEURS
        /**
         * le constructeur "minimum" doit initialiser tous les champs obligatoires
         */

   public Client ( String nom) {
      this.nom = nom ;
   }

   // MEMBRES : ACCESSEURS
   public String getNom() { return this.nom ; }
}
  • coder une classe Panier simplifiée avec:

    • une méthode public void ajouter(Livre lv). Pour réaliser provisoirement ce service utiliser une variable membre qui soit un tableau pouvant référencer 5 livres. Ne pas faire de contrôle sur le dépassement éventuel du nombre maximum de livres (c’est une maquette "quick and dirty") Faites donc un ajout simple (on ne retire pas de livre pour le moment).

    • une méthode String toString() qui renverra un objet String avec une ligne pour chaque chaîne représentant un Livre dans le panier.

  • Dans une classe de test écrire un main très simple: créer deux Livres, les mettre dans un Panier et afficher le contenu du panier.

Classe "Garage" (première version)

Pour clore ce chapitre un exercice qui permette de repréciser certaines techniques.

La société qui commercialise des Voitures les range dans un garage.

D’abord créer un package en respectant les normes de nommage puis déplacer le code de Voiture dans ce package.

Ecrire une classe Garage avec les caractéristiques suivantes:

  • un Garage est créé en spécifiant le nombre de places. La réalisation consiste à initialiser un tableau de Voitures

  • Dans un Garage on peut garer une Voiture. pour cela il faut rechercher la première place "libre" (le premier élément du tableau qui est à null); puis à initialiser l’emplacement correspondant avec la voiture à garer.

    (méthode public void garer(Voiture voiture))

    (le code d’un méthode public void sortir(Voiture voit) sera écrit plus tard).

  • ( facultatif ) écrire une méthode qui donne le montant total correspondant à la somme des prixTTC des voitures garées.

  • recopier cette méthode (elle nous servira à tracer le contenu du garage - pensez à mettre une méthode toString() dans Voiture -):

       public String tostring() {
          return Arrays.toString(tableauParking) ;
       }

Faire un code de test en créant un Garage, en garant deux voitures et en traçant les résultats.

Proposition de corrigés:

Exemples

une classe simple

public class Voiture {
   private final String référence ;
   private final String modèle ;
   private double prixHT ;
   private java.awt.Color couleur  ;

    /*  Pour les besoins de l'exercice seulement
     * en effet le taux de TV peut changer
     * et on ne devrait pas avoir à modifier ce code
     * en pratique ce taux est ce qu'on appelle une "ressource"
     * et il faudra apprendre à la configurer.
     */
   public static double TAUX_TVA = 1.235 ;


   public Voiture (String ref ,String md, double px) {
       this(ref, md, px, java.awt.Color.LIGHT_GRAY);
   }

   public Voiture (String ref , String md, double px, java.awt.Color col) {
      this.référence = ref ;
      this.prixHT = px ;
      this.modèle = md ;
      this.couleur = col ;
   }

   public String getRéférence() {
      return this.référence ;
   }

   public String getModèle() {
      return this.modèle ;
   }

   public java.awt.Color getCouleur() {
      return this.couleur ;
   }

   public double getPrixTTC() {
      return Math.rint(this.prixHT *  TAUX_TVA) ;
   }

        /** prixHT est le seul champ modifiable!
         * @param prixHT
         */
   public void setPrix(double prix) {
      this.prixHT = prix ;
   }


   //////////////////////////////////////////////////////////
   public static void main(String[] args) {
      Voiture rosalie = new Voiture("ref456", "verso", 11567.56, java.awt.Color.GRAY) ;
      System.out.println(rosalie.getPrixTTC()) ;

   }
}

Design d’une application (simplifié)

Livre

Attributs :

nISBN

Identifiant unique (ISBN); type : String ;

Immuable : un accesseur (getISBN()) .

Obligatoire

note: on a souvent deux types d’identifiants (celui standard et celui spécifique au vendeur)

titre

Titre du Livre ; type : String ;

Immuable : un accesseur (getTitre()) .

Obligatoire

auteur

Auteur(s) ; type : String ;

Immuable : un accesseur (getAuteur()) .

Obligatoire ?

description

Commentaires descriptifs ( objectif commercial ); type: text (many lines) ;

Modifiable : accesseur et mutateur (setDescription(description), getDescription()).

Pas obligatoire (peut être null ou mieux get rend un Optional - sera vu ultérieurement -)

autres éléments de description

genre littéraire, langue, nombre de pages, format ,…

Ces éléments sont des candidats pour des améliorations ultérieure (on commence par une maquette). Certains pourront devenir de vrais attributs d’autres finiront dans un champ de "données diverses" - sans structuration typique- (chaîne, liste de propriétés,..)

prix

prix public pour le Livre (ici on n’aborde pas la T.V.A); type numérique (à déterminer).

Modifiable: accesseur et mutateur (setPrix(prix), getPrix()).

Obligatoire

Nota: l’usage d’un mutateur sera reservé à des personnes ayant le rôle et l’habilitation "administrateur".

stock

nombre de Livres en stock; type : entier positif ou égal à zéro (devra probablement être plus tard un objet distinct plus sophistiqué)

Contraintes: Obligatoire; chaque Livre doit être crée avec un stock qui sera au minimum à zéro. Pendant la vie de l’objet le stock ne devra jamais devenir négatif.

Attention : l’usage de "getStock()" par des codes "client" peut avoir des utilisations trompeuses ! (il s’agit d’une valeur "fragile" qui peut changer très rapidement: un Thread peut donner une indication qu’il en reste alors qu’un autre Thread vient d’acheter le dernier!)

Autres méthodes (qui ne sont ni des accesseurs ni des mutateurs) :

retirerDuStock(int nbLivres)

méthode publique : modifie le stock

Nous verrons ultérieurement ce qui doit se passer s’il n’y en a plus en stock!

rajouterAuStock(int nbLivres)

Modifie le stock

Nota: liée au rôle "administrateur".

Panier

Nous n’allons pas nous étendre sur les aspects purement techniques du Panier (comme le type réel de la liste de Livres). Ces détails relèvent de la réalisation.

Chaque instance de Panier a une durée de vie limitée à la visite de l’utilisateur. Quand le client paye (ou abandonne) l’instance de Panier disparaît. Le cas échéant de nouveaux objets entrent en action pour la facturation, etc. (On pourrait aussi créer des Paniers qui seraient persistants: dans ce cas quelles seraient les conséquences sur le définition des attributs de la classe?)

Méthodes:

ajouter(Livre)

ajoute un seul livre dans le panier

La méthode retirer(Livre) sera définie ultérieurement

montantTotal()

Renvoie le prix total du panier. Rend un type représentant une valeur monétaire (le type réel évoluera).

C’est typiquement une valeur calculée (pas un attribut).

contenu()

Renvoie la liste des livres contenu dans le Panier. Le type effectif sera défini ultérieurement.

Point important: l’objet rendu par cette méthode doit être non modifiable (ou une copie du contenu réél).

Association au Client :

client

attribut de type Client

peut être null …jusqu'à ce qu’un client soit réellement associé au Panier -méthode setClient(Client) - Pour éviter tout problème le mieux serait que la méthode get rende un objet Optional (sera expliqué ultérieurement).

Client

Un modèle très simplifié pour une maquette. Donc pas d’attributs comme identifiant, autres éléments du nom, adresse, données de paiement, etc. Ceci sera rajouté ultérieurement.

Attributs :

nom

type : String ; obligatoire

Immuable : un accesseur (getName()) .

autres attributs

A voir plus tard ….

Méthodes (autres méthodes qui ne sont pas des accesseurs/mutateurs) A voir plus tard …. .

Maquette de l’application "Oubangui"

package com.oubangui.simplebiz;

/**
 *
  */
public class Livre {
        // MEMBRES :  CHAMPS
      // immuables, obligatoires
   private long nISBN ; //id
   private String titre ;
   private String auteur ;

      // modifiables , obligatoires
    private double prix ;

      // modifiable, peut être non initialisé
   private String description ;

      // donnée technique
   private int stock ;

   // NON MEMBRES : CONSTRUCTEURS
   /**
    * Le constructuer "minimum" doit initialiser tous les champs obligatoires
    * (soit à partir d'un paramètre , soit d'une autre manière)
    */

   public Livre (long id, String titre, String auteur, double prix) {
      this.nISBN = id ;
      this.titre = titre ;
      this.auteur = auteur ;
      this.prix = prix ;
   }
   // d'autres cosntructeurs complémentaires peuvent exister

   // MEMBRES : METHODES
   // ACCESSEURS ET MUTATEURS
   public long getISBN() { return this.nISBN ; }
   public String getTitre() { return this.titre ; }
   public String getAuteur() { return this.auteur ; }

   public double getPrix() { return this.prix ; }
   public void setPrix(double val) {
      this.prix = val ;
   }

   public String getDescription(){
      return this.description ;
   }
   public void setDescription(String desc) {
      this.description = desc ;
   }

   // MODIFICATEURS, SERVICES
   public void retirerDuStock(int nb) { // sera modifié... on doit controler le stock!
      this.stock -= nb ;
   }
   public void rajouterAuStock(int nb) {
      this.stock += nb ;
   }

   public String toString(){
       return this.titre +
                 " (" + this.nISBN + ") "
                   + this.auteur ;
    }
}
package com.oubangui.simplebiz;

/**
 *
 */
public class Panier {
// MEMBRES :  CHAMPS
   // pourrait avoir un ID si persistant
   //
   // immuable,  pas obligatoire jusqu'au paiement ...
   private Client  client ;

   //donneés "technique"
   // risque de crash! mais maquette provisoire!
   private Livre[] livres = new Livre[10] ;
   private int nbLivres = 0 ;

   // NON MEMBRES : CONSTRUCTEURS
   // cosntructeur par défaut ICI

   // MEMBRES : ACCESSEURS/MUTATEURS
   public Client getClient() { return this.client ; }
   public void setClient(Client cli) {
      this.client = cli;
   }

   // MODIFICATEURS, SERVICES
   public void ajouter(Livre bk) {
      this.livres[this.nbLivres++] = bk ;
   }
   public String toString(){
        String res = " --- Panier ---\n";
       //mieux: utiliser java.utiL.Arrays.toString
        for(int ix = 0 ; ix < this.nbLivres; ix++){
            res += this.livres[ix].toString() + '\n'  ;
        }
        return res ;
   }
}
package com.oubangui.simplebiz;

/**
 *
  */
public class TestPanier {

    public static void main(String[] args) {
        Panier cart = new Panier();
        cart.ajouter(new Livre(1234L, "Le tour de table en 80 jours", "Pierre DAC", 1345.67));
        cart.ajouter(new Livre(45678L, "Tout Java en 666 minutes", "Mephisto Feles", .99));
        System.out.println(cart.toString());
    }
}

Classe "Garage" (première version)

package com.grandgarage.biz;


public class VoitureV1 {
   private final String référence ;
   private final String modèle ;
   private double prixHT ;
   private java.awt.Color couleur;

   /*  Pour les besoins de l'exercice seulement
    * en effet le taux de TV peut changer
    * et on ne devrait pas avoir à modifier ce code
    * en pratique ce taux est ce qu'on appelle une "ressource"
    * et il faudra apprendre à la configurer.
    */
   public static double TAUX_TVA = 1.235 ;


   public VoitureV1 (String ref, String md, double px) {
      this(ref,md,px, java.awt.Color.LIGHT_GRAY);
   }

   public VoitureV1 (String ref ,String md, double px, java.awt.Color col) {
      this .référence= ref ;
      this.prixHT = px ;
      this.modèle = md ;
      this.couleur = col ;
   }

   public String getRéférence() {
      return this.référence ;
   }
   public String getModèle() {
      return this.modèle ;
   }

   public java.awt.Color getCouleur() {
      return this.couleur ;
   }

   public double getPrixTTC() {
      return Math.rint(this.prixHT *  TAUX_TVA) ;
   }

   /** prixHT est le seul champ modifiable!
     * @param prixHT
     */
   public void setPrix(double prix) {
      this.prixHT = prix ;
   }

   public String toString() {
      return this.référence
         + " " + this.modèle ;
   }


   //////////////////////////////////////////////////////////
   public static void main(String[] args) {
      VoitureV1 rosalie = new VoitureV1("ref456", "verso", 11567.56, java.awt.Color.GRAY) ;
      System.out.println(rosalie.getPrixTTC()) ;

   }
}
package com.grandgarage.biz;

import java.util.Arrays;
import static java.awt.Color.*;

public class GarageA {

   private VoitureV1[] parking;
   private int nbPlacesLibres;

   public GarageA(int nbPlaces) {
      parking = new VoitureV1[nbPlaces];
      this.nbPlacesLibres = nbPlaces;
   }

   public void garer(VoitureV1 voiture) {
      for (int ix = 0; ix < parking.length; ix++) {
         if (parking[ix] == null) {
            parking[ix] = voiture;
            this.nbPlacesLibres--;
            break;
         }
      }
   }

   public double getValeur() {
      double res = 0;
      for (int ix = 0; ix < parking.length; ix++) {
         if (parking[ix] != null) {
            res += parking[ix].getPrixTTC();
         }
      }
      return res;
   }

   public String toString() {
      return Arrays.toString(parking) ;
   }

   public static void main(String[] args) {
      GarageA garage = new GarageA(3);
      garage.garer(new VoitureV1("hj456", "verso", 11567.56, GRAY));
      garage.garer(new VoitureV1("hj457", "frum", 8567.56, GRAY));
      System.out.println(garage.getValeur());
      System.out.println(" garage :" + garage.toString());
   }
}