Un bref résumé des meilleures pratiques de codage Java

basé sur les normes de codage d'Oracle, Google, Twitter et Spring Framework

L’objectif de cet article est de vous donner un résumé rapide des choses à faire et à éviter en préférant et en évitant les normes de codage de géants de la technologie tels que Oracle, Google, Twitter et Spring Framework.

Vous êtes peut-être d’accord ou pas avec certaines des meilleures pratiques présentées ici, et c’est tout à fait correct tant qu’une norme de codage est en place.

Pourquoi coder les normes en premier lieu? Il y a beaucoup de bonnes raisons si vous utilisez Google et je vous laisse avec l'illustration suivante

Le document de normes de codage peut être long et ennuyeux. Cet article reprend les éléments des conventions de codage de Google, Oracle, Twitter et Spring. Son objectif est de vous fournir un ensemble de pratiques faciles à suivre et moins ennuyeuses pour rendre votre code facile à lire et à gérer.

Presque toujours, vous rejoindrez des équipes travaillant sur des logiciels existants et il y a de bonnes chances que la plupart des auteurs aient quitté ou basculé vers des projets différents, vous laissant bloqués avec des portions de code qui vous font interroger l'humanité.

Laissez-nous plonger dans les meilleures pratiques de différentes normes de codage.

Fichier source Java

Les éléments suivants sont considérés comme les meilleures pratiques en matière de fichiers source Java:

  • La longueur du fichier source est inférieure à 2 000 lignes de code
  • Le fichier source est organisé avec un commentaire de documentation, une déclaration de package, suivi d'un commentaire de classe, des importations groupées (dernière statique), une signature de classe / interface, etc. comme indiqué ci-dessous.
package com.example.model;
/ **
 * Perspective sans implémentation à lire par les développeurs
 * qui n'a peut-être pas nécessairement le code source sous la main
 *
 * @author x, y, z
 * @Date
 * @version
 * @droits d'auteur
 *
 * /
import com.example.util.FileUtil;
/ *
 * Commentaire facultatif spécifique à la classe
 *
 * /
Classe publique SomeClass {
  // Variables statiques par ordre de visibilité
  public static final Integer PUBLIC_COUNT = 1;
  statique final Integer PROTECTED_COUNT = 1;
  private statique final Integer PRIVATE_COUNT = 1;
  // Variables d'instance par ordre de visibilité
  nom de chaîne publique;
  String postalCode;
  adresse de chaîne privée;
  // Constructeur et surchargé dans un ordre séquentiel
  public SomeClass () {}
  public SomeClass (nom de chaîne) {
    this.name = name;
  }
  // Méthodes
  public String doSomethingUseful () {
    renvoyer "quelque chose d'utile";
  }
  // getters, setters, equals, hashCode et toString à la fin
}

Appellation

Les noms de classe et d'interface sont CamelCase et il est recommandé d'utiliser le mot entier et d'éviter les acronymes / abréviations. Par exemple, la classe Raster ou la classe ImageSprite

  • Paquetage - nomme com.deepspace sur com.deepSpace ou com.deep_space
  • Les noms de fichier sont CamelCase et se terminent par .java correspondant au nom de la classe. Il existe une classe publique par fichier avec chaque classe de niveau supérieur dans son fichier.
  • Méthode - les noms doivent être des verbes en casse mixte avec chaque mot interne en majuscule, par exemple run (); ou runFast ();
  • Constantes - devrait être en majuscule avec “_” séparant chaque mot, par exemple int MIN_WIDTH = 44; et int MAX_WIDTH = 99;
  • Variable - nom qui indique au lecteur du programme ce que la variable représente, c’est-à-dire si vous stockez une note de test, choisissez grade vs var1. Gardez les noms de variables courts pour éviter d’inclure des métadonnées.
// préfère (✔) - noms de variables abrégés et décrivent ce qu'ils stockent
int schoolId;
int [] filterSchoolIds;
int [] uniqueSchooldIds;
Mapper  usersById;
Valeur de chaîne;
// Avoid (x) - Nom de variable trop détaillé
int schoolIdentificationNumber;
int [] userProvidedSchoolIds;
int [] schoolIdsAfterRemovingDuplicates;
Map  idToUserMap;
String valueString;

Rappelez-vous - le nom de la variable doit être court et indiquer facilement au lecteur quelle valeur il représente. Utilisez votre jugement.

Préférer et éviter

Le formatage et l'indentation sont tous centrés sur l'organisation de votre code afin de le rendre facile à lire. Il comprend l'espacement, la longueur de ligne, les retours à la ligne, les coupures, etc.

  • Indentation - Utilisez 2 ou 4 espaces et restez cohérent
  • Longueur de ligne - Jusqu'à 70 à 120 caractères, en fonction de la lisibilité. Il est important d’éliminer le défilement horizontal et de placer des sauts de ligne après une virgule et un opérateur.

Méthodes - Voici une liste des meilleures pratiques

// Préférer (✔) les sauts de ligne sont arbitraires et coupent après une virgule.
Chaîne téléchargerAnternet (Internet internet, Tubes tubes,
    Blogosphère de blogosphère, Quantité  Bande passante) {
  tubes.download (Internet);
}
// Avoid (x) Difficile de différencier les arguments de méthode au corps de la méthode
Chaîne téléchargerAnternet (Internet internet, Tubes tubes,
    Blogosphère de blogosphère, Quantité  Bande passante) {
    tubes.download (Internet);
}
// Préférer () Ajouter 8 (doubles de 2 ou 4) espaces pour un retrait profond
horkingLongMethodName privé synchronisé statique (int anArg,
        Object anotherArg, String yetAnotherArg,
        Object etStillAnother) {
  ...
}
// Prefer () Numérisation facile et espace supplémentaire dans les colonnes.
public String downloadAnInternet (
    Internet Internet,
    Tubes tubes,
    Blogs de blogosphère,
    Quantité  bande passante) {
  tubes.download (Internet);
  ...
}
Un test unitaire aurait attrapé que

If-checks - L'OMI écrit du code bien formaté permet de repérer facilement les fautes de frappe et les erreurs commises par l'auteur et les réviseurs de code.

// Eviter (x) Ne pas omettre {}
si (condition)
  déclaration;
// éviter (x)
si (x <0) négatif (x);
// éviter (x)
si (a == b && c == d) {
...
}
// Préfère ()
si ((a == b) && (c == d)) {
...
}
// Préfère ()
si (condition) {
  les déclarations;
} sinon si (condition) {
  les déclarations;
} sinon si (condition) {
  les déclarations;
}
// éviter (x)
si ((condition1 && condition2)
    || (condition3 && condition4)
    ||! (condition5 && condition6)) {// BAD WRAPS
    doSomethingAboutIt (); // RENDRE CETTE LIGNE FACILE À MANQUER
}
// Préfère ()
si ((condition1 && condition2)
        || (condition3 && condition4)
        ||! (condition5 && condition6)) {
    doSomethingAboutIt ();
}

Opérateur ternaire - Et voici les pratiques recommandées

alpha = (aLongBooleanExpression)? beta: gamma;
alpha = (aLongBooleanExpression)? bêta
        : gamma;
alpha = (aLongBooleanExpression)
        ? bêta
        : gamma;

Switch - Quand il s’agit de changer, il est préférable de

  • Toujours avoir un cas par défaut même sans code
  • Utilisez / * passe par * / pour indiquer que le contrôle passe au cas suivant
commutateur (condition) {
  cas ABC:
    les déclarations;
  / * tombe à travers * /
  affaire DEF:
    les déclarations;
    Pause;
  défaut:
    les déclarations;
     Pause;
}

Messages d'exception - Lors du lancement d'une exception, voici des exemples de messages corrects et peu indentés.

// Avoid (x) - Pas facile à lire
jette new IllegalStateException ("Echec du traitement de la requête" + request.getId ()
    + "pour l'utilisateur" + user.getId () + "requête: '" + query.getText ()
    + "'");
// préfère () - assez facile à lire
jeter la nouvelle IllegalStateException ("Echec du traitement"
    + "request" + request.getId ()
    + "pour l'utilisateur" + user.getId ()
    + "requête: '" + query.getText () + "'");

Itérateurs et flux - Les flux deviennent de plus en plus courants et peuvent parfois être très complexes, il est donc important d’indenter pour faciliter la lecture.

// Avoid (x) - Pas facile à lire
Iterable  modules = ImmutableList.  builder (). Add (new LifecycleModule ())
    .add (new AppLauncherModule ()). addAll (application.getModules ()). build ();
// préfère () - assez facile à lire
Iterable  modules = ImmutableList.  builder ()
    .add (new LifecycleModule ())
    .add (new AppLauncherModule ())
    .addAll (application.getModules ())
    .construire();
Il suffit de suivre une norme de codage - vraiment

Déclarations et assignations - Une déclaration par ligne est recommandée car elle encourage les commentaires comme indiqué ci-dessous.

// Préfère ()
int niveau; // niveau d'indentation
int sizeMeter; // taille de la table
// éviter (x) en faveur de ci-dessus
int level, sizeMeter;
// Préférer () - Inclure l'unité dans le nom ou le type de la variable
long pollIntervalMs;
int fileSizeGb;
Amount  fileSize;
// éviter les types de mélange (x)
int foo, fooarray [];
// Avoid (x) - Ne sépare pas avec une virgule
Format.print (System.out, «error»), exit (1);
// Eviter (x) affectation multiple
fooBar.fChar = barFoo.lchar = 'c';
// Évitez (x) les affectations incorporées pour améliorer les performances // ou enregistrez une ligne. Je suis coupable de faire ceci :(
d = (a = b + c) + r;
// préfère (✔) ci-dessus
a = b + c;
d = a + r;
// Préfère ()
String [] args
// éviter (x)
Arguments à cordes []
// préfère () Longue utilisation de "L" au lieu de "l" pour éviter toute confusion avec 1
long délai d'attente = 3000000000L;
// Avoid (x) - Difficile de dire que la dernière lettre est l et non 1
long délai d'attente = 3000000000l;

Placez les déclarations uniquement au début des blocs (Un bloc est un code entouré d'accolades {et}). N'attendez pas pour déclarer les variables jusqu'à leur première utilisation; cela peut dérouter le programmeur imprudent et gêner la portabilité du code dans la portée.

// Prefer () déclare au début du bloc.
public void doQuelque chose () {
  dans ce qui est représenté; // début du bloc de méthode
  si (condition) {
    int someFlag; // début du bloc “if”
    …
  }
}

Il est également important d'éviter les déclarations locales qui masquent les déclarations des niveaux supérieurs et d'éviter les confusions, comme indiqué ci-dessous.

int compte;
...
public void doQuelque chose () {
  si (condition) {
    int compte; // ÉVITER!
    ...
  }
  ...
}

Espacement et sauts de ligne - Évitez la tentation de sauvegarder 1 à 2 lignes de code au détriment de la lisibilité. Voici toutes les meilleures pratiques en matière d'espacement et de lignes vides (Un espace blanc fait une différence)

  • Une (1) ligne vide entre les méthodes et les développeurs Spring recommande deux (2) lignes vides après les constructeurs, le bloc statique, les champs et la classe interne
  • Opérateurs de pavé spatial, c.-à-d. Utiliser int foo = a + b + 1; sur int toto = a + b + 1;
  • Séparez tous les opérateurs binaires, sauf “.” Des opérandes utilisant un espace
  • L'accolade ouverte «{» apparaît à la fin de la même ligne que l'instruction de déclaration ou la méthode et l'accolade fermante «}» démarre une ligne elle-même en retrait
// Prefer (✔) - Espace après "while" et avant "("
while (true) {
  ...
}
// Avoid (x) - Contrairement à aucun espace
while (true) {
  ...
}
// Prefer (✔) - Pas d'espace entre "quelque chose" et "("
public void doQuelque chose () {
  ...
}
// Avoid (x) - Contrairement à l'espace ci-dessus
public void doQuelque chose () {
  ...
}
// Prefer () - Ajoute un espace après un argument
vide publique doQuelque chose (int a, int b) {
  ...
}
// Préférence () - Espace entre l'opérande et les opérateurs (c'est-à-dire +, =)
a + = c + d;
a = (a + b) / (c * d);
tandis que (d ++ = s ++) {
  n ++;
}

Documentation et commentaires

Il est à noter que presque tout le code va changer tout au long de sa vie et que parfois quelqu'un essaye de comprendre ce qu'un bloc de code complexe, une méthode ou une classe est destiné à faire à moins d'être clairement décrit. La réalité est presque toujours comme suit

Il arrive que le commentaire sur un morceau de code complexe, une méthode ou une classe n’ajoute aucune valeur et ne serve pas son objectif. Cela se produit généralement lorsque vous commentez pour le plaisir de le faire.

Les commentaires doivent être utilisés pour donner un aperçu du code et fournir des informations supplémentaires qui ne sont pas facilement disponibles dans le code lui-même. Commençons. Il y a deux types de commentaires

Commentaires d'implémentation - sont destinés à commenter le code ou à commenter une implémentation particulière du code.

Les commentaires de la documentation - sont destinés à décrire la spécification du code dans une perspective sans implémentation, à lire par les développeurs qui ne disposent pas nécessairement du code source.

La fréquence des commentaires reflète parfois la mauvaise qualité du code. Lorsque vous vous sentez obligé d’ajouter un commentaire, envisagez de réécrire le code pour le rendre plus clair.

Types de commentaires d'implémentation

Il y a quatre (4) types de commentaires d'implémentation, comme indiqué ci-dessous.

  • Bloquer le commentaire - voir exemple ci-dessous
  • Commentaire sur une seule ligne - lorsque le commentaire ne dépasse pas une ligne
  • Commentaires de fin - Très court commentaire déplacé à l'extrémité droite
  • Commentaire de fin de ligne - commence un commentaire qui continue jusqu'à la nouvelle ligne. Il peut commenter une ligne complète ou seulement une ligne partielle. Il ne doit pas être utilisé sur plusieurs lignes consécutives pour les commentaires de texte; Cependant, il peut être utilisé sur plusieurs lignes consécutives pour commenter des sections de code.
// Bloquer le commentaire
/ *
 * Usage: Fournit une description des fichiers, méthodes, structures de données.
 * et algorithmes. Peut être utilisé au début de chaque fichier et
 * avant chaque méthode. Utilisé pour les longs commentaires qui ne correspondent pas à un
 * une seule ligne. 1 Ligne vide pour continuer après le commentaire de bloc.
 * /
// Commentaire sur une seule ligne
si (condition) {
 / * Traite la condition. * /
  ...
}
// commentaire de fin
si (a == 2) {
 retourne VRAI; /* cas particulier */
} autre {
 return isPrime (a); / * ne fonctionne que pour impairs a * /
}
// Commentaire de fin de ligne
si (foo> 1) {
  // Faites un double retournement.
  ...
} autre {
  retourne faux; // Explique pourquoi ici.
}
// if (bar> 1) {
//
// // fait un triple retournement.
// ...
//}
//autre
// retourne faux;

Commentaires de la documentation (Javadoc, par exemple)

Javadoc est un outil qui génère de la documentation HTML à partir de votre code java en utilisant les commentaires commençant par / ** et finissant par * / - voir Wikipedia pour plus de détails sur le fonctionnement ou les explications de Javadoc.

Voici un exemple de Javadoc

/ **
 * Retourne un objet Image qui peut ensuite être peint à l'écran.
 * L'argument url doit spécifier un {@link URL} absolu. Le nom
 * argument est un spécificateur relatif à l'argument url.
 * 

 * Cette méthode retourne toujours immédiatement, que le  * l'image existe. Lorsque cette applet tente de dessiner l'image sur  * l'écran, les données seront chargées. Les primitives graphiques  * qui dessine l'image va progressivement peindre sur l'écran.  *  * @param url une URL absolue donnant l'emplacement de base de l'image  * @param nomme l'emplacement de l'image, par rapport à l'argument url  * @retournez l'image à l'URL spécifiée  * @see Image  * /  public Image getImage (URL URL, nom de chaîne) {         essayer {             retourne getImage (nouvelle URL (url, nom));         } catch (MalformedURLException e) {             return null;         }  }

Et ce qui précède aboutirait à un code HTML comme suit lorsque javadoc est exécuté contre le code qui contient ce qui précède

Voir ici pour plus

Voici quelques balises clés que vous pouvez utiliser pour améliorer la qualité de la documentation Java générée.

@author => @author Raf
@code => {@code A  C}
@deprecated => @deprecated deprecation-message
@exception => @exception IOException levée lorsque
@link => {@link package.class # member label}
@param => @param description du nom du paramètre
@return => Ce que la méthode retourne
@see => @see "string" OU @see  
@since => Pour indiquer la version lorsqu'une méthode accessible au public est ajoutée

Pour une liste complète et une description plus détaillée, voir ici

La norme de codage de Twitter déconseille l’utilisation de la balise @author

Le code peut changer de main plusieurs fois au cours de sa vie et très souvent, l'auteur original d'un fichier source est sans importance après plusieurs itérations. Nous trouvons qu’il est préférable de faire confiance à l’historique des validations et aux fichiers OWNERS pour déterminer la propriété d’un corps de code.

Vous trouverez ci-dessous des exemples de la manière dont vous pourriez écrire un commentaire de documentation intéressant, comme décrit dans la norme de codage de Twitter.

// Mauvais.
// - La doc ne dit rien que la déclaration de méthode n'a pas.
// - Ceci est le 'document de remplissage'. Cela passerait les vérifications de style, mais
n'aide personne.
/ **
 * Divise une chaîne.
 *
 * @param s Une chaîne.
 * @retour Une liste de chaînes.
 * /
List  split (String s);
// Meilleur.
// - Nous savons en quoi la méthode se divise.
// - Encore un comportement indéfini.
/ **
 * Divise une chaîne sur les espaces blancs.
 *
 * @param s La chaîne à scinder. Une chaîne {@code null} est traitée comme une chaîne vide.
 * @return Une liste des parties de l'entrée délimitées par des espaces.
 * /
List  split (String s);
// Génial.
// - Couvre encore un autre cas.
/ **
 * Divise une chaîne sur les espaces blancs. Caractères d'espacement répétés
 * sont effondrés.
 *
 * @param s La chaîne à scinder. Une chaîne {@code null} est traitée comme une chaîne vide.
 * @return Une liste des parties de l'entrée délimitées par des espaces.
 * /
List  split (String s);

Il est important d’être professionnel en matière de rédaction de commentaires.

// éviter (x)
// Je déteste tellement xml / soap, pourquoi ne peut-il pas le faire pour moi !?
essayer {
  userId = Integer.parseInt (xml.getField ("id"));
} catch (NumberFormatException e) {
  ...
}
// Préfère ()
// TODO (Jim): Validez la validation du champ dans une bibliothèque.
essayer {
  userId = Integer.parseInt (xml.getField ("id"));
} catch (NumberFormatException e) {
  ...
}

Et il est important de garder à l'esprit de ne pas documenter la méthode surchargée sauf si la mise en œuvre a changé.

Et voici quelques points à garder à l'esprit

  • Évitez les importations avec des caractères génériques - comme décrit dans les normes de codage de Twitter, cela rend la source de la classe moins claire. Je travaille en équipe avec un mélange d'utilisateurs d'Eclipse et d'IntelliJ et j'ai découvert qu'Eclipse supprime les importations de wildcard et qu'IntelliJ l'introduit. Il y a probablement une option pour le désactiver, je voulais juste souligner la valeur par défaut pour les deux.
  • Toujours utiliser les annotations @Override lors de la substitution
  • Encourager l'utilisation de @Nullable lorsqu'un champ ou une méthode renvoie null
  • Utilisez des commentaires spéciaux pour vos travaux futurs et n'oubliez pas de laisser une référence à vous-même pour que les autres sachent à qui poser leur question en Y au lieu de deviner, de la supprimer ou de vérifier le blit de git pour trouver qui l'a ajoutée. Certains IDE tels qu'Eclipse et IntelliJ aident également à les répertorier pour un accès facile ainsi qu'un rappel.
// FIXME (Raf): Un message exploitable décrit ce qui doit être fait
// TODO (Raf): Un message exploitable décrit ce qui doit être fait

Le jeu final consiste à écrire du code qui facilite la vie des futurs auteurs et responsables de la maintenance.

La fin du jeu

Autres matériels de lecture pertinents

Une liste d'articles pertinents pour la rédaction de code qui sont propres, bien structurés, faciles à lire et faciles à gérer. Si vous souhaitez en savoir plus, recommandez certainement ce qui suit

et une autre bonne liste de conseils pour écrire du code propre