:: Enseignements :: Master :: M1 :: 2015-2016 :: Programmation Orientée Objet - Design Patterns ::
[LOGO]

Je loggue, tu loggues, ...


Decorateur externe, Decorateur interne, Proxy et Composite

Exercice 1 - Mon logger à moi

Un des principaux sports en Java est de se ré-écrire sa propre bibilothèque de logger, car les bibliothèques java.util.logging, Apache Common logging, Apache Log4j, Apache Log4j2, Tiny Log, Android Log et QOS Logback sont vraiment pas terribles.

Votre entreprise, elle aussi atteinte du syndrome NIH, utilise la classe suivante

Bien sûr, elle ne permet de logguer des messages que sur la sortie d'erreur standard mais nous allons améliorer cela.

  1. On souhaite aussi pouvoir compter le nombre de chaque niveau d'erreurs envoyé au logger, on se propose donc d'écrire une classe CounterLogger avec une méthode supplémentaire int getLevelCounter(Level level) qui renvoie un entier indiquant le nombre d'erreurs recu pour un niveau d'erreur.
    Ecrire la classe CounterLogger.
    Note de Java: Il existe une classe java.util.EnumMap qui possède deux méthodes intéressantes getOrDefault() et merge !
  2. En tant que développeur, on souhaite pouvoir manipuler indifféremment un SystemLogger ou un CounterLogger à travers la même API.
    Faite les changements qui s'impose.
  3. On peut remarquer que créer plusieurs instances de SystemLogger est un peu idiot, on pourrait partager la même instance.
    Quel design pattern doit-on utiliser dans ce cas ?
    Faites les changements qui s'impose dans la classe SystemLogger.
    1. On souhaite pouvoir filtrer les logs du CounterLogger, c-a-d afficher les logs qui sont plus grands ou plus petits qu'un certain Level.
      Comment doit-on faire ?
      Implanter la solution retenue.
      PS: pour plus de flexibilité on representera le fait de choisir si on affiche un Level ou non par un Predicate<Level> pour ne pas hardcoder le choix d'un Level dans le code.
    2. On veut aussi permettre de filtrer les logs sur le SystemLogger et plus généralement sur n'importe quel logger.
      Expliquer pourquoi utiliser une classe abstraite qui contient un Predicate pour partager le code ne marche pas ici ?
    3. Comment le design pattern Proxy permet de résoudre le problème ?
      Modifier votre code pour implanter ce design pattern.
    4. En fait, dans 90% des cas, on ne rend pas la classe du proxy publique.
      Expliquer pourquoi et comment un utilisateur va utiliser la classe ?
      Faire les changements qui s'imposent dans le code.
  4. Votre chef a essayé de créer un nouveau Logger, un PathLogger avec le code suivant:
    		   public class PathLogger implements Logger {
                 private final BufferedWriter writer;
    
                 public PathLogger(Path path) throws IOException {
                   this.writer = Files.newBufferedWriter(path);
                 }
      
                 @Override
                 public void log(Level level, String message) {
                   try {
                     writer.write(level.name() + ' ' + message);
                     writer.newLine();
                     writer.flush();
                   } catch(IOException e) {
                     throw new UncheckedIOException(e);
                   }
                 }
               }
    		  
    mais il veut le tester comme ceci:
    		    public class Foo {
    		      ...
    		      private static final PathLogger logger = new PathLogger(Paths.get("log.txt"));
    		    }
    		  
    le code ne compile pas car il manque la gestion des exceptions d'entrée/sortie. (la classe PathLogger à aussi un problème car il n'y a pas de close mais ce n'est pas ce qui nous intéresse ici).
    Au lieu de devoir gérer les exceptions dans un bloc statique, il vous demande d'écrire du code qui essaye de creér un PathLogger et s'il n'y arrive pas pour cause d'exceptions, renvoie un SystemLogger à la place.
    Bien sûr ce code devra pouvoir être appelé avec des fichiers de log différents.
    Quel design pattern doit on mettre en oeuvre ici ?
    Ecrire le code correspondant.
  5. Enfin, on souhaite pouvoir créer des loggeurs capable par exemple d'afficher ses logs sur la console et de maintenir les compteurs pour chaque level d'erreur.
    Comment doit-on implanter un tel logger ?
    Ecrire le code correspondant.