mardi 27 avril 2010

Agilité - La chasse à la bonne méthode

Comme beaucoup, je suis attiré par la mise en pratique des méthodes agile dans mon travail pour palier aux inconvénients rencontrés par la pratique de méthodes dites standard. Connaissant de nom Scrum et XP je me suis déjà penché dessus pour voir leur bénéfices et comment les appliquer. Mais très rapidement on découvre qu'il existe d'autres méthodes : Kanban, Lean, RUP, PUMA, FDD...
Et la ça commence à être le bazar, les questions s'accumulent : quelle méthode choisir? Faut il en prendre qu'une ou plusieurs? Faut il faire un mixte? Prendre que ce qui nous intéresse? Prendre un coach? Suivre des formations?...

Dans cette quête de réponses, quelques sites proposent des comparatifs qui, même s'ils ne peuvent pas tout comparer, peuvent aider :
 
Bonne lecture.

lundi 26 avril 2010

Java - Swing : Exemple de base

Tout ceux qui on un jour fait du client lourd en Java se sont pris la tête pour avoir un composant qui répond exactement à leur besoin. Les bibliothèques Java Swing et AWT propose déjà un bon nombre de composant prêt à l'emploi. Sans parler des « nouvelles » librairies tel que SwingX qui permettent d'étendre le nombre de composant prêt à l’emploi. Mais même ainsi, il n'est pas rare de vouloir un comportement diffèrent de celui qui est implémenté de base. Heureusement ces librairies ont été conçues pour être entièrement paramétrable. Mais ces modifications ne sont pas toujours évidantes à réaliser, surtout si l'on n'est pas un spécialiste dans ce domaine.

Ce site référencie les modifications fréquemment faites avec le code java correspondant : SwingExamples.

vendredi 23 avril 2010

Java - JTree avec des icones spécifiques

J'ai voulu faire un JTree affichant des icones spécifiques à chaque noeud.
Dans mon cas, l'objet du noeud est capable de fournir sa propre icone, ce qui simplifie le code. Du coup la solution est simple : il suffit de faire un Renderer héritant de DefaultTreeCellRenderer et de redéfinir la méthode getTreeCellRendererComponent.

Ce qui donne :
  @Override
  public Component getTreeCellRendererComponent(JTree tree,
            Object value,
            boolean sel,
            boolean expanded,
            boolean leaf, int row,
            boolean hasFocus) {
    Component component = super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
    if (Filiale.class.isInstance(value)) {
      setIcon(((Filiale) value).getIcon());
    }
    return component;
  }

Pour plus d'info : http://java.sun.com/docs/books/tutorial/uiswing/components/tree.html#display

jeudi 22 avril 2010

Java - Bonnes pratiques


Je rassemble ici en ensemble de bonnes pratiques trouvées dans mes périgrinations.

Les classes interdites

Lors d’utilisation de classe provenant d’un SDK, il faut s’assurer qu’elles correspondent bien a nos besoins et qu’il n’y a pas plus « léger » à utiliser. Certaines classes ne sont la que pour assurer la compatibilité ascendante. On en exclura donc leur usage sauf si leur remplacent ne satisfont pas au besoin.

Exemple de classes "interdites" :
  • Hashtable : classe synchronisée => Thread-safe mais lourde (rapport 1 à 5)
    Utiliser plutôt dans une utilisation standard : HashMap
    Si besoin d’un conteneur thread-Safe utiliser : Collections.synchronizedMap
  • Vector : classe synchronisée => Thread-safe mais lourde (rapport 1 à 5)
    Utiliser plutôt dans une utilisation standard : ArrayList
    Si besoin d’un conteneur thread-Safe utiliser : Collections.synchronizedList

Concaténation de chaînes de caractères

Malgré ce que l’on pense, le code suivant n’est pas mauvais ; il est même optimum :
String s = "In memory-constrained situations" +
           ", you should pay close attention" +
           " to the" +
           " proliferation " +
           " of"+
           " strings";

En effet, le compilateur va optimiser l’expression en la transformant en une seule chaîne. De même que ce code :
String myValue = “Ma valeur à afficher”;
String s = “You should pay close attention" +
         " to the" +
         " proliferation " +
         "of"+ myValue;

Par contre celui-ci est plus coûteux car le compilateur ne sait pas l’optimiser. Et, il sera à éviter.
String s = "In memory-constrained situations";
s+= ", you should pay close attention";
s+= " to the";
s+= " proliferation ";
s+= "of";
s+= " strings";

Il est possible d’utiliser un StringBuilder qui sera moins couteux et plus flexible lorsque le 1er cas ne peut être suivi :
StringBuilder sb = new StringBuilder() ;
Sb.add("In memory-constrained situations") ;
Sb.add(", you should pay close attention") ;
Sb.add(" to the") ;
Sb.add(" proliferation ") ;
Sb.add("of") ;
Sb.add(" strings") ;

Classes retournant des collections.

Beaucoup de classe retournent des collections (List, Map,...). Ces collections doivent toujours être initialisées. S’il n’y a pas d’objet dans la collection celle-ci doit être vide mais non null.

Les tableaux fourretouts

Il faut éviter les tableaux fourretouts de type Object[]. Cela rend le code illisible et compliqué à utiliser (quel objet vais-je trouver dans le tableau ? Comment dois-je construire ce tableau ?) et source d’erreur vu qu’il n’y a pas de typage. Il est préférable de créer une classe « Bean » rassemblant les objets composant ce tableau.

Exceptions

Ne jamais ignorer une exception
Une erreur fréquente est de mettre un bloc catch vide sans aucune instruction afin de pouvoir compiler le programme. Ceci est très dangereux. En effet, si une exception survient, elle sera passée sous silence et le programme continuera de fonctionner ce qui peut déboucher sur des bugs incompréhensibles. Le bon réflexe est de bien traiter les exceptions dans les blocs catch. Mettre un commentaire clair si c’est normal de ne rien faire.

Reflet de l’erreur
Les exceptions doivent refléter l’erreur levée. Trouvez la bonne granularité : une interface graphique se moque de savoir que c’est la base de données qui ne répond pas. Elle a juste besoin de savoir que le service est momentanément interrompu. Le niveau de précision (la granularité) de l’exception doit être adapté à la position du module dans une architecture en couche.

Les exceptions génériques
Eviter de catcher les exceptions génériques du style :
Try {
     .....
} catch (Exception e) {
     .....
}

Il est préférable de catcher exception par exception et de faire le traitement spécifique. Cela permet un débogage plus facile en cas de problème.

De même, il est déconseillé de renvoyer une exception générique. Il est préférable de créer des exceptions par package ou d’utiliser les exceptions fournis dans le SDK, cela permet, sur une trace, de retrouver plus facilement les causes de l’exception levée.

Les exceptions et les entrées/sorties
En cas d’exceptions, il faut s’assurer que l’état après le catch est stable et qu’il ne reste rien d’ouvert (fichier, flux, connexion,...).
Il faut donc utiliser le « Finally » pour libérer les ressources.

Exemple java :
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class FluxMajuscule {

/** Crée une nouvelle instance de FluxMajuscule */
  public FluxMajuscule() {
  }

  public void readMaj(){
    try{
      BufferedReader br=new BufferedReader(new FileReader("monFichier.txt"));
        try{
          String ligne;
          String ligneMajuscule;
          while((ligne=br.readLine())!=null){
            ligneMajuscule=ligne.toUpperCase();
            System.out.println(ligneMajuscule);
          }
        }finally{
          br.close();
        }
      }catch(IOException ex){
        ex.printStackTrace();
      }
    }
}

Manifest

En Java, il est conseillé de remplir le manifeste de façon standard. Le SDK fournit des méthodes pour aller lire ce manifeste dans des packages.

Exemple de manifest java :
Manifest-version: 1.0
Implementation-Version: 1.2

Specification-Title: MaLibrary
Specification-Version: 1.2
Specification-Vendor: MaSociété
Implementation-Vendor: MaSociété
Implementation-Title: MaLibrary
Implementation-Version: 1.2
Build-date: 2009-09-07 19:01:52

Thread

Synchronisation
La mise en place d’un processus de synchronisation est tres gourmant en ressource. Du coup avant de se lancer dans la synchronisation, on doit se poser quelques questions :
  • A-t-on vraiment besoin d’avoir une méthode synchronisée ? (Utilisation de ressource synchrone ? Ordre d’exécution important ?...)
  • Cette méthode peut-elle être utilisée en multi-thread ?

Le mot clé « synchronized » en java permet de définir des sections critiques thread safe. Il est souvent utilisé avec la déclaration de la méthode d’une classe.

public synchronized void theMethod() {}

Cette écriture est équivalente à
public void theMethod() {
  synchronized (this) {
    …
  }
}

Plutôt que d’utiliser la synchronisation au niveau de la méthode, il est préférable de l’utiliser uniquement sur la section de la méthode qui nécessite un accès thread safe. D’ailleurs, rien n’oblige le développeur à utiliser « this » comme objet de synchronisation. On peut par exemple utiliser un singleton si l’on souhaite synchroniser plusieurs méthodes disséminées dans plusieurs objets.
Le principal reproche que l’on puisse faire à la technique du synchronized, c’est le manque de granularité. En effet, si un système multi thread tente de lire et de modifier une variable en même temps, le verrou sera le même pour la lecture ainsi que pour l’écriture, provoquant un goulot d’étranglement.

Pour palier a cela, il est possible d’utiliser des verrous d’écriture et de lecture, mais le code s’en retrouve complexifié.
Un verrou de lecture est différent d’un verrou d’écriture. Le verrou d’écriture doit attendre que les verrous de lecture soient tous levés pour être posé. Plusieurs verrous de lecture peuvent être posés en même temps alors qu’un seul verrou d’écriture ne peut être posé à un instant donné.

mercredi 21 avril 2010

Design Pattern Composite


Le pattern Composite est constitué d’objets ayant le même comportement sur une profondeur variable. Cela permet d’interagir sans avoir à connaître si on parle à un objet métier ou à un groupe.

Description du problème

Lorsque l’on travaille avec des données sous forme d’arbre, il est souvent nécessaire de différencier les branches des feuilles pour savoir quel traitement effectuer.

Pour illustrer le contexte, prenons un exemple :
Nous souhaitons faire une application de dessin qui manipule des objets « graphiques » de base (carré, cercle, triangle,...). L’utilisateur lui a besoin de formes plus complexes qui seront fait à partir de formes de base ou même d’assemblage d’autres formes complexes. Il est donc nécessaire que ces formes complexes se comportent de la même façon qu’une forme simple.

Si on utilise un arbre simple, la programmation devient compliquée car il faut parcourir entièrement cet arbre tout en différenciant les actions réalisées sur les branches et les actions réalisées sur les feuilles. Cela nécessite du code et généralement des méthodes récursives pour effectuer les opérations.

Ce pattern répond à ce type de problème.

Diagramme UML


Définition de la solution

Composant : C’est la classe abstraite pour tous les composants, y compris les Composés. Elle déclare les interfaces pour les « opérations » et définit les méthodes de manipulation des enfants.
Feuille : Représente les « feuilles » dans la composition. Elle met en œuvre toutes les méthodes « opération ».
Composé : Elle représente un composant composite (Pouvant avoir des enfants), elle implémente les méthodes pour la manipulation des enfants (ajout, suppression,…) ainsi que les méthodes « opérations »

Le pattern Composite permet au client de ne pas avoir à se soucier s’il interagi avec une feuille ou une branche (Composé).

Le composé s’occupera lui même de transmettre les demandes aux feuilles. On pourra même rajouter de l’intelligence au niveau de la branche.

Dans notre exemple, on pourrait très bien imaginer un calcul de volume : chaque feuille calcule son volume et le composé additionne tous ces volumes.

Le client exécute une « opération » sur un composé. Ce composé exécute un prétraitement (si besoin), parcoure tous ces fils en demandant l’exécution de la même opération, puis fait un post-traitement (si nécessaire).

Variante

L’élément « composant » peut être une simple interface. Mais alors il faut implémenter dans chaque feuille les méthodes pour la manipulation des fils.

Conséquences

L’arbre ainsi réalisé est fortement typé. Il ne peut donc pas être utilisé de façon générique mais facilite fortement le développement du « Client ».

L’interaction entre les branches et les feuilles en est aussi simplifié; les branches ne s’occupant que de retransmettre les ordres aux feuilles sans avoir à connaître leur comportement interne.

Ce design-paterne ne doit être utilisé que si les feuilles et les branches ont des opérations communes (déclaré dans la classe composant).

Exemple

mardi 20 avril 2010

La programmation orientée objet (POO)

La programmation orientée objet (POO) ou programmation par objet, est un paradigme de programmation informatique qui consiste en la définition et l’assemblage de briques logicielles appelées objets.

L’objet

Comme l’indique son nom, la composante principale de la Programmation Orientée Objet est l’objet. Un objet regroupe les données et les moyens de traitement de ces données.
Un objet rassemble de fait deux éléments :
  • Les attributs : ce sont les variables de l’objet : ce sont eux qui ont en charge les données à gérer. Dans la plupart des langages un attribut possède un type quelconque défini au préalable : nombre, caractère, ..., ou même un type objet.
  • Les méthodes : les méthodes sont les éléments d’un objet qui servent d’interface entre les données et le programme. Ce sont en fait les procédures ou fonctions destinées à traiter les données.

Objet et classe

L’autre notion importante qui est inséparable de l’Objet, c’est la notion de classe. L’objet est une instance de classe, plus simplement un exemplaire d’une classe, sa représentation en mémoire. Par conséquent, on déclare comme type une classe, et on déclare des variables de ce type appelées des objets.

Exemple de classe en Java :
public class MaClasse{
....
}

Exemple d’objet (et donc d’instanciation) en Java :
MaClasse monObject = new MaClasse();

Les 3 fondamentaux de la POO

La Programmation Orientée Objet est dirigée par 3 fondamentaux : encapsulation, héritage et polymorphisme.

Encapsulation
L’encapsulation permet de faire voir l’objet à l’extérieur comme une boîte noire ayant certaines propriétés (attributs et fonctions) et ayant un comportement spécifié. La manière dont ces propriétés ont été implémentées est alors cachée aux utilisateurs de la classe. L’implémentation peut être modifiée sans changer le comportement extérieur de l’objet. Cela permet donc de séparer la spécification du comportement d’un objet, de l’implémentation pratique de ces spécifications.

Héritage
L’héritage, permettant entre autres la réutilisabilité et l’adaptabilité des objets. Ce principe est basé sur des classes dont les "filles" héritent des caractéristiques de leur(s) "mère(s)". Chacune des classes filles peut donc posséder les mêmes caractéristiques que ses classes mères et bénéficier de caractéristiques supplémentaires à celles de ces classes mères. Bien sur, toutes les méthodes de la classe héritée (fille) peuvent être redéfinies. Chaque classe fille peut, si le programmeur n’a pas défini de limitation, devenir à son tour classe mère.

Polymorphisme
Cette capacité dérive directement du principe d’héritage vu précédemment. En effet, comme on le sait déjà, un objet va hériter des attributs et méthodes de ses ancêtres. Mais un objet garde toujours la capacité de pouvoir redéfinir une méthode. On voit donc apparaître ici ce concept de polymorphisme : choisir en fonction des besoins quelle méthode ancêtre appeler, et ce au cours même de l’exécution.

Visibilité

De par le principe de l’encapsulation, il convient de pouvoir masquer certaines données et méthodes internes les gérant, et de pouvoir laisser visibles certaines autres devant servir à la gestion publique de l’objet. C’est le principe de la visibilité.

Attributs et méthodes publics
Les attributs et méthodes dits publics sont accessibles par tous.

Attributs et méthodes privés
La visibilité privée restreint la portée d’un attribut ou d’une méthode à l’objet où il ou elle est déclaré(e).

Attributs et méthodes protégés
La visibilité protégée correspond à la visibilité privée excepté que tout attribut ou méthode protégé(e) est accessible dans tous les descendants (c’est à dire dans toutes les classes filles).

Différents types de méthodes

Voici la description de quelques méthodes particulières à la Programmation Orientée Objet.

Constructeurs et destructeurs
Parmi les différentes méthodes d’un objet se distinguent deux types de méthodes bien particulières et remplissant un rôle précis dans sa gestion : les constructeurs et les destructeurs.

Constructeurs
Comme leur nom l’indique, les constructeurs servent à construire l’objet en mémoire. Un constructeur va se charger de mettre en place les données, d’associer les méthodes avec les attributs et de créer le diagramme d’héritage de l’objet, autrement dit de mettre en place toutes les liaisons entre les ancêtres et les descendants. Il peut exister en mémoire plusieurs instances d’un même type objet, par contre seule une copie des méthodes est conservée en mémoire, de sorte que chaque instance se réfère à la même zone mémoire en ce qui concerne les méthodes. Bien entendu, les attributs sont distincts d’un objet à un autre.
Quelques remarques concernant les constructeurs :
  • Un objet peut ne pas avoir de constructeur explicite, il est alors créer par le compilateur.
  • Certains langages (comme le Java) autorise d’avoir plusieurs constructeurs : c’est l’utilisateur qui décidera du constructeur à appeler. Comme pour toute méthode, un constructeur peut être surchargé, et donc effectuer diverses actions en plus de la construction même de l’objet. On utilise ainsi généralement les constructeurs pour initialiser les attributs de l’objet.
  • S’il n’est pas nécessaire de fournir un constructeur pour un objet statique, il devient obligatoire en cas de gestion dynamique, car le diagramme d’héritage ne peut être construit de manière correcte que lors de l’exécution, et non lors de la compilation.
Destructeurs
Le destructeur est le pendant du constructeur : il se charge de détruire l’instance de l’objet. La mémoire allouée pour le diagramme d’héritage est libérée. Certains compilateurs peuvent également se servir des destructeurs pour éliminer de la mémoire le code correspondant aux méthodes d’un type d’objet si plus aucune instance de cet objet ne réside en mémoire.
Quelques remarques sur les destructeurs :
  • Tout comme pour les constructeurs, un objet peut ne pas avoir de destructeur. Une fois encore, c’est le compilateur qui se chargera de la destruction statique de l’objet.
  • Certains langages autorise d’avoir plusieurs destructeurs. Leur rôle commun reste identique, mais peut s’y ajouter la destruction de certaines variables internes pouvant différer d’un destructeur à l’autre. La plupart du temps, à un constructeur distinct est associé un destructeur distinct.
  • En cas d’utilisation dynamique, un destructeur s’impose pour détruire le diagramme créé par le constructeur.

Méthodes et classes abstraites

Méthodes abstraites
Une méthode abstraite est une méthode qu’il est nécessaire de surcharger. Elle ne possède pas d’implémentation. Ainsi, si on tente d’appeler une méthode abstraite, une erreur est déclenchée. Les méthodes abstraites sont généralement utilisées lorsque l’on bâtit un squelette d’objet devant donner lieu à de multiples descendants devant tous posséder un comportement analogue.

Classe abstraite
On retrouve le même principe au niveau de la classe. Une classe abstraite ne peut pas être instanciée. Par contre il est possible d’y mélanger des méthodes abstraites et concrète.

Interface
Une interface est un cas particulier de la classe abstraite car toutes ces méthodes sont abstraites. Seule l’interface de la classe apparaît.

Méthode et attribut statique

Les méthodes et attributs statiques sont dites d’instance ou de classe. Un attribut statique existe dès que sa classe est chargée, en dehors et indépendamment de toute instanciation. Quelque soit le nombre d’instanciation de la classe, un attribut de classe, existe en un et un seul exemplaire.
Une méthode statique ne doit pas manipuler, directement ou indirectement, d’attributs non statiques de sa classe.

Pointeur interne

Il peut se révéler indispensable pour un objet de pouvoir se référencer lui-même. Pour cela, toute instance dispose d’un pointeur interne vers elle-même. Ce pointeur peut prendre différentes appellations. En Java, c++, PHP il s’agira du pointeur "this".