Copyright (c) 1999 Scott Ambler, Ambysoft, Inc.

Les instructions de programmation Java sont fournies sous licence concédée par Scott Ambler, Ambysoft Inc., www.ambysoft.com. Elles ont été restructurées pour être ajoutées au Rational Unified Process.


Sommaire

1      Introduction
       
1.1    Instruction principale

2    Standards de codage
     
2.1    Conventions de dénomination
      2.2    Conventions de documentation
              2.2.1  Types de commentaires Java
              2.2.2  Présentation rapide de javadoc   

3    Standards des fonctions membres
     
3.1    Désignation de fonctions membres
              3.1.1    Désignation de fonctions membres accesseurs
                         3.1.1.1    Getters
                         3.1.1.2    Setters
      3.2    Désignation de constructeurs
      3.3    Visibilité des fonctions membres
      3.4    Documentation des fonctions membres
               3.4.1    En-tête de fonction membre
               3.4.2    Documentation interne
      3.5    Techniques pour l'écriture d'un code sain
               3.5.1    Documenter votre code
               3.5.2    Division du code en paragraphes ou mise en retrait
               3.5.3    Utilisation de blancs dans votre code
               3.5.4    Suivi de la règle des 30 secondes
               3.5.5    Ecriture de lignes de commande courtes sur une seule ligne
               3.5.6    Spécification de l'ordre des opérations

4    Standards des champs et des propriétés
      4.1    Désignation de champs
              4.1.1    Désignation de composants (widgets)
                         4.1.1.1    Alternative à la désignation de composants : la notation hongroise
                         4.1.1.2    Alternative à la désignation de composants : la notation hongroise suffixée
                         4.1.1.3    Définir des standards de noms de composant
              4.1.2    Désignation de constantes
               4.1.3    Désignation de collections
      4.2    Visibilité des champs
               4.2.1    Ne pas "masquer" les noms
      4.3    Documentation d'un champ
      4.4    Utilisation des fonctions membres accesseurs
              4.4.1    Pourquoi utiliser des accesseurs ?               
                          4.4.1.1   Quand ne pas utiliser d'accesseurs
              4.4.2    Désignation d'accesseurs
              4.4.3    Techniques avancées des accesseurs
                         4.4.3.1    Initialisation paresseuse
                         4.4.3.2    
Accesseurs de constantes
                         4.4.3.3    
Accesseurs de collections
                         4.4.3.4    Accès simultané à plusieurs champs   
      4.5    Visibilité des accesseurs
      4.6    Toujours initialiser les champs statiques

5    Standards des variables locales
      5.1    Désignation de variables locales
                  5.1.1    Désignation de flux
                   5.1.2    Désignation de compteurs de boucles
                   5.1.3    Désignation d'objets exceptions
        5.2   Déclaration et documentation de variables locales
               5.2.1     Généralités sur la déclaration

6    Standards des paramètres pour les fonctions membres
     
6.1    Désignation de paramètres
      6.2    Documentation de paramètres
               6.2.1    Utilisation d'interfaces pour les types de paramètres

7    Standards des classes, interfaces, packages et des unités de compilation
      7.1    Standards des classes
                7.1.1    Désignation de classes
                7.1.2    Documentation d'une classe
                7.1.3    Déclarations de classes
                7.1.4    Réduction de l'interface publique et protégée
                            7.1.4.1    Définir d'abord l'interface publique
      7.2     Standards des interfaces
                7.2.1    Désignation d'interfaces
                            7.2.1.1    Alternative
                7.2.2    Documentation d'interfaces
      7.3     Standards des packages
                7.3.1    Désignation de packages
                7.3.2    Documentation de packages
      7.4     Standards des unités de compilation
                7.4.1    Désignation d'unités de compilation
                7.4.2    Documentation d'unités de compilation

8    Traitement des erreurs et exceptions

9    Problèmes et standards divers
      9.1    Réutilisation
      9.2    Importation de classes
      9.3    Optimisation du code Java
      9.4    Ecriture de routines de test Java

10    Patterns de succès
       10.1    Utilisation efficace de ces standards
       10.2    Autres facteurs conduisant à l'écriture d'un code réussi

11    Récapitulatif
       
11.1    Conventions de dénomination Java
        11.2    Conventions de documentation Java
                   11.2.1    Types de commentaires Java
                   11.2.2    Que documenter ?
        11.3    Conventions de codage Java (général)

12    Références

13    Glossaire


1    Introduction

Ce document décrit un ensemble de standards, de conventions et d'instructions pour l'écriture d'un code Java sain. Ils sont basés sur les principes avérés et solides du génie logiciel, produisant un code facile à comprendre, à gérer et à étendre. En outre, en suivant ces standards de codage, votre productivité en tant que développeur Java, s'accroît considérablement. L'expérience montre qu'en prenant le temps d'écrire un code de grande qualité dès le début, il sera alors plus aisé de le modifier lors du processus de développement. Enfin, le suivi d'un ensemble de standards de codage généraux conduit à une cohérence plus importante, rendant les équipes de développeurs considérablement plus productives.

1.1    Instruction principale 

Utilisez le sens commun. Lorsque vous ne trouvez aucune règle ou instruction, qu'une règle ne s'applique manifestement pas ou que toute autre solution échoue : utilisez le sens commun et examinez les principes fondamentaux. Cette règle l'emporte sur toutes les autres. Le sens commun est obligatoire.


2    Standards de codage

Les standards de codage en Java sont importants car ils conduisent à une plus grande cohérence dans votre code et celui de vos collaborateurs. Une plus grande cohérence entraîne un code plus facile à comprendre, et donc à développer et à gérer. Cela réduit le coût total des applications que vous créez.

Souvenez-vous que votre code Java va longtemps exister, bien après que vous soyez passé à d'autres projets. Lors du développement, il est important de vous assurer que vous pouvez transmettre votre travail à un autre développeur, ou à une autre équipe de développeurs afin qu'ils puissent continuer à le gérer et à l'étendre, sans avoir à fournir un effort démesuré pour comprendre votre code. Un code difficile à comprendre risque d'être abandonné et réécrit.

2.1    Conventions de dénomination

Les conventions de dénomination sont traitées tout au long des standards. Voici quelques points fondamentaux :

  1. Utilisez des libellés en anglais et en langage clair décrivant précisément la variable, le champ et la classe. Par exemple, choisissez des noms tels que firstName, grandTotal ou CorporateCustomer. Même si les noms tels que x1, y1 ou fn sont faciles à taper puisqu'ils sont courts, ils ne fournissent aucune indication sur ce qu'ils représentent, ce qui rend le code difficile à comprendre, à gérer et à étendre.
  2. Utilisez la terminologie s'appliquant au domaine. Si vos utilisateurs mentionnent leurs clients en tant que customers, utilisez alors le terme Customer pour la classe, pas Client. Beaucoup de développeurs font l'erreur de créer des termes génériques à des concepts alors que des termes parfaitement corrects existent déjà dans le domaine ou le secteur d'activité.
  3. Utilisez la casse mixte pour rendre les noms lisibles. En général ,utilisez des minuscules, mais la première lettre des noms de classe et des noms d'interface doit être en majuscule, ainsi que la première lettre d'un mot non initial. [KAN97]
  4. Utilisez les abréviations avec parcimonie et intelligence. Vous devez donc gérer une liste de formes courtes standards (abréviations). Choisissez-les avec sagesse, et utilisez-les systématiquement. Par exemple, si vous voulez utiliser une forme courte pour le mot "number", choisissez alors nbr, no ou num. Documentez celui que vous avez choisi (peu importe lequel), et n'utilisez que celui-ci.
  5. Evitez les noms longs (un nom < 15 caractères est approprié). Même si le nom de classe PhysicalOrVirtualProductOrService peut sembler bon au moment de sa création, ce nom est trop long et vous devriez penser à le renommer en un nom plus court comme Offering.
  6. Evitez les noms similaires ou qui ne diffèrent que par la casse. Par exemple, les noms de variable persistentObject et persistentObjects ne doivent pas être utilisés ensemble, ni anSqlDatabase et anSQLDatabase.
  7. Evitez les traits de soulignements en début ou en fin de mot. Les traits de soulignements en début ou en fin de mot sont généralement réservés aux systèmes et ne doivent pas être utilisés pour les noms créés par l'utilisateur. Plus important, les traits de soulignement sont exaspérants et difficiles à taper, aussi, évitez autant que possible de les utiliser.

2.2    Conventions de documentation

Ce document traite également les conventions de documentation dont voici les points essentiels :

  1. Les commentaires doivent clarifier votre code. La documentation de votre code le rend plus compréhensible pour vous, vos collaborateurs et tout développeur passant après vous.
  2. Si votre programme n'est pas bien documenté, il ne fonctionne probablement pas correctement. [NAG95]
  3. Evitez la décoration : n'utilisez pas de commentaires en forme de bannière. Dans les années 60 et 70, les programmeurs COBOL avait l'habitude de dessiner des encadrés avec des astérisques, autour de leurs commentaires internes. Evidemment, cela leur permettait d'exprimer leurs talents artistiques, mais ce n'était franchement qu'une immense perte de temps n'ajoutant que peu de valeur au produit final. Vous voulez écrire un code sain, pas un joli code. En outre, puisque de nombreuses polices de caractères utilisées pour afficher et imprimer votre code sont proportionnelles, et que beaucoup ne le sont pas, de toute façon, vous ne pouvez pas aligner vos encadrés correctement.
  4. Conservez des commentaires simples. Quelques-uns des meilleurs commentaires sont des notes abrégées et simples. Vous n'avez pas à écrire un livre, vous devez simplement fournir suffisamment d'informations pour que les autres personnes puissent comprendre votre code.
  5. Ecrivez la documentation avant d'écrire le code. La meilleure façon de documenter un code est d'écrire d'abord les commentaires. Cette méthode vous donne l'opportunité de penser au fonctionnement du code avant de l'écrire et assure que la documentation sera bien écrite. Autrement, vous devez au moins documenter votre code tout en l'écrivant. Puisque la documentation rend votre code plus facile à comprendre, vous pouvez en tirer parti au moment de le développer. Si vous devez passer du temps à écrire la documentation, autant en tirer quelque chose. [AMB98]
  6. Documentez pourquoi quelque chose est fait, pas seulement ce qui est fait. Par exemple, le code dans l'exemple 1 ci-dessous montre qu'une remise de 5% est offerte aux commandes d'au moins 1 000 dollars. Pour quelle raison ? Existe t'il une règle commerciale indiquant que les commandes importantes ont une remise ? S'agit-il d'une offre limitée dans le temps ou d'un programme permanent ? Le premier programmeur était-il juste généreux ? Vous ne le savez pas, à moins que ce soit expliqué quelque part, dans le code source lui-même ou dans un document externe.

Exemple 1 :

if ( grandTotal >= 1000.00)

{

grandTotal = grandTotal * 0.95;

}

2.2.1    Types de commentaires Java

Java a trois styles de commentaires : 

Le tableau suivant récapitule les suggestions d'utilisation pour chaque type de commentaire, et présente quelques exemples.

Type de commentaire Utilisation Exemple
Documentation Utilisez les commentaires de documentation juste avant les déclarations des interfaces, des classes, des fonctions membre et des champs pour les documenter. Les commentaires de documentation sont traités par javadoc, voir ci-dessous, pour la création de documentation externe d'une classe. /**
Customer (client) : un client est une personne ou organisation auquelle nous vendons des services et des produits.
@author S.W. Ambler
*/
Style C Utilisez des commentaires de style C pour documenter les lignes de code qui ne sont plus applicables, mais que vous souhaitez conserver au cas où vos utilisateurs changent d'avis, ou que vous voulez provisoirement mettre hors fonction pendant un débogage. /*
Ce code a été commenté par B. Gustafsson, le 4 juin 1999 car il a été remplacé par le code précédent. Supprimez-le deux ans après s'il n'est toujours pas applicable.
. . . (le code source)
*/
Une seule ligne Utilisez des commentaires sur une seule ligne au sein des fonctions membres pour documenter la logique applicative, les sections de code et les déclarations des variables temporaires. // Appliquez une remise de 5 % à toutes
// les factures de plus de 1 000 $ en raison de
// la campagne de générosité démarrée en
// février 1995.

Il est important que votre organisation définisse un standard sur la manière dont les commentaires de style C et les commentaires sur une seule ligne doivent être utilisés, afin de suivre ce standard systématiquement. Utilisez un type pour documenter la logique applicative et utilisez l'autre pour documenter le code antérieur. Utilisez des commentaires sur une seule ligne pour la logique applicative, étant donné que vous pouvez placer la documentation sur la même ligne que le code (il s'agit de la mise en ligne). Utilisez des commentaires de style C pour documenter le code antérieur car cela permet de commenter plusieurs lignes à la fois. Le style C paraissant très similaire aux commentaires de documentation, pour éviter toute confusion, ne les utilisez pas ensemble.

Faites attention aux commentaires de fin de ligne. [MCO93] est fortement contre l'utilisation de commentaires sur une seule ligne, ou commentaires de fin de ligne. McConnell indique que les commentaires doivent être alignés à droite du code afin qu'ils n'interfèrent pas avec la structure visuelle du code. Par conséquent, ils ont tendance à être difficiles à mettre en forme et "si vous utilisez plusieurs d'entre eux, il faut du temps pour les aligner. Ce temps n'est pas dédié à apprendre plus sur le code, mais seulement à appuyer fastidieusement sur la barre d'espacement ou la touche de tabulation." Il montre également que les commentaires de fin de ligne sont difficiles à gérer car lorsque le code s'étend sur la ligne, il les repousse, et si vous les alignez, vous devez faire de même pour tout le reste.

2.2.2        Présentation rapide de javadoc

Dans le kit JDK de Sun, un programme intitulé javadoc traite les fichiers de code Java et produit une documentation externe, sous forme de fichiers HTML, pour vos programmes Java. Javadoc prend en charge un nombre restreint de balises (mots clés marquant le début de la section de documentation). Pour plus d'informations, reportez-vous à la documentation du kit JDK javadoc.

Balise Utilisée pour Objet
@author name Classes, interfaces Indique le ou les auteur(s) d'une partie d'un code. Une balise doit être utilisée pour chaque auteur.
@deprecated Classes, fonctions membres Indique que l'interface de programmation de la classe est obsolète et qu'il ne faut donc plus l'utiliser.
@exception name description Fonctions membres Décrit les exceptions émises par une fonction membre. Utilisez une balise par exception et donnez le nom complet de la classe à l'exception.
@param name description Fonctions membres Utilisé pour décrire un paramètre passé à une fonction membre, comprenant son type ou sa classe et son utilisation. Utilisez une balise par paramètre.
@return description Fonctions membres Décrit la valeur de retour, s'il y en a une, d'une fonction membre. Indiquez le type ou la classe et le(s) l'utilisation(s) potentielle(s) de la valeur de retour.
@since Classes, fonctions membres Spécifie depuis combien de temps l'élément existe, c'est-à-dire depuis JDK 1.1.
@see ClassName Classes, interfaces, fonctions membres, champs Génère un lien hypertexte dans la documentation vers la classe spécifiée. Vous pouvez, et devez probablement, utiliser un nom de classe qualifié complet.
@see ClassName#member functionName Classes, interfaces, fonctions membres, champs Génère un lien hypertexte dans la documentation vers la fonction membre spécifiée. Vous pouvez, et devez probablement, utiliser un nom de classe qualifié complet.
@version text Classes, interfaces Indique les informations sur la version pour un élément de code donné.

La manière dont documentez votre code a un impact très important sur votre propre productivité comme sur celle de toute personne qui le gérera et l'étendra par la suite. En documentant votre code précocement lors du processus de développement, vous devenez plus productif car cela vous oblige à examiner votre logique en détail avant de la soummettre au code. En outre, lorsque vous revisitez le code que vous avez écrit quelques jours ou quelques semaines plus tôt, vous pouvez facilement déterminer ce à quoi vous pensiez lors de son écriture puisqu'il est déjà documenté pour vous.


3    Standards des fonctions membres

N'oubliez jamais que le code que vous écrivez aujourd'hui sera peut-être toujours utilisé dans plusieurs années, et sera probablement géré et étendu par quelqu'un d'autre. Efforcez-vous à rendre votre code aussi "sain" et compréhensible que possible, car ces facteurs le rendent plus facile à gérer et à étendre.

3.1    Désignation de fonctions membres

Les fonctions membres doivent être désignées en utilisant une description en anglais et en langage clair, avec une casse mixte, et la première lettre de chaque mot non initial doit être en majuscule. Le premier mot d'un nom de fonction membre est souvent un verbe actif et fort.

Exemples :

openAccount()

printMailingLabel()

save()

delete()

Cette convention crée des fonctions membres dont l'objectif peut souvent être déterminé en lisant simplement leurs noms. Même si cette convention oblige le développeur à taper plus de texte, puisque les noms sont plus longs, c'est plus que compensé par l'accroissement de la clarté de votre code.

3.1.1    Désignation de fonctions membres accesseurs

Les accesseurs sont des fonctions membres consultant et modifiant les valeurs des champs (des champs ou des caractéristiques). Nous traiterons des accesseurs de manière détaillée dans un prochain chapitre. Les conventions de dénomination des accesseurs sont toutefois récapitulées ci-dessous.

3.1.1.1    Getters 

Les getters sont des fonctions membres retournant la valeur d'un champ. Préfixez le mot "get" au nom du champ. S'il est booléen, préfixez "is" au nom du champ au lieu de "get".

Exemples :

getFirstName()

getAccountNumber()

isPersistent()

isAtEnd()

En suivant cette convention de dénomination, vous constatez qu'une fonction membre retourne un champ d'un objet, et true (vrai) ou false (faux) pour les getters booléens. Autre avantage de ce standard : il suit les conventions de dénomination utilisées par le Beans Development Kit (BDK) pour les fonctions membres getters. [DES97] Le principal inconvénient est que "get" est superflu et oblige à taper plus de mots.

Convention de dénomination alternative pour les getters : has et can

Voici une alternative viable, basée sur les conventions anglaises : l'utilisation du préfixe "has" ou "can" au lieu de "is" pour les getters booléens. Par exemple, les noms de getter tels que hasDependents() et canPrint() sont plein de sens lors de la lecture du code. Le problème de cette méthode est que le BDK ne reprend rien de cette stratégie de dénomination (pas encore). Vous pouvez renommer ces fonctions membres isBurdenedWithDependents() et isPrintable().

3.1.1.2    Setters

Les setters, ou mutateurs, sont des fonctions membres qui modifient les valeurs d'un champ. Préfixez le mot "set" au nom du champ, quel que soit le type de champ.

Exemples :

setFirstName(String aName)

setAccountNumber(int anAccountNumber)

setReasonableGoals(Vector newGoals)

setPersistent(boolean isPersistent)

setAtEnd(boolean isAtEnd)

En suivant cette convention de dénomination, vous constatez qu'une fonction membre modifie la valeur d'un champ d'un objet. Autre avantage de ce standard : il suit les conventions de dénomination utilisées par le BDK pour les fonctions membres setters. [DES97] Le principal inconvénient est que "set" est superflu et oblige à taper plus de lettres.

3.2    Désignation de constructeurs

Les constructeurs sont des fonctions membres exécutant toute initialisation nécessaire lorsqu'un objet est créé pour la première fois. Les constructeurs reçoivent toujours le même nom que leur classe. Par exemple, un constructeur de la classe Customer sera Customer(). La même casse est utilisée.

Exemples :

Customer()

SavingsAccount()

PersistenceBroker()

Cette convention de dénomination est définie par Sun Microsystems et doit être scrupuleusement respectée.

3.3    Visibilité des fonctions membres

Pour une conception optimale, avec un couplage faible entre classes, il faut généralement appliquer le plus de restriction possible sur la visibilité d'une fonction membre. Si une fonction membre ne doit pas être publique, alors définissez-la sur protégée, et si elle n'a pas besoin d'être protégée, rendez-la privée.

Visibilité Description Utilisation correcte
publique Une fonction membre publique peut être appelée par toute autre fonction membre dans n'importe quel autre objet ou classe. Lorsque la fonction membre doit être accessible aux objets et aux classes en dehors de la hiérarchie de classe dans laquelle la fonction membre est définie.
protégé Une fonction membre protégée peut être appelée par toute autre fonction membre de la classe dans laquelle elle est définie, par les sous-classes de cette classe, ou par les classes dans le même package. Lorsque la fonction membre fournit un comportement nécessaire au sein du package ou de la hiérarchie de classe, mais pas à l'extérieur.
privé Une fonction membre privée ne peut être appelée que par d'autres fonctions membres de la classe dans laquelle elle est définie, mais pas dans les sous-classes. Lorsque la fonction membre fournit un comportement spécifique à la classe. Les fonctions membres privées sont souvent le résultat d'une restructuration, ou réorganisation, du comportement des autres fonctions membres dans la classe, pour encapsuler un comportement spécifique.
par défaut Par défaut (aucune visibilité spécifiée), une fonction peut seulement être appelée par d'autres fonctions membres de la classe dans laquelle elle est définie, ou toute classe dans le même package. Lorsque la fonction membre fournit un comportement nécessaire aux classes au sein du même package, mais pas à l'extérieur, et pas aux sous-classes.

3.4    Documentation des fonctions membres

La manière dont vous documentez une fonction membre sera souvent le facteur déterminant de sa clarté, et donc de sa gestion et de son extensibilité.

3.4.1    En-tête de fonction membre

Chaque fonction membre Java doit comporter une sorte d'en-tête, appelée documentation de fonction membre, en haut du code source, qui documente toutes les informations indispensables à sa compréhension. Ces informations comportent, mais ne se limitent pas aux éléments suivants :

  1. Ce que fait la fonction membre et pourquoi. En documentant ce que fait une fonction membre, vous facilitez aux autres le choix de réutiliser ou non votre code. Le code est plus facile à mettre en contexte s'il est documenté sur la raison de son action. Les autres personnes peuvent également déterminer facilement si une nouvelle modification doit être apportée à une partie du code (la raison de la nouvelle modification peut entrer en conflit avec la raison qui a entraînée l'écriture du code).
  2. Les paramètres qui doivent être passés à une fonction membre. Vous devez également indiquer quels paramètres, s'il y en a, doivent être passés à une fonction membre et comment ils vont être utilisés. Ces informations sont nécessaires aux autres programmeurs, pour qu'ils sachent quelles informations passer à une fonction membre. La balise javadoc @param, traitée dans la section 2.2.2, Présentation rapide de javadoc, est utilisée à cet effet.
  3. Ce que retourne une fonction membre. Vous devez documenter ce que retourne (s'il y en a) une fonction membre afin que les autres programmeurs puissent utiliser la valeur de retour ou l'objet de manière appropriée. La balise javadoc @return, traitée dans la section 2.2.2, Présentation rapide de javadoc, est utilisée à cet effet.
  4. Bogues connus. Tout problème important concernant une fonction membre doit être documenté afin que les autres développeurs comprennent les faiblesses et les difficultés de la fonction membre. Si un bogue donné est applicable à plus d'une fonction membre dans une classe, il doit alors être plutôt documenté pour la classe.
  5. Exceptions émises par une fonction membre. Documentez les exceptions émises par une fonction membre afin que les autres programmeurs sachent ce que leur code doit intercepter. La balise javadoc @exception, traitée dans la section 2.2.2, Présentation rapide de javadoc, est utilisée à cet effet.
  6. Décisions de visibilité. Si vous sentez que votre choix de visibilité d'une fonction membre sera remis en question par d'autres développeurs (vous avez peut-être rendu une fonction membre publique même si aucun autre objet n'appelle encore la fonction membre), documentez votre décision. Les autres développeurs comprendront alors votre décision et ne perdront pas de temps à se demander pourquoi vous l'avez prise.
  7. Comment une fonction membre modifie l'objet. Si une fonction membre change un objet, il faut l'indiquer, par exemple la fonction membre withdraw() d'un compte en banque modifie le solde du compte. Ces informations sont nécessaires pour que les autres programmeurs Java sachent exactement comment l'appel d'une fonction membre touche l'objet cible.
  8. Evitez l'utilisation d'en-têtes contenant des informations telles que l'auteur, les numéros de téléphone, les dates de création et de modification et la localisation de l'unité (ou nom de fichier), car ces informations sont rapidement obsolètes. Placez les notices de copyright et de propriété à la fin de l'unité. Par exemple, les lecteurs ne veulent pas avoir à parcourir deux ou trois pages de texte qui ne soient pas utiles à la compréhension du programme, ni parcourir un texte ne comportant aucune information sur le programme, telle que la notice de copyright. Evitez l'utilisation de barres verticales, de cadres ou de cases fermés, qui n'ajoutent que de la nuisance visuelle et restent difficilement uniformes. Utilisez un outil de gestion de configuration pour conserver un historique des unités.
  9. Exemples sur la manière d'appeler la fonction membre, si besoin. L'une de façons les plus simples de présenter le fonctionnement d'un élément de code est d'observer un exemple. Pensez à inclure un exemple ou deux sur la manière d'appeler une fonction membre.
  10. Préconditions et postconditions applicables. Une précondition est une contrainte sous laquelle une fonction membre va fonctionner correctement, et une postcondition est une caractéristique ou une assertion qui sera vérifiée après la fin du fonctionnement d'une fonction membre. [MEY88] A bien des égards, les préconditions et les postconditions décrivent les suppositions que vous avez faites en écrivant une fonction membre [AMB98], définissant exactement les limites d'utilisation d'une fonction membre.
  11. Tous les problèmes d'accès concurrent. L'accès concurrent est un nouveau concept complexe pour beaucoup de développeurs. En fait, il s'agit au mieux d'un sujet ancien et complexe pour les programmeurs d'accès concurrent expérimentés. Aussi, si vous utilisez les fonctions de programmation Java d'accès concurrent, documentez-le entièrement. [LEA97] suggère que lorsqu'une classe comprend des fonctions membres synchronisées et désynchronisées, vous devez documenter le contexte d'exécution sur lequel repose une fonction membre, particulièrement lorsqu'elle nécessite un accès libre pour que les autres développeurs puissent utiliser vos fonctions membres sans risque. Lorsqu'un setter (fonction membre mettant à jour un champ) d'une classe qui implémente l'interface Runnable n'est pas synchronisé, vous devez alors en documenter la raison. Enfin, si vous annulez ou surchargez une fonction membre et modifiez sa synchronization, vous devez également en documenter la raison.
  12. Ne documentez que ce qui ajoute de la clarté à votre code. Ne documentez pas tous les facteurs décrits ci-dessus pour chacune des fonctions membres puisque tous les facteurs ne sont pas applicables à chaque fonction membre. Par contre, vous documenterez certains d'entre eux pour chaque fonction membre que vous avez écrite.

3.4.2    Documentation interne

En plus de la documentation sur les fonctions membres, vous devez également inclure des commentaires au sein de vos fonctions membres pour décrire votre travail. L'objectif est de rendre votre fonction membre plus facile à comprendre, à gérer et à étendre.

Il existe deux types de commentaires à utiliser pour documenter la structure interne de votre code : les commentaires de style C ( /* et */ ) et les commentaires sur une seule ligne ( // ). Comme traité précédemment, vous devez sérieusement penser à choisir un style de commentaires pour la documentation de la logique applicative de votre code et un autre pour commenter le code inutile. Il est recommandé d'utiliser des commentaires sur une seule ligne pour votre logique applicative, car vous pouvez utiliser ce style de commentaire pour des lignes entières de commentaires et pour les commentaires en ligne qui suivent à la fin d'une ligne de code. Utilisez les commentaires de style C pour documenter les lignes de code inutile car il est plus facile de retirer plusieurs lignes avec un seul commentaire. En outre, puisque les commentaires de style C ressemblent tant aux commentaires de documentation, leur utilisation peut être confuse, ce qui soustrait à la clarté de votre code. Aussi, utilisez-les avec parcimonie.

En interne, documentez toujours les éléments suivants :

  1. Les structures de contrôle. Décrivez chaque structure de contrôle : par exemple les boucles et les instructions de comparaison. Vous ne devez pas avoir à lire le code en entier dans une structure de contrôle pour déterminer ce qu'il fait, mais juste avoir à lire une ou deux lignes de commentaires le précédant immédiatement.
  2. Ce que fait le code et pourquoi. Vous pouvez toujours regarder une partie de code et comprendre sa fonction, mais pour un code qui n'est pas évident, vous pouvez rarement déterminer pourquoi il a été fait de cette manière. Par exemple, vous pouvez regarder une ligne de code et comprendre facilement qu'une remise de 5% est appliquée à la somme totale d'une facture. C'est simple. Ce qui ne l'est pas, c'est de comprendre POURQUOI cette remise est appliquée. Bien évidemment, il existe une règle commerciale préconisant l'application de la remise, ainsi, vous pouvez y faire référence dans votre code afin que les autres développeurs comprennent pourquoi votre code fait ce qu'il fait.
  3. Les variables locales. Bien que nous expliquerons cela plus clairement dans le Chapitre 5, notez que chaque variable locale définie dans une fonction membre doit être déclarée dans sa propre ligne de code and doit généralement avoir un commentaire en ligne décrivant sont utilisation.
  4. Un code difficile ou complexe. Si vous pensez, soit que vous ne pouvez pas le réécrire, soit que vous n'en avez pas le temps, vous devez alors documenter entièrement tout code complexe dans une fonction membre. En règle générale, si votre code n'est pas évident, vous devez alors le documenter.
  5. L'ordre de traitement. Si des instructions de votre code doivent être exécutées dans un ordre défini, assurez-vous alors que cela est documenté [AMB98]. Il n'y a rien de pire que d'apporter une simple modification à une partie de code pour découvrir qu'il ne fonctionne plus, puis de passer des heures à chercher le problème et découvrir enfin que plusieurs choses sont en dérangement.
  6. Documentez vos accolades fermantes. Vous trouverez occasionnellement des structures de contrôle dans des structures de contrôle dans des structures de contrôle. Bien que vous devriez éviter d'écrire un code de cette manière, parfois, c'est la meilleure solution. Le problème vient du fait qu'il devient difficile de savoir à quelle accolade fermante appartient le caractère }, à quelle structure de contrôle. La bonne nouvelle, c'est que certains éditeurs de code prennent en charge une fonction qui, lorsque vous sélectionnez une accolade ouvrante, met automatiquement en évidence l'accolade fermante correspondante. La mauvaise nouvelle, c'est que tous les éditeurs de code ne la prennent pas en charge. J'ai remarqué que le marquage des accolades fermantes avec un commentaire en ligne tel que //end if, //end for, //end switch rend le code plus facile à comprendre.

3.5    Techniques pour l'écriture d'un code sain

Cette section traite des diverses techniques aidant à distinguer les développeurs professionnels des bidouilleurs. Ces techniques sont :

3.5.1    Documenter votre code 

Rappelez-vous que si votre code n'est pas correctement documenté, il ne vaut mieux pas le conserver.[NAG95] Lorsque vous appliquez correctement les instructions et les standards de documentation proposés dans ce document, vous pouvez fortement améliorer la qualité de votre code.

3.5.2    Division du code en paragraphes ou mise en retrait 

Il est possible d'améliorer la lisibilité d'une fonction membre en la divisant en paragraphes ou en mettant votre code en retrait dans la portée d'un bloc de code. Tout code dans des accolades (les caractères { et }) forme un bloc. L'idée de base est que le code au sein d'un bloc doit être uniformément mis en retrait d'une unité.

D'après la convention Java, l'accolade ouvrante doit être placée sur la ligne suivant le propriétaire du bloc et l'accolade fermante doit être mise en retrait d'un niveau. L'essentiel, souligné par [LAF97], est que votre organisation choisisse un style de retrait et s'y tienne. Utilisez le même style de retrait que celui qu'utilise votre environnement de développement Java pour le code qu'il génère.

3.5.3    Utilisation de blancs dans votre code 

Quelques interlignes, appelés blancs, ajoutés à votre code Java peuvent le rendre plus lisible en le divisant en petites sections faciles à lire. [VIS96] suggère l'utilisation d'un seul interligne pour séparer les groupes logiques de code, tels que les structures de contrôle, et de deux interlignes pour séparer les définitions des fonctions membres. Sans blanc, le code est très difficile à lire et à comprendre.

3.5.4    Suivi de la règle des 30 secondes 

En regardant votre fonction membre, les autres programmeurs doivent être capable de comprendre tout à fait ce qu'elle fait, pourquoi elle le fait et comment elle le fait, en moins de 30 secondes. Si c'est impossible, c'est que votre code est trop difficile à gérer, il faut donc l'améliorer. Trente secondes, c'est tout. En règle générale, si une fonction membre dépasse l'écran, c'est qu'elle est trop longue.

3.5.5    Ecriture de lignes de commande courtes sur une seule ligne 

Votre code doit faire une seule chose par ligne. A l'époque des cartes à perforer, il était normal d'essayer de mettre le maximum de fonctionnalité sur une seule ligne de code. Lorsque vous essayez de faire plus d'une chose sur une seule ligne de code, vous la rendez plus difficile à comprendre. Pourquoi faire ainsi ? Nous souhaitons clarifier notre code afin qu'il soit facile à gérer et à étendre. Tout comme une fonction membre ne doit faire qu'une seule chose à la fois, vous ne devez faire qu'une chose dans une seule ligne de code.

En outre, vous devez écrire un code qui reste visible à l'écran [VIS96]. Vous ne devez pas avoir à faire défiler votre fenêtre d'édition vers la droite pour lire la ligne de code en entier, y compris pour un code utilisant des commentaires en ligne.

3.5.6    Spécification de l'ordre des opérations 

Voici une manière très simple d'améliorer la clarté de votre code : l'utilisation de parenthèses pour indiquer l'ordre exacte des opérations dans votre code Java [NAG95] et [AMB98]. Si vous devez connaître l'ordre des opérations d'un langage pour comprendre votre code source, c'est qu'il y a un sérieux problème. Il s'agit principalement d'un problème de comparaisons logiques où vous reliez plusieurs autres comparaisons par les opérateurs logiques ET et OU. Si vous utilisez des lignes de commandes courtes sur une seule ligne, comme conseillé ci-dessus, alors cela ne doit vraiment pas poser de problème.


4    Standards des champs et des propriétés

Le terme champ utilisé ici fait référence à un champ que le BDK appelle une propriété [DES97]. Un champ est un élément de donnée décrivant un objet ou une classe. Les champs doivent être des types de données de base, comme une chaîne ou une variable flottante, ou être un objet, comme un client ou un compte bancaire.

4.1    Désignation de champs

Utilisez un libellé en anglais et en langage clair pour désigner vos champs, [GOS96] et [AMB98], ce que représente le champ devient ainsi évident. Les champs qui sont des collections, telles que des tableaux ou des vecteurs, doivent avoir des noms au pluirel pour indiquer qu'ils représentent des valeurs multiples.

Exemples :

firstName

zipCode

unitPrice

discountRate

orderItems

4.1.1    Désignation de composants (widgets)

Pour les noms de composants (widgets d'interface), utilisez un libellé en anglais et en langage clair, suffixé par le type de widget. Cela vous permet d'identifier facilement l'objet du composant et son type. Vous pouvez alors trouver chaque composant dans une liste. De nombreux environnements de programmation visuelle fournissent des listes de tous les composants dans un applet ou une application. Si tout est intitulé bouton1, bouton2, et ainsi de suite, cela peut créer de la confusion.

Exemples :

okButton

customerList

fileMenu

newFileMenuItem

4.1.1.1    Alternative à la désignation de composants : la notation hongroise 

La "notation hongroise" [MCO93] est basée sur le principe qu'un champ doit être désigné à l'aide de la méthode suivante : xEeeeeeEeeeee où x indique le type de composant et EeeeeEeeeee est le libellé en anglais et en langage clair.

Exemples :

pbOk

lbCustomer

mFile

miNewFile

Le principal avantage est qu'il s'agit d'un standard métier commun pour le code C++. De nombreuses personnes le suivent donc déjà. A partir du nom de la variable, les développeurs peuvent rapidement deviner son type et la manière dont elle est utilisée. Les inconvénients principaux sont que la notation avec préfixe devient encombrante lorsque vous avez beaucoup de widgets du même type et que vous ne respectez pas la convention de dénomination consistant à fournir des libellés en anglais et en langage clair.

4.1.1.2    Alternative à la désignation de composants : la notation hongroise suffixée 

Il s'agit essentiellement d'une combinaison des deux autres alternatives conduisant à des noms tels que okPb, customerLb, fileM et newFileMi. Le principal avantage est que le nom du composant indique le type de widget et que les widgets du même type ne sont pas rassemblés dans une liste alphabétique. L'inconvénient est que vous n'utilisez toujours pas de description en anglais et en langage clair, ce qui fait qu'il est plus difficile de se souvenir du standard puisqu'il s'écarte de la norme.

4.1.1.3    Définir des standards de noms de composant 

Quelle que soit la convention choisie, vous voudrez créer une liste de noms de widgets "officiels". Par exemple, lors de la désignation des boutons, utilisez-vous Button ou PushButton, b ou pb ? Créez une liste et mettez-la à la disposition des développeurs Java de votre organisation.

4.1.2    Désignation de constantes

Dans Java, les constantes (valeurs qui ne changent pas) sont habituellement implémentées en tant que champs finaux statiques des classes. La convention reconnue consiste à utiliser des mots en anglais et en langage clair, en majuscules, avec des traits de soulignements entre les mots [GOS96].

Exemples :

MINIMUM_BALANCE

MAX_VALUE

DEFAULT_START_DATE

Le principal avantage de cette convention est qu'elle vous aide à distinguer les constantes des variables. Nous verrons un peu plus loin dans ce document que vous pouvez largement accroître la flexibilité et la maintenabilité de votre code en ne définissant pas de constantes. Définissez plutôt des fonctions membres getters retournant la valeur des constantes.

4.1.3    Désignation de collections

Une collection, telle qu'un tableau ou un vecteur, doit se voir attribuer un nom au pluriel représentant les types d'objets stockés par le tableau. Le nom doit être un libellé en anglais et en langage clair, avec la première lettre des mots non initiaux en majuscule.

Exemples :

customers

orderItems

aliases

Le principal avantage de cette convention est qu'elle vous aide à distinguer les champs représentant des valeurs multiples (collections) de celles qui représentent des valeurs uniques (qui ne sont pas des collections).

4.2    Visibilité des champs

Lorsque les champs sont déclarés protégés, les fonctions membres des sous-classes peuvent y accéder directement, ce qui augmente le couplage au sein d'une hiérarchie de classe. Il faut donc l'éviter puisque cela rend vos classes plus difficiles à gérer et à étendre. Les champs ne devraient jamais être accédés directement, il est préférable d'utiliser des fonctions membres accesseurs (voir ci-dessous).

Visibilité Description Utilisation correcte
publique Un champ publique peut être accédé par toute autre fonction membre dans un autre objet ou une autre classe. Ne définissez pas les champs sur publiques.
protégé Un champ protégé peut être accédé par n'importe quelle fonction membre de la classe dans laquelle il est déclarée, ou par n'importe quelle fonction membre définie dans les sous-classes de cette classe. Ne définissez pas les champs sur protégés.
privé Un champ privé ne peut être accédé que par les fonctions membres de la classe dans laquelle il est déclaré, mais pas des sous-classes. Tous les champs doivent être privés, et être accédés par des fonctions membres getters et setters (accesseurs).

Concernant les champs non persistants (qui ne seront pas enregistrés dans la mémoire permanente), définissez-les soit sur statique soit sur transitoire [DES97]. Cela les rend conformes aux conventions du BDK.

4.2.1    Ne pas "masquer" les noms 

Le masquage de nom réfère à la méthode de désignation d'une variable locale, d'un argument ou d'un champ, égal (ou similaire) à un autre de plus grande portée. Par exemple, si vous avez un champ firstName, ne créez pas de variable locale ou de paramètre ayant le nom firstName ou tout nom se rapprochant comme firstNames ou firstName. Cela rend votre code difficile à comprendre et sujet aux bogues car les autres développeurs, ou vous, vont mal interpréter votre code en le modifiant et vont créer des erreurs difficiles à détecter.

4.3    Documentation d'un champ

Chaque champ doit être assez bien documenté afin que les autres développeurs puissent le comprendre. Pour être efficace, vous devez documenter :

  1. Sa description. Vous devez décrire un champ afin que tous sachent comment l'utiliser.
  2. Tous les invariants applicables. Les invariants d'un champ sont les conditions toujours vérifiées de ce champ. Par exemple, un invariant du champ jourDuMois peut être que sa valeur se situe entre 1 et 31 (évidemment, cela peut être beaucoup plus complexe avec cet invariant, en restreignant la valeur du champ sur le mois et l'année). En documentant les restrictions sur la valeur d'un champ, vous aidez à définir les règles métier importantes rendant le fonctionnement du code plus facile à comprendre.
  3. Exemples. Pour les champs associés à des règles métier complexes, fournissez plusieurs valeurs d'exemple pour faciliter leur compréhension. Un exemple est souvent comme une belle image : elle vaut mieux que des milliers de mots.
  4. Problèmes d'accès concurrents. L'accès concurrent est un nouveau concept complexe pour beaucoup de développeurs. En fait, il s'agit au mieux d'un sujet ancien et complexe pour les programmeurs d'accès concurrents expérimentés. Aussi, si vous utilisez les fonctions de programmation Java d'accès concurrent, documentez-le entièrement.
  5. Décisions de visibilité. Si vous avez déclaré un champ autrement que privé, documentez-en la raison. La visibilité des champs a été auparavant expliquée dans la section 4.2, Visibilité des champs et l'utilisation de fonctions membres accesseurs pour la prise en charge de l'encapsulation est traitée ultérieurement dans la section 4.4, Utilisation de fonctions membres accesseurs. Aussi, vous devez avoir une très bonne raison de ne pas déclarer une variable comme privée.

4.4    Utilisation des fonctions membres accesseurs

En plus des conventions de dénomination, la maintenabilité des champs est obtenue par l'utilisation appropriée de fonctions membres accesseurs. Ces dernières sont des fonctions membre offrant la possibilité, soit de mettre à jour un champ, soit d'accéder à sa valeur). Les fonctions membres accesseurs peuvent être de deux types : les setters (ou mutateurs) et les getters. Un setter modifie la valeur d'une variable, tandis qu'un getter l'obtient pour vous.

Les fonctions membres accesseurs servaient à ajouter une surcharge à votre code, mais les compilateurs Java sont désormais optimisés pour leur utilisation, ce qui n'est donc plus vrai. Les accesseurs aident à masquer les caractéristiques d'implémentation de votre classe. En ayant deux points de contrôle, un setter et un getter, où une variable est le plus souvent accédée, vous pouvez accroître la maintenabilité de vos classes en réduisant les points où des modifications peuvent êre apportées. L'optimisation du code Java est expliquée dans la section 9.3, Optimisation du code Java.

L'un des standards les plus importants que votre organisation peut appliquer est l'utilisation d'accesseurs. Certains développeurs ne veulent pas utiliser de fonctions membres accesseurs car ils ne veulent pas taper les quelques lettres supplémentaires requises. Par exemple, pour un getter, vous devez taper "get" et "()" avant et après le nom du champ. Résultat : la maintenabilité et l'extensibilité accrues liées à l'utilisation des accesseurs justifient pleinement leur utilisation.

Les accesseurs sont la seule manière d'accéder aux champs. Un concept clé de l'utilisation appropriée des fonctions membres accesseurs est que les SEULES fonctions membres autorisées à travailler directement avec un champ sont les fonctions membres accesseurs elles-mêmes. Il est en effet possible d'accéder directement à un champ privé au sein des fonctions membres de la classe dans laquelle le champ est affecté, mais vous ne le ferez pas puisque cela augmenterait le couplage dans votre classe.

4.4.1    Pourquoi utiliser des accesseurs ? 

"Une bonne conception de programme cherche à isoler des composants d'un programme d'influences extérieures inutiles, involontaires ou encore indésirables. Les modificateurs d'accès (accesseurs) fournissent au langage des moyens explicites et vérifiables pour contrôler de tels contacts." [KAN97]

Les fonctions membres accesseurs améliorent la maintenabilité de vos classes comme suit :

  1. Mise à jour des champs. Il existe des points uniques de mise à jour pour chaque champ, ce qui le rend plus facile à modifier et à tester. En d'autres termes, vos champs sont encapsulés.
  2. Obtention des valeurs des champs. Vous avez un contrôle complet sur la manière dont les champs sont accédés, ainsi que sur les personnes y accédant.
  3. Obtention des valeurs des constantes et des noms des classes. En encapsulant la valeur des constantes et des noms de classes en fonctions membres getters lorsque ces valeurs/noms changent, vous devez seulement mettre à jour la valeur dans le getter et pas dans chaque ligne de code ou la constante/le nom est utilisé(e).
  4. Initialisation des champs. L'utilisation de l'initialisation paresseuse garantit que les champs sont toujours initialisés, et uniquement s'ils sont nécessaires.
  5. Réduction du couplage entre une sous-classe et sa/ses superclasse(s). Lorsque les sous-classes accèdent à des champs hérités uniquement via leurs fonctions membres accesseurs correspondantes, il est possible de modifier l'implémentation des champs dans la superclasse sans affecter aucune des sous-classes, en réduisant effectivement le couplage entre eux. Les accesseurs réduisent le risque d'une "classe de base fragile" où les modifications dans une superclasse s'étendent dans ses sous-classes.
  6. Encapsulation des modifications apportées aux champs. Si les règles métier appartenant à un ou plusieurs champs changent, vous pouvez modifier vos accesseurs pour fournir la même fonction qu'avant le changement. Cela vous permet de répondre alors plus facilement aux nouvelles règles métier.
  7. Simplification des problèmes d'accès concurrents. [LEA97] indique que les fonctions membres setters offrent un seul endroit pour inclure un notifyAll si vous avez des attentes basées sur la valeur de ce champ. Le passage à une solution d'accès concurrent est alors plus facile.
  8. Le masquage de nom devient moins problématique. Bien que vous devriez éviter le masquage de nom, le fait de donner aux variables locales les mêmes noms qu'aux champs et l'utilisation des accesseurs visant à toujours accéder aux champs signifie que vous pouvez donner le nom que vous souhaitez aux variables locales. Vous n'avez plus à vous soucier du masquage des noms de champs puisque désormais, vous n'y accédez plus directement.

4.4.1.1    Quand ne pas utiliser d'accesseurs 

La seule fois où vous pouvez souhaiter ne pas utiliser d'accesseurs, c'est lorsque le temps d'exécution est de la plus haute importance . Toutefois, il est très rare que l'augmentation du couplage au sein de votre application justifie cette action.

4.4.2    Désignation d'accesseurs

Les fonctions membres getters doivent se voir attribuer le nom "get" + le nom de champ, à moins que le champ représente un booléen (valeur true ou false), auquel cas le getter reçoit le nom "is" + nom de champ. Les fonctions membres setters doivent se voir attribuer le nom "set" + le nom de champ, quel que soit le type de champ ([GOS96] et [DES97]). Le nom du champ est toujours en casse mixte avec la première lettre de tous les mots en majuscule. Cette convention de désignation est systématiquement utilisée dans le kit JDK et est obligatoire pour les développements beans.

Exemples :

Champ Type Nom de getter Nom de setter
firstName
chaîne
getFirstName()
setFirstName()
address
Adresse 
de l'objet
getAddress()
setAddress()
persistent
booléen
isPersistent()
setPersistent()
customerNo
int
getCustomerNo()
setCustomerNo()
orderItems
 
Rang 
d'objets 
OrderItem
getOrderItems()
 
setOrderItems()
 

 

4.4.3    Techniques avancées des accesseurs

L'obtention et la modification des valeurs des champs d'instance n'est pas la seule fonction des accesseurs. Cette section explique comment accroître la flexibilité de votre code en utilisant les accesseurs pour :

4.4.3.1    Initialisation paresseuse 

Il faut initialiser les variables avant d'y accéder. A propos de l'initialisation, il existe deux écoles de pensées : l'initialisation de toutes les variables au moment de la création de l'objet (approche traditionnelle) ou l'initialisation au moment de sa première utilisation. 

La première approche utilise des fonctions membres spéciales, appelées lors de la première création de l'objet : les constructeurs. Même si cette méthode fonctionne, elle est très sujette aux erreurs. Lors de l'ajout d'une nouvelle variable, vous pouvez facilement oublier de mettre les constructeurs à jour.

L'initialisation paresseuse est une approche alternative où les champs sont initialisés par leurs fonctions membres getters, comme présenté ci-dessous. Notez comment une fonction membre setter est utilisée au sein de la fonction membre getter. La fonction membre vérifie que le numéro de la succursale est zéro. Si c'est le cas, il est alors défini sur la valeur par défaut appropriée.

/** Donne le numéro de la succursale, qui est composé des quatre chiffres

les plus à gauche du numéro de compte entier.

Les numéros de compte sont au format BBBBAAAAAA.

*/

protected int getBranchNumber()

{

if ( branchNumber == 0)

{

// Le numéro de la succursale par défaut est 1000, qui

// est la succursale principale dans le centre-ville de Bedrock.

setBranchNumber(1000);

}

return branchNumber;

}

Il est assez courant d'utiliser l'initialisation paresseuse pour les champs qui sont en fait d'autres objets stockés dans la base de données. Par exemple, lorsque vous créez un nouvel article d'inventaire, vous n'avez pas besoin de chercher quel type d'article d'inventaire de la base de données vous avez défini par défaut. Utilisez plutôt l'initialisation paresseuse pour définir cette valeur dès la première fois qu'elle est accédée. Ainsi, vous n'avez qu'à lire l'objet du type d'article d'inventaire dans la base de données au moment où vous le souhaitez et si besoin. 

Cette méthode est avantageuse pour les objets dont les champs ne sont pas régulièrement accédés. Pourquoi risquer la surcharge en récupérant quelque chose de la mémoire persistante si vous n'allez pas l'utiliser ?

Lorsque l'initialisation paresseuse est utilisée dans une fonction membre getter, documentez pourquoi la valeur par défaut est ce qu'elle est, comme nous l'avons vu dans l'exemple ci-dessus. Lorsque vous faites cela, vous supprimez le mystère planant sur la manière dont les champs sont utilisés dans votre code, ce qui améliore sa maintenabilité et son extensibilité.

4.4.3.2    Accesseurs de constantes

Le schéma commun de mise en oeuvre Java est d'implémenter des valeurs constantes comme des champs finaux statiques. Cette méthode a du sens pour les "constantes" dont la stabilité est assurée. Par exemple, le booléen de classe implémente deux champs finaux statiques appelés TRUE et FALSE, qui représentent les deux instances de cette classe. Cela aurait également du sens pour une constante DAYS_IN_A_WEEK dont la valeur ne changera probablement jamais.

Toutefois, de nombreuses "constantes" métier connues changent avec le temps car les règles métier changent. Examinez l'exemple suivant : la Archon Bank de Cardassia (ABC) a toujours insisté pour qu'un compte ait un solde minimum de 500 $ pour pouvoir gagner des intérêts. Pour implémenter cela, nous pouvions ajouter un champ statique intitulé MINIMUM_BALANCE à la classe Account (compte) qui serait utilisée dans les fonctions membres calculant les intérêts. Même si ce système fonctionne, il n'est pas flexible. Que se passe t'il si les règles métier changent et que différents types de comptes ont différents soldes minimaux, par exemple 500 $ pour les comptes épargnes mais seulement 200 $ pour les comptes chèques ? Que se passerait-il si les règles métier passaient d'un solde minimum de 500 $ la première année, 400 $ la seconde, 300 $ la troisième, et ainsi de suite ? La règle sera peut-être de 500 $ en été, mais seulement de 250 $ en hiver ? Une combinaison de toutes ces règles devra peut-être être implémentée dans l'avenir.

La conclusion à en tirer est que l'implémentation de constantes en tant que champs n'est pas flexible. Autre solution, bien plus appropriée : l'implémentation de constantes en tant que fonctions membres getters. Dans l'exemple ci-dessus, une fonction membre (classe) statique appelée getMinimumBalance() est bien plus flexible qu'un champ statique appelé MINIMUM_BALANCE puisque nous pouvons implémenter les diverses règles métier dans cette fonction membre et la sous-classer de manière appropriée pour divers types de comptes.

/** Consulter la valeur du numéro de compte. Les numéros de compte sont au format
    suivant : BBBBAAAAAA, où BBBB est le numéro de la succursale et 
    AAAAAA est le numéro de compte de la succursale.
*/
public long getAccountNumber()
{
	return ( ( getBranchNumber() * 100000 ) + getBranchAccountNumber() );
}
/**
	Définir le numéro de compte. Les numéros de compte sont au format 
	suivant : BBBBAAAAAA où BBBB est le numéro de la succursale et
	AAAAAA est le numéro de compte de la succursale.
*/
public void setAccountNumber(int newNumber)
{
	setBranchAccountNumber( newNumber % 1000000 );
	setBranchNumber( newNumber / 1000000 );
}

Autre avantage des getters de constantes : ils clarifient votre code. Examinez le code présenté ci-dessus : il ne fonctionne pas correctement. Un numéro de compte est la concaténation du numéro de la succursale et du numéro de compte de la succursale. En testant notre code, nous découvrons que la fonction membre setter setAccountNumber() ne met pas correctement à jour les numéros de compte de la succursale. Elle prend les trois caractères les plus à gauche, et non pas quatre. Cela est dû au fait que nous avons utilisé 1 000 000 au lieu de 100 000 pour extraire le champ branchAccountNumber. Si nous n'avions utilisé qu'une seule source pour cette valeur, le getter de constantes getAccountNumberDivisor() comme présenté ci-dessous, notre code aurait été plus cohérent et aurait fonctionné.

/**
	Retourne le diviseur nécessaire à la séparation du numéro de compte de la succursale, du 
	numéro de la succursale au sein du numéro de compte entier. 
Les numéros de compte entiers sont au format BBBBAAAAAA.
*/
public int getAccountNumberDivisor()
{
	return ( (long) 1000000);
}
/**
	Consulter la valeur du numéro de compte. Les numéros de compte sont au format 
 suivant : BBBBAAAAAA, où BBBB est le numéro de la succursale et 
	AAAAAA est le numéro de compte de la succursale.
*/
public long getAccountNumber()
{
	return ( ( getBranchNumber() * getAccountNumberDivisor() ) + getBranchAccountNumber() );
}
/**
	Définir le numéro de compte. Les numéros de compte sont au format 
	suivant : BBBBAAAAAA où BBBB est le numéro de la succursale et
	AAAAAA est le numéro de compte de la succursale.
*/
public void setAccountNumber(int newNumber)
{
	setBranchAccountNumber( newNumber % getAccountNumberDivisor() );

	setBranchNumber( newNumber / getAccountNumberDivisor() );
}

L'utilisation d'accesseurs pour les constantes diminue le risque de bogue et accroît en même temps la maintenabilité de notre système. Lorsque la présentation d'un numéro de compte change, ce qui est possible, il se peut que notre code soit plus facile à modifier car nous avons à la fois masqué et centralisé les informations nécessaires à la construction ou à la division des numéros de compte.

4.4.3.3    Accesseurs de collections

L'objectif principal des accesseurs est d'encapsuler l'accès aux champs, afin de réduire le couplage dans le code. Les collections, telles que les tableaux et les vecteurs sont plus complexes que des champs de valeur unique et nécessitent donc plus que la fonction membre setter et getter standard implémentée pour eux. Puisque vous pouvez ajouter et retirer au sien des collections, les fonctions membres accesseurs doivent être incluses à cet effet. Ajoutez les fonctions membres accesseurs suivantes où elles sont nécessaires à un champ représentant une collection :

Type de fonction membre Convention de dénomination Exemple
Getter de la collection
getCollection()
getOrderItems()
Setter de la collection
setCollection() 
setOrderItems()
Insérer un objet dans la collection
insertObject()
insertOrderItem()
Supprimer un objet de la collection
deleteObject()
deleteOrderItem()
Créer et ajouter un nouvel objet dans la collection
newObject()
newOrderItem()

L'avantage de cette approache réside dans le fait que la collection est complètement encapsulée, ce qui vous permet de la remplacer ultérieurement par une autre structure, par exemple une liste liée ou un arbre binaire.

4.4.3.4    Accès simultané à plusieurs champs

Une des forces des fonctions membres accesseurs est qu'elles vous permettent d'appliquer les règles métier. Supposons, par exemple, une hiérarchie de classes de formes. Chaque sous-classe de forme connaît sa position via l'utilisation de deux champs : "xPosition et yPosition", et peut être déplacée sur l'écran dans un plan à deux dimensions en appelant la fonction membre move(Float xMovement, Float yMovement). Dans cet exemple, il est inutile de déplacer une forme sur un axe à la fois. A la place, les axes x et y seront déplacés simultanément (il est possible de passer une valeur 0.0 comme pour le paramètre de la fonction membre move()). Cela implique que la fonction membre move() doit être publique, mais les fonctions membres setXPosition() et setYPosition() doivent être toutes les deux privées et appelées par la fonction membre move() de manière appropriée.

A la place de l'implémentation, il est possible d'introduire une fonction membre setter qui mette à jour les champs ensemble, comme présenté ci-dessous. Les fonctions membres setXPosition() et setYPosition() seraient toujours privées afin de ne pas être directement appelées par les sous-classes ou les classes externes (vous souhaiteriez ajouter de la documentation, présentée ci-dessous, indiquant qu'elles ne doivent pas être directement appelées).

/** Définir la position de la forme */
protected void setPosition(Float x, Float y)
{
	setXPosition(x);
	setYPosition(y);
}
/** Définir la position x.  Important : appeler setPosition(), pas cette fonction membre. */
private void setXPosition(Float x)
{
	xPosition = x;
}
/** Définir la position y de la forme
Important : appeler setPosition(), pas cette fonction membre.
*/
private void setYPosition(Float y)
{
	yPosition = y;
}

4.5    Visibilité des accesseurs

Efforcez-vous de toujours définir les accesseurs sur protégés afin que seules les sous-classes puissent accéder aux champs. Ne rendez le getter ou le setter correspondant publique uniquement si une "classe externe" doit accéder à un champ. Il est courant que la fonction membre getter soit publique et la fonction membre setter protégée.

Parfois, vous devez rendre les setters privés pour assurer une mise en attente des invariants. Par exemple, une classe Order (commande) peut avoir un champ représentant une collection d'instances OrderItem, et un second champ intituléorderTotal, qui est le total de la commande entière. orderTotal est un champ de grande diffusion représentant la somme ou les sous-totaux des articles commandés. Les seules fonctions membres mettant à jour la valeur de orderTotal sont celles qui manipulent la collection d'articles de commande. Supposons que ces fonctions membres soient toutes implémentées dans Order, vous devez rendre setOrderTotal() privé, même si getOrderTotal() est certainement publique.

4.6    Toujours initialiser les champs statiques

Les champs statiques, également connus sous le nom de champs de classe, doivent avoir des valeurs valides, étant donné que vous ne pouvez pas supposer que les instances d'une classe seront créées avant qu'un champ statique soit accédé.

 


5    Standards des variables locales

Une variable locale est un objet ou un élément de donnée défini dans la portée d'un bloc, souvent une fonction membre. La portée d'une variable locale est le bloc dans lequel elle est définie. Les standards essentiels de codage des variables locales se focalisent sur :

5.1    Désignation de variables locales

En général, les variables locales sont nommées suivant les mêmes conventions que celles utilisées pour les champs. Aussi, utilisez des libellés en anglais et en langage clair, avec la première lettre de tout mot non initial en majuscule.

Dans un souci de cohérence, cette convention de dénomination est toutefois assouplie pour divers types spécifiques de variables locales :

5.1.1    Désignation de flux

Lorsqu'il y a un seul flux d'entrée et/ou de sortie ouvert(s), utilisé(s) puis fermé(s) dans une fonction membre, la convention standard consiste à utiliser respectivement In et Out pour les noms de ces flux,[GOS96]. Si un flux utilise l'entrée et la sortie, employez alors le nom inOut.

Il existe une alternative standard à cette convention de dénomination, mais elle est incompatible avec les recommandations de Sun. Elle consiste à utiliser les noms inputStream (flux d'entrée), outputStream (flux de sortie) et ioStream au lieu de, respectivement In, Out et inOut.

5.1.2    Désignation de compteurs de boucles

Puisque les compteurs de boucles sont très couramment utilisés pour les variables locales, et qu'elles étaient autorisés dans C/C++ ; dans la programmation Java, l'utilisation de i, j ou k est acceptable pour les compteurs de boucle [GOS96]. Si vous utilisez ces noms pour les compteurs de boucles, utilisez-les systématiquement.

Les noms tels que loopCounter ou simplement counter sont des alternatives couramment utilisées, mais le problème est que vous trouvez souvent des noms tels que counter1 et counter2 dans les fonctions membres nécessitant plus d'un compteur. Résultat : i, j, k fonctionnent comme des compteurs, ils sont rapides à taper et généralement acceptés.

5.1.3    Désignation d'objets exceptions

Le traitement des exceptions est également très commun dans le codage Java, l'utilisation de la lettre e pour une exception générique est donc acceptable [GOS96].

5.2    Déclaration et documentation de variables locales

Il existe plusieurs conventions en ce qui concerne la déclaration et la documentation de variables locales dans Java. Ces conventions sont :

  1. Déclaration d'une variable locale par ligne de code. Ce système est cohérent avec une instruction par ligne de code et permet de documenter chaque variable avec un commentaire en ligne.
  2. Documentation des variables locales avec un commentaire en ligne. Le commentaire en ligne représente un style, où un commentaire sur une seule ligne, signalé par des //, suit immédiatement une commande sur la même ligne de code (il s'agit du commentaire de fin de ligne). Documentez pourquoi une variable locale est utilisée, où son utilisation est appropriée et pourquoi elle est utilisée. Cela clarifie votre code.
  3. Utilisation de variables locales pour une seule chose. Lorsque vous utilisez une variable locale pour plus d'une raison, vous diminuez sa cohésion et la rendez plus difficile à comprendre. Vous augmentez également les risques d'introduire des bogues dans votre code à partir des effets secondaires inattendus des valeurs précédentes d'une variable locale, située précédemment dans le code. La réutilisation de variables locales est en effet plus efficace puisque moins de mémoire nécessite d'être allouée, mais la réutilisation de variables locales diminue la maintenabilité de votre code et le rend plus fragile. Cela ne vaut généralement pas le faible avantage lié au fait de ne pas avoir à allouer plus de mémoire.

5.2.1    Généralités sur la déclaration

Les variables locales déclarées entre des lignes de code, par exemple, dans la portée d'une instruction if peuvent être difficiles à trouver par les personnes qui ne connaissent pas bien votre code.

Au lieu de déclarer des variables locales immédiatement après leur première utilisation, il est possible de les déclarer en haut du code. Puisque vos fonctions membres doivent de toute façon être courtes, reportez-vous à la section 3.5.5, Ecriture de ligne de commande courtes sur une seule ligne. Cela ne doit pas être si terrible d'aller en haut de votre code pour déterminer de quoi traite la variable locale.


6    Standards des paramètres pour les fonctions membres

Les standards essentiels des paramètres et arguments pour les fonctions membres traitent de la manière dont ils sont désignés et documentés. Le terme paramètre est utilisé pour se référer à un argument de fonction membre.

6.1    Désignation de paramètres

Les paramètres doivent être désignés en suivant les mêmes conventions utilisées pour les variables locales. Comme pour les variables locales, le masquage de nom est un problème.

Exemples :

customer

inventoryItem

photonTorpedo

e

Il existe une alternative viable, tirée de Smalltalk : l'utilisation des conventions de dénomination pour les variables locales, en ajoutant "a" ou "an" au début du nom. L'ajout de "a" ou "an" permet au paramètre d'apparaître à partir de champs et de variables locales, et évite le problème du masquage de nom. C'est la meilleure méthode.

Exemples :

aCustomer

anInventoryItem

aPhotonTorpedo

anInputStream

anException

6.2    Documentation de paramètres

Les paramètres pour une fonction membre sont documentés dans la documentation d'en-tête de la fonction membre à l'aide de la balise javadoc @param. Décrivez les points suivants :

  1. Ce à quoi il doit être utilisé. Documentez ce pourquoi un paramètre est utilisé, afin que les autres développeurs comprennent le contexte entier de la manière dont le paramètre sera utilisé.
  2. Toute restriction ou précondition. Si toutes les valeurs possibles d'un paramètre ne sont pas acceptables pour une fonction membre, l'auteur de l'appel de la fonction membre doit le savoir. Une fonction membre n'accepte peut-être que les nombres positifs ou les chaînes de moins de cinq caractères.
  3. Exemples. Si ce qu'un paramètre devrait être n'est pas complètement évident, fournissez alors un ou plusieurs exemples dans la documentation.

6.2.1    Utilisation d'interfaces pour les types de paramètres

Au lieu de spécifier une classe telle que Object, pour le type de paramètre, spécifiez si possible une interface telle que Runnable. L'avantage est que cette approche, selon la situation, peut être plus précise (Runnable est plus précis que Object), ou peut potentiellement être une meilleure façon de prendre en charge le polymorphisme. Au lieu d'insister sur le fait qu'un paramètre est une instance d'une classe dans une hiérarchie de classe spécifique, indiquez qu'il prend en charge une interface spécifique impliquant qu'il doit uniquement être polymorphiquement conforme à ce dont vous avez besoin.


7    Standards des classes, des interfaces, des packages et des unités de compilation

Ce chapitre traite des standards et instructions des classes, interfaces, packages et unités de compilation. Une classe est un canevas à partir duquel des objets sont instanciés (crées). Les classes contiennent la déclaration des champs et des fonctions membres. Les interfaces représentent la définition d'une signature commune, comprenant à la fois des champs et des fonctions membres, qu'une classe de mise en oeuvre d'interface doit prendre en charge. Un package est une collection de classes associées. Enfin, une unité de compilation est un fichier code source dans lequel des classes et des interfaces sont déclarées. Java permet le stockage des unités de compilation dans une base de données, ainsi, une unité de compilation individuelle ne doit pas nécessairement être associée à un fichier code source physique.

7.1    Standards des classes

Les standards essentiels des classes sont basés sur :

7.1.1    Désignation de classes

La convention Java standard consiste à utiliser un libellé en anglais et en langage clair, dont la première lettre et en majuscule et le reste du nom en casse mixte. ([GOS96] et [AMB98])

Les noms de classe doivent être au singulier.

Exemples :

Customer

Employee

Order

OrderItem

FileStream

String

7.1.2    Documentation d'une classe

Les informations suivantes doivent apparaître dans les commentaires de documentation juste avant la définition d'une classe :

  1. Objet de la classe. Les développeurs ont besoin de connaître l'objet général d'une classe pour pouvoir déterminer si elle satisfait leur besoins. Prenez l'habitude de documenter tout point positif que vous connaissez à propos d'une classe. Par exemple, si elle fait partie d'un pattern ou s'il existe des restrictions à son utilisation [AMB98].
  2. Bogues connus. Tout problème important avec une classe doit être documenté afin que les autres développeurs comprennent les faiblesses et les difficultés liées à la classe. En outre, vous devez également documenter la raison pour laquelle un bogue n'a pas été corrigé. Si un bogue est spécifique à une seule fonction membre, il doit alors être plutôt associé à la fonction membre.
  3. Historique de développement ou de maintenance de la classe. Il est courant d'inclure une table historique répertoriant les dates, les auteurs et les résumés des modifications apportées à une classe. Elle fournit aux programmeurs de maintenance un aperçu des modifications antérieures d'une classe et documente qui a fait quoi à une classe.
  4. Documentation des invariants applicables. Un invariant est un ensemble d'assertions concernant une instance ou une classe qui doivent être vérifiées à chaque période sans modifications. Un période sans modifications se définit par la période précédent l'appel d'une fonction membre sur l'objet ou la classe et immédiatement après l'appel d'une fonction membre [MEY88]. En documentant les invariants d'une classe, vous fournissez aux autres développeurs un précieux aperçu des utilisations possibles d'une classe.
  5. Stratégie d'accès concurrent. Toute classe implémentant l'interface Runnable doit avoir une description complète de sa stratégie d'accès concurrent. La programmation d'accès concurrent est un sujet complexe, nouveau pour beaucoup de programmeurs. Vous devez donc y investir du temps pour vous assurer que tout le monde puisse comprendre votre travail. Il est important de documenter votre stratégie d'accès concurrent et la raison pour laquelle vous l'avez choisie. Les stratégies d'accès concurrent générales [LEA97] comportent les éléments suivants :
  • objets synchronisés
  • objets indéterminés
  • objets protégés
  • objets versionnés
  • contrôleurs des règles de l'accès concurrent
  • accepteurs

7.1.3    Déclarations de classes

Pour rendre vos classes plus faciles à comprendre, vous pouvez les déclarer de manière cohérente. L'approche générale dans Java consiste à déclarer une classe dans l'ordre suivant :

  • fonctions membres publiques
  • champs publiques
  • fonctions membres protégées
  • champs protégés
  • fonctions membres privées
  • champs privés

[LAF97] montre que les constructeurs et finalize() doivent être répertoriés en premier, probablement car ils sont les premières fonctions membres qu'un autre développeur va examiner pour comprendre comment utiliser la classe. En outre, puisque nous avons un standard pour déclarer tous les champs comme privés, l'ordre de déclaration se présente de la manière suivante :

constructeurs

finalize()

fonctions membres publiques

fonctions membres protégées

fonctions membres privées

champs privés

Dans chaque groupe de fonctions membres, il est courant de les répertorier dans l'ordre alphabétique. Beaucoup de développeurs choisissent de répertorier d'abord les fonctions membres statiques dans chaque groupe, puis les fonctions membres d'instances, et enfin, dans chacun de ces deux sous-groupes, de lister les fonctions membres de manière alphabétique. Ces deux approches sont valables, vous devez juste en choisir une et vous y tenir.

7.1.4    Réduction de l'interface publique et protégée

Un des principes fondamentaux de la conception orientée objet est la réduction de l'interface publique d'une classe. En voici plusieurs raisons :

  1. Facilité d'apprentissage. Pour apprendre comment utiliser une classe, vous devez seulement avoir à comprendre son interface publique. Plus l'interface publique est petite, plus une classe est facile à apprendre.
  2. Couplage réduit. Lorsque l'instance d'une classe envoie un message à une instance d'une autre classe, ou directement à la classe elle-même, il y a un couplage entre les deux classes. La réduction de l'interface publique suppose que vous diminuez les opportunités de couplage.
  3. Flexibilité accrue. Elle est directement liée au couplage. Si vous souhaitez changer la manière dont une fonction membre est implémentée dans votre interface publique (vous voulez peut-être modifier ce que renvoie la fonction membre), vous devez modifier tout code appelant la fonction membre. Plus l'interface publique est petite, plus l'encapsulation est importante, et donc, plus votre flexibilité est accrue.

Il est clair que vous ne regretterez pas la réduction de l'interface publique, mais parfois, vous souhaiterez également réduire l'interface protégée. L'idée de base est que du point de vue d'une sous-classe, les interfaces protégées de toutes ses superclasses sont effectivement publiques. Toute fonction membre de l'interface protégée peut être appelée par une sous-classe. Ainsi, vous voulez réduire l'interface protégée d'une classe pour les mêmes raisons que vous souhaitez réduire l'interface publique.

7.1.4.1    Définir d'abord l'interface publique

La plupart des développeurs expérimentés définissent l'interface publique d'une classe avant de commencer à la coder. 

7.2    Standards des interfaces

Les standards importants concernant les interfaces sont basés sur :

7.2.1    Désignation d'interfaces

La convention Java sert à désigner les interfaces en casse mixte, avec la première lettre de chaque mot en majuscule. La convention Java la plus appropriée pour le nom d'une interface est l'utilisation d'un adjectif descriptif, tel que Runnable ou Cloneable, même si les substantifs descriptifs, tels que Singleton ou DataInput sont également courants [GOS96].

7.2.1.1    Alternative

Préfixez le nom d'interface avec la lettre "I". Comme [COA97] le suggère, l'ajout de la lettre "I" au début des noms d'interface produit des noms tels que ISingleton ou IRunnable. Cette méthode aide à faire la distinction entre les noms d'interfaces et les noms de packages et de classes. J'apprécie cette convention de dénomination potentielle car elle rend les diagrammes de classes, parfois appelés modèles d'objets, plus facile à lire. Le principal inconvénient est que les interfaces existantes, telles que Runnable, ne sont pas désignées avec cette méthode. Cette convention de désignation des interfaces est également appréciée pour l'architecture COM/DCOM de Microsoft.

7.2.2    Documentation d'interfaces

Les informations suivantes doivent apparaître dans les commentaires de documentation juste avant la définition d'une interface :

  1. Précisez l'objet. Avant que d'autres développeurs utilisent une interface, ils doivent comprendre le concept qu'elle encapsule. En d'autres termes, ils ont besoin de connaître son objet. Pour savoir si vous avez besoin de définir une interface, vérifiez si vous pouvez facilement décrire son objet. Si vous le décrivez difficilement, il y a alors peu de chances que vous ayez besoin de l'interface pour démarrer. Le concept des interfaces est nouveau chez Java, les personnes ne sont donc pas encore expérimentées dans leur utilisation appropriée et elles risquent de trop l'utiliser en raison de leur nouveauté.
  2. Comment utiliser et ne pas utiliser les interfaces. Les développeurs ont besoin de savoir comment une interface peut être utilisée, et comment elle ne doit pas l'être [COA97].

Etant donné que la signature des fonctions membres est définie dans une interface, pour chaque signature de fonction membre, vous devez suivre les conventions de documentation relatives aux fonctions membres expliquées dans le Chapitre 3.

7.3    Standards de packages

Les standards importants concernant les packages sont basés sur :

  • Les conventions de dénomination
  • Les conventions de documentation

7.3.1    Désignation de packages

Plusieurs règles sont associées à la désignation des packages. Dans l'ordre, ces régles sont :

  1. Les identificateurs sont séparés par des points. Pour rendre les noms des packages plus lisibles, Sun suggère que les identificateurs dans les noms de packages soient séparés par des points. Par exemple, le nom de package java.awt est composé de deux identificateurs, java et awt.
  2. Les packages de distribution standard Javas de Sun commencent par l'identificateur "java". Sun s'est réservé ce droit afin que les packages Java standard soit désignés de manière cohérente, quel que soit le vendeur de votre environnement de développement Java.
  3. Les noms des packages locaux commencent par un identificateur dont toutes les lettres ne sont pas en majuscule. Les packages locaux sont utilisés en interne, au sein de votre organisation et ne seront pas distribués à d'autres organisations. Voici des exemples de nom de packages : persistence.mapping.relational et interface.screens.
  4. Les noms des packages globaux commencent par le nom de domaine Internet inversé de votre entreprise. Un package destiné à être distribué à plusieurs entreprises doit comporter le nom de domaine de l'entreprise d'origine avec le type de domaine de niveau supérieur en majuscule. Par exemple, pour distribuer les packages précédents, ils seront désignés comme suit : com.rational.www.persistence.mapping.relational et com.rational.www.interface.screens.0

7.3.2    Documentation de packages

Vous devez gérer un ou plusieurs documents externes décrivant l'objectif des packages développés par votre entreprise. Pour chaque package, documentez :

  1. La raison du package. Les autres développeurs ont besoin de connaître un package afin de pouvoir décider s'ilsveulent l'utiliser et, s'il s'agit d'un package partagé, s'ils veulent l'améliorer ou l'étendre.
  2. Les classes du package. Ajoutez une liste des classes et interfaces dans le package avec une courte description d'une ligne pour chacunes, afin que les autres développeurs sachent ce que le package contient.

Conseil : créez un fichier HTML en utilisant le nom du package et placez-le dans le répertoire approprié du package. Le fichier devrait avoir le suffixe .html.

7.4    Standards des unités de compilation

Les standards et instructions des unités de compilation sont basés sur :

7.4.1    Désignation d'unités de compilation

Une unité de compilation (dans ce cas, il s'agit d'un fichier code source) doit recevoir le nom de l'interface ou de la classe primaire où elle est déclarée. Utilisez le même nom pour le package ou la classe que pour le fichier, et la même casse. L'extension .java doit être suffixée au nom de fichier.

Exemples :

Customer.java

Singleton.java

SavingsAccount.java

7.4.2    Documentation d'unités de compilation

Même si vous devez vous efforcer à n'avoir qu'une déclaration d'interface ou de classe par fichier, il est parfois utile de définir plusieurs classes (ou interfaces) dans le même fichier. En règle générale, si le seul objet d'une classe B est d'encapsuler la fonctionnalité nécessaire uniquement à la classe A, la classe B doit donc apparaître dans le même fichier code source que la classe A. Par conséquent, les conventions de documentation suivantes s'appliquent à un fichier code source et pas spécifiquement à une classe :

  1. Pour les fichiers avec plusieurs classes, répertoriez chaque classe. Si un fichier contient plus d'une classe, vous devez fournir une liste des classes et une courte description de chacune d'entre elles.
  2. Le nom du fichier et/ou les informations d'identification. Le nom du fichier doit être inclus en haut de celui-ci. L'avantage est que si le code est imprimé, vous en connaissez le fichier source.
  3. Informations de copyright. Si possible, indiquez toute information de copyright du fichier. Il est courant d'indiquer l'année du copyright et le nom de la personne ou de l'organisation détenant le copyright. L'auteur du code n'est pas nécessairement le détenteur du copyright.

8    Traitement des erreurs et exceptions

La philosophie générale consiste à utiliser des exceptions uniquement pour les erreurs : les erreurs de programmation et de raisonnement, les erreurs de configuration, les données altérées, l'épuisement des ressources et ainsi de suite. La règle générale est que les systèmes, dans des conditions normales et en l'absence d'incident matériel ou de surcharge ne devraient soulever aucune exception.

  1. Utilisez des exceptions pour gérer les erreurs de programmation et de raisonnement, les erreurs de configuration, les données altérées et l'épuisement des ressources

Signalez les exceptions en utilisant le mécanisme de journalisation approprié, le plus tôt possible ainsi qu'à l'endroit où elles sont soulevées.

  1. Réduisez le nombre d'exceptions exportées à partir d'une abstraction donnée.

Dans les systèmes importants, la gestion d'un grand nombre d'exceptions à chaque niveau rend le code difficile à lire et à gérer. Le traitement des exceptions éclipse parfois le traitement normal.

Il existe différentes façons de réduire le nombre d'exceptions :

  • Exportez seulement quelques exceptions mais fournissez des primitives de "diagnose" permettant de demander l'abstraction erronée ou le mauvais objet afin d'obtenir plus de détails sur la nature du problème survenu.
  • Ajoutez des états "exceptionnels" aux objets, et fournissez des primitives pour vérifier explicitement la validité des objets.
  1. N'utilisez pas d'exceptions pour des événements fréquents, prévus.

L'utilisation d'exceptions pour représenter des conditions qui ne sont pas clairement des erreurs présente plusieurs inconvénients :

  • C'est confus.
  • Cela entraîne généralement une perturbation du flux de commande qui est plus difficile à comprendre et à gérer.
  • Le code est plus difficile à déboguer, puisque la plupart des débogueurs de niveau source signalent par défaut toutes les exceptions.

Par exemple, n'utilisez pas d'exception comme valeur additionnelle retournée par une fonction (par exemple Value_Not_Found dans une recherche). Utilisez une procédure avec un paramètre "out" ou introduisez une valeur spéciale signifiant Not_Found, ou encore, comprimez le type retourné dans un enregistrement avec un discriminant Not_Found.

  1. N'utilisez pas d'exceptions pour implémenter des structures de contrôle.

Il s'agit d'un cas particulier de la règle précédente : les exceptions ne doivent pas être utilisées sous forme d'instruction "goto".

  1. Assurez-vous que les codes d'état ont une valeur appropriée.

Lors de l'utilisation d'un code d'état renvoyé par des sous-programmes en tant que paramètre "out", assurez-vous toujours qu'une valeur est attribuée au paramètre "out" en faisant de lui la première instruction exécutable du corps du sous-programme. Faites systématiquement, de tous les statuts, un succès par défaut ou un échec par défaut. Pensez à toutes les sorties possibles du sous-programme, y compris les gestionnaires d'exceptions.

  1. Exécutez des contrôles de sécurité localement, n'attendez pas que votre client les fasse.

Si un sous-programme risque de produire un résultat erroné à moins d'une saisie appropriée, installez le code dans le sous-programme pour détecter et signaler l'entrée non valide d'une manière contrôlée. Ne vous fiez pas à un commentaire disant au client de passer des valeurs appropriées. Il est virtuellement certain qu'un jour ou l'autre, ce commentaire sera ignoré, ce qui entraînera des erreurs difficiles à déboguer si les paramètres non valides ne sont pas détectés.


9    Problèmes et standards divers

Ce chapitre décrit plusieurs standards et instructions importants, assez généraux pour nécessiter leur propre chapitre.

9.1    Réutilisation

Tout package ou bibliothèque de classe Java que vous acquerrez ou réutilisez à partir d'une source externe doit être certifié Java 100 % [SUN97]. L'application de ce standard guarantit que ce que vous réutilisez fonctionnera sur toutes les plates-formes choisies pour le déployer. Vous pouvez obtenir des classes, des packages ou des applets Java à partir d'une diversité de sources, soit d'une entreprise de développement tierce spécialisée en bibliothèques Java, soit d'une autre division ou équipe de projet au sein de votre entreprise.

9.2    Importation de classes

L'instruction d'importation permet l'utilisation de caractères génériques lors de l'indication des noms des classes. Par exemple, l'instruction

import java.awt.*;

introduit toutes les classes dans le package java.awt. En fait, cela n'est pas tout à fait vrai. Ce qui se passe réellement est que chaque classe que vous utilisez à partir du package java.awt sera apportée dans votre code lors de sa compilation, sauf les classes que vous n'utilisez pas. Cela semble être une bonne fonction, mais en fait, elle réduit la lisibilité de votre code. Il vaut mieux qualifier complètement le nom des classes utilisées par votre code [LAF97]; [VIS96]. L'exemple ci-dessous constitue une meilleure manière d'importer des classes :

import java.awt.Color
import java.awt.Button
import java.awt.Container

9.3    Optimisation du code Java

L'optimisation de votre code est l'une des dernières choses auxquelles les programmeurs doivent penser, pas la première. Laissez l'optimisation pour la fin car ne voulez optimiser que le code qui en a besoin. Très souvent, un faible pourcentage de votre code devient la grande majorité du temps de traitement, et ce code est alors celui qui doit être optimisé. Une erreur classique de programmeurs inexpérimentés est d'essayer d'optimiser leur code entier, même celui qui fonctionne déjà assez rapidement.

  1. Ne perdez pas votre temps à optimiser un code dont tout le monde se moque !

Que devez-vous rechercher lorsque vous optimisez votre code ? Comme [KOE97] le précise, les facteurs les plus importants sont la surcharge fixe et la performance sur les entrées importantes. La raison en est simple : la surcharge fixe contrôle la vitesse d'exécution pour les petites entrées et l'algorithme contrôle les entrées importantes. La règle générale de Koenig est qu'un programme fonctionnant bien, à la fois pour les petites entrées et les entrées importantes, fonctionnera probablement bien pour les entrées de tailles moyenne.

Les développeurs qui doivent écrire un logiciel fonctionnant sur plusieurs plates-formes matérielles et/ou systèmes d'exploitation doivent connaître les idiosyncrasies dans les diverses plates-formes. Les opérations qui semblent nécessiter un temps important, par exemple la manière dont la mémoire et les mémoires tampons sont traitées, montrent souvent les variations substantielles entre les plates-formes. Il est courant de découvrir que le code doit être optimisé différemment, selon la plate-forme.

Autre problème à connaître lors de l'optimisation du code : les priorités de vos utilisateurs, car selon le contexte, les personnes seront sensibles à des retards particuliers. Par exemple, vos utilisateurs seront probablement plus satisfaits par un écran qui s'affiche immédiatement de lui-même et prend ensuite huit secondes pour charger des données plutôt qu'un écran qui s'affiche de lui-même après avoir pris cinq secondes pour charger les données. En d'autres termes, la plupart des utilisateurs sont prêts à attendre un peu plus longtemps, tant qu'ils ont un commentaire en retour immédiat, ce qui est important lors de l'optimisation du code.

  1. Il n'est pas nécessaire d'augmenter la rapidité de votre code pour l'optimiser aux yeux de vos utilisateurs.

L'optimisation peut représenter la différence entre le succès et l'échec de votre application, n'oubliez jamais qu'il est beaucoup plus important que votre code fonctionne correctement. N'oubliez jamais qu'un logiciel lent qui fonctionne est toujours préférable à un logiciel rapide qui ne fonctionne pas.

9.4    Ecriture de routines de test Java

Les tests orientés objet représentent un sujet essentiel, loin d'être ignoré par la communauté de développement d'objets. La réalité est que vous ou une autre personne devra tester le logiciel que vous écrivez, quel que soit le langage choisi. Une routine de test est une collection de fonctions membres, dont quelques-unes sont imbriquées dans les classes elles-mêmes (appelées tests d'intégration) et d'autres dans des classes de test spécialisé, utilisé pour tester votre application.

  1. Préfixez tous les noms des fonctions membres du test avec "test". Cela vous permet de trouver rapidement toutes les fonctions membres de test dans votre code. Cette méthode offre l'avantage de permettre de supprimer facilement vos fonctions membres de test de votre code source avant de compiler la version de production.
  2. Désignez systématiquement toutes les fonctions membres de test des fonctions membres. Le test de méthode est le fait de vérifier qu'une seule fonction membre fonctionne comme prévu. Toutes les fonctions membres de test des fonctions membres doivent être désignés en suivant le format "testMemberFunctionNameForTestName". Par exemple, les fonctions membres des routines de test pour tester withdrawFunds() vont inclure testWithdrawFundsForInsufficientFunds() testWithdrawFundsForSmallWithdrawal(). Si vous avez une série de tests pour withdrawFunds(), vous pouvez choisir d'écrire une fonction membre intitulée testWithdrawFunds() les appelant tous.
  3. Désignez systématiquement toutes les fonctions membres des tests de classe. Le test de classe est le fait de vérifier qu'une seule classe fonctionne comme prévu. Toutes les fonctions membres des tests de classe doivent être désignées en suivant le format "testSelfForTestName". Par exemple, les fonctions membres des routines de test pour tester la classe Account (compte) testSelfForSimultaneousAccess() et testSelfForReporting().
  4. Créez un seul point pour appeler les tests d'une classe. Développez une fonction membre statique intitulée testSelf() qui appelle toutes les fonctions membres des tests de méthode et des tests de classe.
  5. Documentez les fonctions membres de votre routine de test. La documentation doit contenir une description du test, ainsi que les résultats prévus du test.

10    Patterns de succès

Le fait d'avoir un document de standards ne fait pas automatiquement de vous un développeur plus productif. Pour réussir, vous devez décider de devenir plus productif et cela signifie que vous devez appliquer efficacement ces standards.

10.1    Utilisation efficace de ces standards

Les conseils suivants vont vous aider à utiliser les standards de codage Java et les instructions décrits dans ce document, avec plus d'efficacité.

  1. Comprendre les standards. Prenez votre temps pour comprendre pourquoi chaque standard et chaque instruction entraîne une plus grande productivité. Par exemple, ne déclarez pas chaque variable locale sur sa propre ligne juste parce que les instructions le recommandent. Faites-le parce que vous comprenez que cela accroît la clarté de votre code.
  2. Soyez convaincu. La compréhension de chaque standard est un début, mais vous devez également être convaincu de ce qu'ils préconisent. Suivez les standards parce que vous pensez qu'il s'agit de la meilleure manière de coder, pas juste parce que vous en avez le temps.
  3. Suivez-les tout en codant, pas en y pensant après coup. Un code documenté est plus facile à comprendre pendant et après son écriture. De la même manière, il est plus facile de travailler avec des champs et des fonctions membres nommés lors du développement et de la maintenance. Il est également plus aisé de travailler avec un code sain lors du développement et de la maintenance. Le suivi de ces standards accroît votre productivité pendant que vous développez et rend votre code plus facile à gérer (les développeurs de maintenance sont de ce fait également plus productifs). Si vous écrivez dès le début un code sain, vous en bénéficiez pendant sa création.
  4. Intégrez-les dans votre processus d'assurance qualité. Une partie de l'inspection d'un code doit être dédiée à vous assurer que le code source suit les standards adoptés par votre organisation. Utilisez les standards comme base à partir de laquelle vous formez et conseillez vos développeurs dans un souci d'efficacité.

10.2    Autres facteurs conduisant à l'écriture d'un code réussi

  1. Programmez pour les personnes, pas pour la machine. L'objectif premier de vos efforts de développement doit être de rendre votre code facile à comprendre par les autres personnes. Si personne ne peut le saisir, c'est qu'il est mauvais. Utilisez les conventions de dénomination. Documentez votre code. Divisez-le en paragraphes.
  2. Concevoir d'abord, coder ensuite. Avez-vous déjà été dans une situation où une partie du code sur laquelle repose votre programme nécessite une modification ? Un nouveau paramètre a peut-être eu besoin d'être transmis à une fonction membre, ou une classe a pu devoir être divisée en plusieurs classes. Quelle quantité de travail supplémentaire a été nécessaire pour vous assurer que votre code fonctionnait avec la version reconfigurée du code modifié ? Etiez-vous satisfait ? Vous êtes-vous demandé pourquoi la personne ayant écrit le code ne s'était pas arrêtée et n'y avait pas pensé afin que cela n'arrive pas ou que ce soit CONÇU d'abord ? Bien sûr, vous l'avez fait. Si vous prenez du temps pour comprendre comment écrire le code avant de commencer à coder, vous passerez sans doute moins de temps à l'écrire. En outre, vous réduirez potentiellement l'impact des modifications futures de votre code, en y pensant simplement en amont.
  3. Développez par petites étapes. Le développement en petites étapes (écrire quelques fonctions membres, les tester, puis écrire des fonctions membres supplémentaires) est souvent beaucoup plus efficace que d'écrire un éventail entier de code en une seule fois, puis essayer de le corriger. Il est bien plus facile de tester et de corriger dix lignes de code plutôt que 100. En fait, il est certain que vous pouvez programmer, tester et corriger 100 lignes de code en dix incréments de 10 lignes en moitié moins de temps que vous auriez passé à écrire un seul bloc de code de cent lignes, produisant le même travail. 
    La raison de cela est simple. Lorsque vous testez votre code et que vous trouvez un bogue, vous trouvez presque toujours le bogue dans le nouveau code que vous venez d'écrire (en supposant bien sûr que le reste du code était assez solide pour démarrer). Vous pouvez pourchasser un bogue beaucoup plus vite dans une petite section de code que dans une grande. En développant en petites étapes incrémentales, vous réduisez le temps moyen nécessaire pour trouver un bogue, ce qui réduit alors le temps passé à développer.
  4. Gardez votre code simple. Un code complexe peut être intellectuellement satisfaisant à écrire, mais si les autres personnes ne le comprennent pas, alors il est mauvais. La première fois que quelqu'un, peut-être même vous, doit modifier une partie d'un code complexe, soit pour corriger un bogue, soit pour l'étendre, il y a de fortes chances que le code soit réécrit. En fait, vous avez probablement déjà réécrit le code de quelqu'un, car il était trop difficile à comprendre. Qu'avez-vous alors pensé du développeur d'origine ? Pensiez-vous que cette personne était un génie ou un idiot ? Il n'y a aucun mérite à écrire un code nécessitant d'être réécrit ultérieurement. Suivez donc la règle KISS : Keep it simple, stupid (Conservez un code simple et facile à comprendre).
  5. Apprenez les patterns généraux, les anti-patterns et les schémas de mise en oeuvre. Il existe une abondance d'anti-patterns et de patterns d'analyse, de conception et de processus, ainsi que de schémas de mise en oeuvre de programmation, disponibles pour vous aider à accroître votre productivité de développeur.

11    Récapitulatif

Ce chapitre résume les instructions données à votre intention dans ce document et est organisé en plusieurs récapitulatifs de nos standards de codage Java, rassemblés par rubrique. Ces rubriques sont :

  • Les conventions de dénomination Java
  • Les conventions de documentation Java
  • Les conventions de codage Java

Avant que nous résumions le reste des standards et des instructions décrites dans ce document de présentation technique, je souhaiterais réitérer la première instruction :

Si vous ne suivez pas un standard, documentez-le. Tous les standards, excepté celui-là, peuvent être ignorés. Si vous le faites, vous devez en documenter la raison, les implications potentielles liées au non suivi du standard, et toute condition qui pourrait/doit se dérouler avant que le standard puisse être appliqué à cette situation.

11.1    Conventions de dénomination Java

Pour les quelques exceptions ci-dessous, vous devez toujours utiliser des libellés en anglais et en langage clair lors de la désignation d'éléments. En outre, vous devez généralement utiliser des minuscules, mais la première lettre des noms de classe et des noms d'interface doit être en majuscule, ainsi que la première lettre d'un mot non initial.

Concepts généraux :

  • Utiliser des libellé en anglais et en langage clair.
  • Utiliser une terminologie relative au domaine.
  • Utiliser la casse mixte pour rendre les noms lisibles.
  • Utilisez les formes raccourcies avec parcimonie et intelligence.
  • Eviter les noms longs (un nom de moins de 15 caractères est correct).
  • Eviter les noms similaires ou qui ne diffèrent que par la casse.
  • Eviter les traits de soulignements.
Elément Convention de dénomination Exemple
Arguments/
paramètres
Utilisez une description en anglais et en langage clair de la valeur/objet ayant été transmis, par exemple en préfixant le nom avec "a" ou "an". Il est essentiel de trouver une méthode et de s'y tenir.
customer, account,
               - ou -
aCustomer, anAccount
Champs/
champs/
propriétés
Utilisez une desciption en anglais et en langage clair du champ, avec la première lettre en minuscule et la première lettre de tout mot non initial en majuscule.
firstName, lastName, 
warpSpeed
 
Fonctions membres getters booléens Tous les getters booléens doivent avoir le préfixe "is". Si vous suivez le standard de désignation pour les champs booléens décrits ci-dessus, vous lui donnez simplement le nom du champ.
isPersistent(), isString(), 
isCharacter() 
Classes Utilisez une description en anglais et en langage clair, avec une majuscule à la première lettre des mots.
Customer, SavingsAccount
Fichiers d'unités de compilation Utilisez le nom de la classe ou de l'interface, ou s'il y a plus d'une classe dans le fichier que la classe primaire, suffixez par ".java" pour spécifier qu'il s'agit d'un fichier code source.
Customer.java, 
SavingsAccount.java,
Singleton.java
Composants/
widgets
Utilisez une description en anglais et en langage clair décrivant l'utilisation du composant, avec le type de composant concaténé jusqu'à la fin.
okButton, customerList, 
fileMenu
Constructeurs Utilisez le nom de la classe.
Customer(), SavingsAccount()
Destructeurs Java n'a pas de destructeurs, mais appelle à la place la fonction membre finalize() avant qu'un objet soit jeté à la poubelle.
finalize()
Exceptions Il est généralement possible d'utiliser la lettre "e" pour représenter les exceptions.
e
Champs statiques finaux (constantes) Mettez toutes les lettres en majuscules avec des traits de soulignements entre les mots. Il est préférable d'utiliser les fonctions membres getters statiques finales car elles accroissent énormément la flexibilité.
MIN_BALANCE, DEFAULT_DATE
Fonctions membres getters Préfixez le nom du champ accédé par "get".
getFirstName(), getLastName(), 
getWarpSpeeed()
Interfaces Utilisez une description en anglais et en langage clair décrivant le concept que l'interface encapsule, avec une majuscule à la première lettre des mots. Il est habituel d'ajouter un suffixe au nom : "able", "ible" ou "er", mais ce n'est pas obligatoire.
Runnable, Contactable, 
Prompter, Singleton
Variables locales Utilisez des descriptions en anglais et en langage clair avec la première lettre en minuscule mais ne masquez pas les champs/champs existants. Par exemple, si vous avez un champ intitulé "firstName", vous ne devez pas avoir de variable locale intitulée "firstName".
grandTotal, customer, 
newAccount
Compteurs de boucles Il est généralement possible d'utiliser les lettres i, j ou k ou le nom counter.
i, j, k, counter
Packages Utilisez des descriptions en anglais et en langage clair, en utilisant la casse mixte, avec la première lettre de chaque mot en majuscule, et le reste en minuscule. Pour les packages globaux, inversez votre nom de domaine Internet et concaténez le nom du package.
java.awt,
com.ambysoft.www.
persistence.mapping
Fonctions membres Utilisez une description en anglais et en langage clair de ce que fait la fonction membre, en commençant par un verbe d'action lorsque c'est possible, avec la première lettre en minuscule.
openFile(), addAccount()
Fonctions membres setters Préfixez le nom du champ accédé par "set".
setFirstName(), setLastName(), 
setWarpSpeed()

11.2    Conventions de documentation Java

Concernant la documentation, si vous n'avez jamais vu ce code auparavant, prenez l'habitude de vous poser la question suivante : "quelles sont les informations nécessaires pour me permettre de comprendre le code en un temps raisonnable ?"

Concepts généraux :

11.2.1    Types de commentaires Java

Le tableau suivant décrit les trois types de commentaires Java et suggère l'utilisation pour chacun d'eux.

Type de commentaire Utilisation Exemple
Documentation Utilisez les commentaires de documentation juste avant les déclarations des interfaces, des classes, des fonctions membres et des champs pour les documenter. Les commentaires de documentation sont traités par javadoc, voir ci-dessous, pour créer une documentation externe à une classe. /**
Client : un client est toute personne ou organisation à laquelle nous vendons des services et des produits.
@author S.W. Ambler
*/
style C Utilisez les commentaires de style C pour documenter les lignes de code qui ne sont plus applicables, mais que vous souhaitez conserver si vos utilisateurs changent d'avis, ou que vous voulez provisoirement mettre hors fonction pendant le débogage. /*
Ce code a été commenté par B.Gustafsson, le 4 juin 1999 car il a été remplacé par le code précédent. Supprimez-le après deux années s'il n'est toujours pas applicable.
. . . (le code source )
*/
Une seule ligne Utilisez des commentaires sur une seule lignes, en interne, au sein des fonctions membres pour documenter la logique applicative, les sections de code et les déclarations des variables temporaires. // Appliquer une remise de 5 % à toutes les factures
// supérieures à 1 000 $, comme indiqué dans
// la campagne de générosité Sarek entamée en
// février 1995.

11.2.2    Que documenter ?

Le tableau suivant répertorie ce qu'il faut documenter pour chaque étape du code Java que vous écrivez.

Elément Documenter :
Arguments/
paramètres
Le type de paramètre

Ce à quoi il doit être utilisé

Toute restriction ou précondition

Les exemples

Champs/
champs/propriétés
Sa description

Documenter tous les invariants applicables

Les exemples

Les problèmes d'accès concurrent

Les décisions de visibilité

Classes L'objet de la classe

Les bogues connus

L'historique de développement et de maintenance de la classe

Documenter les invariants applicables

La stratégie d'accès concurrent

Unités de compilation Chaque classe ou interface définie dans la classe, avec une courte description

Le nom de fichier et/ou les informations d'identification

Informations de copyright

Fonction membre getter Documenter pourquoi l'initialisation paresseuse a été utilisée, si elle est applicable
Interfaces L'objet

Comment une interface doit et ne doit pas être utilisée

Variables locales Son utilisation et son objet
Fonctions membres : Documentation Ce que fait la fonction membre et pourquoi

Les paramètres qui doivent être passés à une fonction membre

Ce qu'une fonction membre retourne

Les bogues connus

Toute exception émise par une fonction membre

Les décisions de visibilité

Comment une fonction membre modifie l'objet

Ajouter un historique des modifications du code

Exemples sur la manière d'appeler la fonction membre, si c'est approprié

Les préconditions et postconditions applicables

Fonctions membres : commentaires internes Structures de contrôle

Ce que fait le code et pourquoi

Variables locales

Un code difficile ou complexe

L'ordre de traitement

Package La raison d'un package

Les classes dans le package

11.3    Conventions de codage Java (général)

Plusieurs conventions et standards sont indispensables à la maintenabilité et à l'amélioration de votre code Java. Dans 99,9% du temps, il est plus important de programmer pour des personnes, vos collaborateurs développeurs, que pour la machine. Il est prioritaire de rendre votre code compréhensible aux autres.

Cible de la convention Convention
Fonctions membres accesseurs Envisager l'utilisation de l'initialisation paresseuse pour les champs dans la base de données

Utiliser des accesseurs pour obtenir ou modifier tous les champs

Utiliser des accesseurs pour les "constantes"

Pour les collections, ajouter des fonctions membres pour insérer et supprimer des éléments

Chaque fois que c'est possible, définir les accesseurs sur protégés, pas sur publiques

Champs Les champs doivent toujours être déclarés privés

Ne pas accéder directement aux champs, utiliser plutôt des fonctions membres accesseurs

Ne pas utiliser de champs statiques finaux (constantes), mais plutôt des fonctions membres accesseurs

Ne pas masquer les noms

Toujours initialiser les champs statiques

Classes Réduire les interfaces publiques et protégées

Définir l'interface publique pour une classe avant de commencer à la coder

Déclarer les champs et les fonctions membres d'une classe dans l'ordre suivant :

  • constructeurs
  • finalize()
  • fonctions membres publiques
  • fonctions membres protégées
  • fonctions membres privées
  • Champ privé
Variables locales Ne pas masquer les noms

Déclarer une variable locale par ligne de code

Documenter les variables locales avec un commentaire en ligne

Déclarer les variables locales juste avant leur utilisation

Utiliser les variables locales pour une seule chose

Fonctions membres Documenter votre code

Diviser votre code en paragraphes

Utiliser des blancs, une ligne avant les structures de contrôle et deux avant les déclarations des fonctions membres

Une fonction membre doit être comprise en moins de trente secondes

Ecrire des lignes de commande courtes et simples

Restreindre autant que possible la visibilité d'une fonction membre

Spécifier l'ordre des opérations


12    Références

Code réf Informations réf
[AMB98] S.W. Ambler (1998). Building Object Applications That Work: Your Step-By-Step Handbook for Developing Robust Systems with Object Technology. New York : SIGS Books/Cambridge University Press.
[COA97] P. Coad et M. Mayfield (1997). Java Design: Building Better Apps & Applets. Upper Saddle River, NJ : Prentice Hall Inc.
[DES97] A. DeSoto (1997). Using the Beans Development Kit 1.0 February 1997: A Tutorial. Sun Microsystems.
[GOS96] J. Gosling, B. Joy, G. Steele (1996). The Java Language Specification. Reading, MA : Addison Wesley Longman Inc.
[GRA97] M. Grand (1997). Java Language Reference. Sebastopol, CA : O. Reilly & Associates, Inc.
[KAN97] J. Kanerva (1997). The Java FAQ. Reading, MA : Addison Wesley Longman Inc.
[KOE97] A. Koenig (1997). The Importance--and Hazards--of Performance Measurement. New York : SIGS Publications, Journal of Object-Oriented Programming, Janvier 1997, 9(8), pp. 58-60.
[LAF97] Laffra, C. (1997). Advanced Java: Idioms, Pitfalls, Styles and Programming Tips. Upper Saddle River, NJ : Prentice Hall Inc.
[LEA97] D. Lea (1997). Concurrent Programming in Java: Design Principles and Patterns. Reading, MA : Addison Wesley Longman Inc.
[MCO93] S. McConnell (1993). Code Complete: A Practical Handbook of Software Construction. Redmond, WA : Microsoft Press.
[MEY88] Meyer, B. (1988). Object-Oriented Software Construction. Upper Saddle River, NJ : Prentice Hall Inc.
[NAG95] Nagler, J. (1995). Coding Style and Good Computing Practices. http://wizard.ucr.edu/~nagler/coding_style.html
[SUN96] Sun Microsystems (1996). javadoc - The Java API Documentation Generator. Sun Microsystems.
[SUN97] Sun Microsystems (1997). 100% Pure Java Cookbook for Java Developers: Rules and Hints for Maximizing the Portability of Java Programs. Sun Microsystems.
[VIS96] Vision 2000 CCS Package and Application Team (1996). Coding Standards for C, C++, and Java. http://v2ma09.gsfc.nasa.gov/coding_standards.html

13    Glossaire

100 % : "label de qualité" de Sun indiquant qu'un applet, une application ou un package Java, fonctionne sur N'IMPORTE QUELLE plate-forme prenant en charge Java VM.

Accesseur : fonction membre modifiant ou renvoyant la valeur d'un champ. Egalement connu sous le nom de modificateur d'accès. Voir Getter et Setter.

Pattern d'analyse : pattern de modélisation décrivant une solution à un problème métier ou de domaine.

Antipattern : méthode de résolution d'un problème commun, qui s'est révélée avec le temps erronée ou très inefficace.

Argument : voir paramètre.

BDK: Beans Development Kit

Bloc : collection de zéro instruction ou plus, entourée d'accolades.

Accolades : les caractères { et }, respectivement accolade ouvrante et accolade fermante, sont utilisées pour indiquer le début et la fin d'un bloc.

Classe : définition ou canevas à partir desquels des objets sont instanciés.

Test de classe : fait de s'assurer qu'une classe et ses instances (objets) fonctionnent comme prévu.

CMVC: Configuration Management and Version Control (gestion de configuration et contrôle des versions)

Unité de compilation : fichier code source, soit physique sur un disque, soit "virtuel" stocké dans une base de données, où les classes et les interfaces sont déclarées.

Composant : widget d'interface tel qu'une liste, un bouton ou une fenêtre.

Getter de constante : fonction membre getter retournant la valeur d'une "constante," et peut, à son tour, être codée en dur ou calculée si nécessaire.

Constructeur : fonction membre exécutant toute initialisation nécessaire lorsqu'un objet est créé.

Confinement : un objet contient d'autres objets avec lesquels il collabore pour effectuer ses comportements. Cela peut être réalisé soit par l'utilisation de classes internes (JDK 1.1+) soit par le regroupement d'instances d'autres classes dans un objet (JDK 1.0+).

UC : unité centrale

Commentaires de style C : format de commentaire Java, /* & */, adopté à partir du langage C/C++ pouvant être utilisé pour créer des commentaires de plusieurs lignes. Couramment utilisé pour "documenter" des lignes de code inutiles ou indésirables lors d'une phase de test.

Pattern de conception : pattern de modélisation décrivant une solution à un problème de conception.

Destructeur : fonction membre de classe C++ utilisée pour supprimer un objet de la mémoire une fois qu'il n'est plus nécessaire. Puisque Java gère sa propre mémoire, ce type de fonction membre n'est pas nécessaire. Toutefois, Java prend en charge une fonction membre dont le concept est similaire, intitulée finalize().

Commentaires de documentation : format de commentaire Java, /** & */, pouvant être traité par javadoc pour fournir une documentation externe à un fichier classe. La documentation principale des interfaces, des classes, des fonctions membres et des champs doit être écrite avec des commentaires de documentation.

Champ : variable, soit un type de données littéral soit un autre objet, décrivant une classe ou une instance de classe. Les champs d'instance décrivent des objets (instances) et les champs statiques décrivent des classes. Les champs sont également appelés variables de champs et propriétés.

finalize() : fonction membre automatiquement appelée lors de la récupération de place, avant qu'un objet soit supprimé de la mémoire. L'objectif de cette fonction membre est d'effectuer tout apurement nécessaire, tel que la fermeture de fichiers ouverts.

Récupération de place : gestion automatique de la mémoire, où les objets qui ne sont plus référencés sont automatiquement supprimés de la mémoire.

Getter : type de fonction membre accesseur retournant la valeur d'un champ. Un getter peut être utilisé pour répondre à la valeur d'une constante, ce qui est souvent préférable à l'implémentation de la constante en tant que champ statique car c'est une méthode plus flexible.

HTML : langage de balisage hypertexte, un format standard métier pour la création de pages Web.

Mise en retrait : voir division en paragraphes.

Commentaires en ligne : utilisation d'un commentaire en ligne pour documenter une ligne de code source où le commentaire suit immédiatement le code, sur la même ligne que le code. Les commentaires sur une seule ligne sont généralement utilisés dans ce but, même si les commentaires de style C peuvent également être employés.

Interface : définition d'une signature commune, comprenant à la fois des champs et des fonctions membres, qu'une classe implémentant une interface doit prendre en charge. Les interfaces promeuvent le polymorphisme par composition.

E/S : entrée/sortie

Invariant : ensemble d'assertions concernant une instance ou une classe qui doivent être vérifiées à chaque période sans modifications, telles que les périodes précédant et suivant l'appel d'une fonction membre sur l'objet ou la classe.

Java : standard métier, langage de développement orienté objet, adapté au développement d'applications pour Internet et d'applications qui doivent opérer sur une large variété de plates-formes de traitement.

javadoc : utilitaire inclus dans le kit JDK qui traite un fichier code source Java et produit un document externe, au format HTML, décrivant le contenu du fichier code source, sur la base des commentaires de documentation du fichier code.

JDK : Java Development Kit

Initialisation paresseuse : technique où un champ est initialisé dans sa fonction membre getter correspondante, dès sa première utilisation. L'initialisation paresseuse est utilisée lorsqu'un champ n'est pas souvent nécessaire et qu'il requiert une grande quantité de mémoire à stocker ou nécessite d'être lu à partir de la mémoire permanente.

Variable locale : variable définie au sein de la portée d'un bloc, souvent une fonction membre. La portée d'une variable locale est le bloc dans lequel elle est définie.

Fonction membre : élément de code exécutable associé à une classe ou aux instances d'une classe. Une fonction membre doit être considérée comme l'équivalent orienté objet d'une fonction.

Signature d'une fonction membre : voir signature.

Test de méthode : fait de s'assurer qu'une fonction membre (fonction membre) fonctionne comme prévu.

Masquage de nom : fait d'utiliser le même nom ou un nom similaire pour un champ, une variable ou un argument comme pour un nom de plus grande portée. L'abus le plus courant concernant le masquage de nom est de désigner une variable locale comme un champ d'instance. Le masquage de nom doit être évité car il rend votre code plus difficile à comprendre, et sujet aux bogues.

Surcharge : une fonction membre se dit surchargée lorsqu'elle est définie plus d'une fois dans la même classe (ou dans une sous-classe). La seule différence porte sur la signature de chaque définition.

Sustitution : une fonction membre est dite sustituée lorsqu'elle est redéfinie dans une sous-classe et qu'elle a la même signature que la définition d'origine.

Package : collection de classes associées.

Division en paragraphes : technique où vous mettez en retrait d'une unité le code dans la portée d'un bloc de code, généralement par une tabulation horizontale, afin de le distinguer du code situé en dehors du bloc de code. La division en paragraphes permet d'accroître la lisibilité de votre code.

Paramètre : argument transmis à une fonction membre, un paramètre peut être un type défini tel qu'une chaîne, un int ou un objet.

postcondition : propriété ou assertion qui sera vérifiée après la fin du fonctionnement d'une fonction membre.

précondition : contrainte sous laquelle une fonction membre va fonctionner correctement.

Propriété : voir champ.

Setter : fonction membre accesseur modifiant la valeur d'un champ.

Signature : combinaison du type des paramètres, s'il y en a, et de leur ordre de transmission à une fonction membre. On parle également de signature de fonction membre.

Commentaires sur une seule ligne : format de commentaire Java, // , adopté à partir du langage C/C++, couramment utilisé pour la documentation interne des fonctions membres de la logique applicative.

Balises : convention consistant à marquer des sections spécifiques de commentaires de documentation qui seront traités par javadoc, afin de générer des commentaires professionnels. @see et @author sont des exemples de balises.

Routine de test : collection de fonctions membres servant à tester votre code.

UML: langage unifié de modélisation d'objets, qui est une notation de modélisation standard métier.

Visibilité : technique utilisée pour indiquer le niveau d'encapsulation d'une classe, d'une fonction membre ou d'un champ. Les mots clés : publique, protégé et privé, peuvent être utilisés pour définir la visibilité.

Blanc : interlignes, espaces et onglets ajoutés à votre code pour accroître sa lisibilité.

Widget : voir composant.