rapport_finale.md 14.4 KB

RAPPORT FINALE

ANDJEMBE Maksoudath, TANIEL Rémi

Le but du projet est de réaliser un tableur "basique" mais facilement extensible, l'application sera divisée en 2 parties :

  • le kernel
  • la partie graphique

Le kernel s'occupera de toutes les opérations de notre grid, les cases ne pourront contenir que des réels ou des formules(opération binaire ou des fonctions acceptant des plages de cases).

1. ANALYSE ET CONCEPTION

SCHEMA UML

Voici le schéma UML de notre application, les classes et méthodes abstraites sont en italique :

UML

PSEUDO-JAVA CREATION GRILLE,CASES

Voici un exemple de création d'une grid et de l'ajout / modification / affichage de plusieurs types de case :

class Application {

    public static void main(String[] args) {
        Grid g = new Grid();

        g.createCase("b",1); //Ajout case vide
        g.createCase("a",1,100.0); //Ajout case avec valeur
        g.createCase("a",2,50.0); //Ajout case avec valeur
        g.createCase("a",3,new Addition(g.getCase("a",2),g.getCase("a",1))); //Ajout case avec operation binaire

        List<Cell> plageCase1 = new ArrayList<Cell>(); // Crée une liste de case
        plageCase1.add(g.getCase("a",1));
        plageCase1.add(g.getCase("a",2));
        plageCase1.add(g.getCase("a",3));

        g.createCase("a",4,new Somme(plageCase1)); //Ajout case avec fonctions

        g.setValue("b",1,100); //Met la valeur de b1 à 100

        List<Cell> plageCase2 = new ArrayList<Cell>(); // Crée une liste de case
        plageCase1.add(g.getCase("a",4));
        plageCase1.add(g.getCase("a",2));
        plageCase1.add(g.getCase("a",3));

        g.setFormula("b",2,new Moyenne(plageCase2)); //Met la formule dans b2 

        g.getValue("a",1); //Affichera 100.0
        g.getValue("a",4); //Affichera (100+50+150)=100
        g.getFormulaAsString("b",2); //Affichera MOYENNE(a4,a2,a3)
        g.getDevelopedFormula("b",2); 
    }
}

CHOIX STRUCTURE DE DONNÉES

Nous devons choisir une structure de donnée pour stocker les cases dans notre grid, nous savons déjà que nous allons utiliser ne collection pour les stocker,voici celles que nous connaissons:

  • des tableaux
  • des listes
  • des maps
  • des sets

D'après le schéma UML ci-dessus, nous allons donc utiliser une HashMap pour stocker les cases de notre grid :

  • Pour rechercher une case et, effectuer des opérations dessus ce sera plus facile, la clé de la Map sera une chaine de caractère (String) qui représente la coordonnée de cette case (c'est-à-dire la concaténation du nom de ligne et de l'indice de la colonne, exemple "A1", "B9", etc...)

Une case peut etre utilisée dans plusieurs autres cases, on ne sait pas le nombre d'autres cases où elle sera utilisée, on stockera donc cette donée dans une ArrayList de Case.

Certaines fonctions (Moyenne, Somme) utilise également une plage de case, pour stocker ces cases, nous allons également une ArrayList de Case.

METHODES ESSENTIELLES EN PSEUDO-JAVA

1. Methode getValeur

Cette méthode retourne la valeur d'une case se trouvant dans une grille grâce aux coordonnées de la case. Elle utilise la méthode getCell qui permet d'accet à une case qui existe et dans le contraire lève une exeption.


class Grid {
  Map<String, Cell> cells  = new HashMap<>();

  double getValue(String column, int line){
    return this.getCell(column, line).getValue();
  }
}


class Cell {
  String column;
  int line;
  double value;

  double getValue() {
    return value;
  }
}

2. Methode getFormuleAsString (acceder au contenu)

Cette méthode permet de retourner le contenu d'une case. Si elle contient une formule, on retourne la formule simple et dans le cas contraire son nom pou dire qu'elle ne contient pas de formule mais une valeur.


class Grid {
  Map<String, Cell> cells  = new HashMap<>();

  String getFormuleAsString(String column, int line) {
    return this.getCell(column, line).toString();
  }
}


class Cell {
  String column;
  int line;
  double value;
  Formula formula;

  String getFormuleAsString() {
      if (formula != null) 
          return formula.toString();
      else
          return toString();
  }
}

Exemple pour Addition (Opération Binaire)

La classe BinaryOperation implémente la methode toString() en, récuperant l'opérateur qui est définit dans la méthode getOperator() de chaque classe qui hérite d'elle.

class BinaryOperation {
    Cell leftCell;
    Cell rightCell;

    public String toString() {
        return "(" + this.leftCell.getId() + this.getOperator() + this.rightCell.getId() + ")";
    }
}
class Addition {

    public String getOperator() {
        return "+";
    }
}

3. Methode getDevelopedFormula

Cette méthode renvoie la formule dévéloppée d'une case.

class Grid{
  Map<String, Cell> cells  = new HashMap<>();

  String getDevelopedFormula(String column, int line) {
    return this.getCell(column,line).getFormuleDeveloppe();
  }
}

class Cell{
  String column;
  int line;
  double value;
  Formula formula;
  List<Cell> usedIn = new ArrayList<>(); 

  String getDevelopedFormula() {
      if (formula != null)
          return formula.getDevelopedFormula();
      else
          return toString();
  }
}

Exemple de la méthode getDevelopedFormula() pour la class Average (moyenne)

class Average {
    List<Cell> listCases = new ArrayList<Cell>();

    String getDevelopedFormula() {
        return Average + listCases.stream().map(c -> c.getFormuleDeveloppe()).collect((Collectors).joining(", ")) + ")";
    }
}

4. Methode eval()

Cette méthode permet de retourner le résultat d'un calacul avec des opérations binaires ou des fonctions.

Opération binaires

class BinaryOperation { Cell leftCell; Cell rightCell;

} Les classes Addition,Multiplication,Substraction et Division héritent de la classe BinaryOpération et utilisent donc ses attributs leftCell et rightCell.

  • Dans Addition :
class Addition {

  double eval() {
      return leftCell.getValue() + rightCell.getValue();
  }
}

  • Dans Multiplication :
class Multiplication {


  double eval() {
      return leftCell.getValue() * rightCell.getValue();
  }
}
  • Dans Soustraction :
class Substraction {

  double eval() {
      return leftCell.getValue() - rightCell.getValue();
  }
}

  • Dans Division :
class Division {


  double eval() {
    if (droite.getValue() != 0)
        return gauche.getValue() / droite.getValue();
    else
        lève une exception
  }
}

Fonctions

class Function { List listCells;

} Les classes Average et Sum héritent de la classe BinaryOpération et utilisent donc son attribut listCells.

  • Dans Moyenne :
class Average {

  double eval() {
    double val=0;

    if (listCases.size() != 0)
      for(int i=0; i<listCells.size(); i++)
            val += listCells.get(i).getValue();
      return val / listCells.size();
    else
      lève une exception
  }
}

  • Dans Somme :
class Sum {

  double eval() {
    double val=0;
    if (listCases.size() != 0)
      for(int i=0; i<listCells.size(); i++)
            val += listCells.get(i).getValue();
      return val;
    else
        lève une exception
  }
}

5. Methode setValue

Lorsque l'on modifie une valeur d'une case, on met à jour la valeur des cases qui utilisent la case.

class Grid {
  Map<String, Cell> cells  = new HashMap<>();

  void setValue(String column, int line, double value)  {
        this.getCell(column, line).setValue(value);
    }
}

class Cell {
  String column;
  int line;
  double value;
  Formula formula;
  List<Cell> usedIn = new ArrayList<Cell>(); 

  void setValue(double valeur) {
    value = valeur;
    for(int i=0; i<usedIn.size(); i++)
       usedIn.get(i).updateValue();
  }
}

5. Methode setFormula

Cette méthode assigne une formule à une case en étant sûre qu'un cycle n'est pas créée.

class Grid {
  Map<String, Cell> cases  = new HashMap<>();

  void setFormula(String column, int line, Formula formula) {
    String code = colonne + ligne;
        this.getCell(column, line).setFormula(formula);
  }
}


class Cell {
  String column;
  int line;
  double value;
  Formula formula;
  List<Cell> usedIn = new ArrayList<Cell>(); 

  void updateValue() {
      valeur = formula.eval();
  }

  void setFormula(Formula formula) {
    if (!formula.createCycle(this))
      formule = formula;
      updateValue();
      for(int i=0; i<usedIn.size(); i++)
        usedIn.get(i).updateValue();
    else
      lève une exception
  }
}

5. Methode createCycle

Cette méthode vérifie si on créée un cycle direct ou indirece.La façon de la définir est differente si on a une opération binaire ou une focntion.

Opération binaire
class BinaryOperation {
    Cell leftCell;
    Cell rightCell;

    boolean createCycle(Cell cell) {
        Si la case gauche ne contient pas de formule et la case de droite contient une
          rechercher si la case est dans la formule de la case de droite 

        Si la case gauche contient une  formule et la case de droite n'en contient pas
          rechercher si la case est dans la formule de la case de gauche

        Si la case gauche et la droite contiennent des formules
          rechercher si la case est dans les deux formule

        Si les deux cases n'ont pas de formules
          rechercher si la case est égale à case gauche ou droite
    }
}

Fonction
class Function {
    List<Cell> listCells;

    boolean creerCycle(Cell cell) {

        Si la case n'est pas dans listCells
          Pour toute les cases dans listCells qui contiennent des formules, regarder si la case est dans la formule
    }
}

LISTE DE TESTS

Afin de s'assurer de la maintenabilité de notre code et de la qualité de celui-ci, nous allons réaliser plusieurs tests sur les différentes méthodes que nous allons programmé dans notre application, voici quelques exemples :

  • Création d'une case avec une valeur fixe
  • Création d'une case avec une formule d' Opération binaire et une fonction comme Moyenne
  • Modification d'une case avec une valeur sans qu'elle soit utilisée dans une autre case
  • Modification d'une case avec une valeur utilisée dans une autre case
  • Vérification qu'une erreur se lève lors de la création des 2 types de cycles (direct et indirect)
  • Renvoie de la formule dévéloppée d'une case avec une formule assez compliqué

2. STRUCTURE DU PROJET

PACKAGES,CLASSES,FICHIERS DE DONNÉES

L'implémentation du projet peut se résumer comme suit :

PACKAGE

Le schéma ci-dessus nous montre que, l'implémentation est composé de 5 package (representant 5 repertoires) contenant des classes ( chaque classe est un fichier d'extension java).

MODES D'UTILISATION

Concernant les commandes de compilation et d'exécution, sous des logiciels comme Eclipse ou IntelliJ par exemple, juste un clique bouton suffit.

Par contre, dans le cas contraire, il faut se placer dans à la raicne du repertoire contenant les fichiers et lancer les commandes suivantes :

Commande de compilation:

mkdir -p bin|find src -not ( -path src/kernel/test -prune ) -name *.java|xargs -i javac -d bin {} -cp src/

Commande d'exécution:

java -cp bin app.Menu pour tester les possibilités qu'offre une grille ou java -cp bin ihm.TablooProto pour lancer l'interface graphique de la grille.

Si l'on ne veut pas saisir ces commandes, on peut directement utiliser les fichiers bash exécutable appbash ou tablobash respectivement pour acceder au menu ou à l'interface grapphique en faisant ./appbash ou ./tablobash.

3. BILAN

Au cours de ce projet les actions suivantes ont été faites :

Le noyeau

  • La gestion des cycles aussi bien direct qu'indirects.
  • La modification d'une case par des valeurs ou des formules et dans ce dernier cas la vérification qu'aucun cycle n'est créée.
  • La gestion de la langue (en francais et en anglais).
  • De supprimer une case que si elle existe et, n'est pas utilisée par d'autres cases.
  • De sérialiser l'état d'une grille après avoir effectué des actions.

L'interface graphique

L'interface graphique implementée permet d'effectuer les actions suivantes :

  • Créer une case à partir de d'une valeure.
  • Créer une case en saisissant une formule (aussi bien français qu'en anglais). Par exemple, on crée la case A10=SOMME(A1,A2,B2)

CASE

CASE

  • Emettre une erreur lorsque l'on saisit du texte au lieu d'un double ou une formule incorrecte.

CASE

  • Emettre une erreur indiquant qu'un cycle est généré.
  • Emettre une erreur indiquant qu'on ne peut pas supprimer une case utilisé par d'autres cases.

CASE

Les tests

Plusieurs tests ont été réalisés au cours de ce projet notamment celles permettant de :

  • Créer une ou plusieurs cases
  • Mettre à jour une case avec une valeur ou formule
  • Créer une exception lorsqu'un cycle est créee
  • Créer une exception lorsqu'on utilise une case pas encore créée
  • Effectuer le re-calcul des cases qui dependent d'une case quand elle est modifiée
  • Supprimer une case que si elle n'est pas utilisée par d'autres cases

Conclusion

En résumé, ce projet de programmation par objet (PPO) avait pour but de développer les actions que l'on peut faire avec un classeur Excel usuel.Il était divisé en deux parties :

  • La première, créée un noyeau avec differentes méthodes permettant d'effectuer et vérifier des actions comme la création, modification d'une case avec des valeurs, opérations usuelles ou formules.
  • La deuxième, de créer un interface. Ces deux parties ont été réalisées ainsi que des tests permettant de fonctionner le fonctionnement. ce projet nous a donc été bénéfique car, il nous a permis de pouvoir mettre en pratique nos connaisances tout en developpant des nouvelles. Aussi, concernant les améliorations possibles , l'on pourrait envisager de pouvoir ajouter dans l'interface une option permettant de choisir la langue étant donné qu'actuellement l'on fait un choix d'affichage dans une langue et l'utilisateur ne peut la modifier mais, peut rentrer le nom de d'une fonction dans les deux langues, sera fonctionnera.