Cet article répertorie quelques erreurs typiques que je vois dans le code Java de mes collègues autour de moi. Évidemment, l'analyse de code statique (notre équipe utilise qulice) ne pourra pas trouver tous les problèmes, c'est pourquoi je les liste ici.
Si vous pensez qu'il manque quelque chose, faites-le moi savoir et je serai heureux de l'ajouter.
Toutes ces erreurs répertoriées ci-dessous sont essentiellement liées à la programmation orientée objet, en particulier à la POO de Java.
Nom de la classe
Lisez ce court article "Que sont les objets". Une classe doit être une entité abstraite dans la vie réelle, et non des « validateurs », des « contrôleurs » et des « gestionnaires ». Si le nom de votre classe se termine par « euh », c’est une mauvaise conception.
Bien entendu, les classes d'outils sont également anti-modèles, comme StringUtils, FileUtils et IOUtils d'Apache. Tout ce qui précède sont des exemples de mauvaise conception. Lectures complémentaires : Alternatives aux classes d'outils en POO.
Bien entendu, n’utilisez pas de préfixes ou de suffixes pour distinguer les classes des interfaces. Par exemple, ces noms sont erronés : IRecord, IfaceEmployee ou RecordInterface. D'une manière générale, le nom de l'interface doit être le nom de l'entité réelle et le nom de la classe doit décrire les détails de son implémentation. S'il n'y a rien de spécial dans l'implémentation, vous pouvez l'appeler Default, Simple ou quelque chose de similaire. Par exemple:
Copiez le code comme suit :
la classe SimpleUser implémente User {} ;
la classe DefaultRecord implémente Record {} ;
classe Implémente avec suffixe Nom {} ;
la classe Validated implémente le contenu {} ;
nom de la méthode
Les méthodes peuvent renvoyer des valeurs ou être nulles. Si une méthode renvoie une valeur, son nom doit décrire ce qu'elle renvoie, par exemple (n'utilisez jamais le préfixe get) :
Copiez le code comme suit :
boolean isValid (nom de la chaîne);
Contenu de la chaîne();
int ageOf(Fichier fichier);
S'il revient nul, son nom doit expliquer ce qu'il fait. Par exemple:
Copiez le code comme suit :
void save(Fichier fichier);
processus nul(Travail de travail);
void append (fichier, ligne de chaîne);
Il n'y a qu'une seule exception aux règles que nous venons de mentionner : la méthode de test de JUnit ne compte pas. Ceci sera discuté ci-dessous.
Le nom de la méthode de test
Dans les cas de test JUnit, le nom de la méthode doit être une instruction anglaise sans espaces. Ce sera plus clair avec un exemple :
Copiez le code comme suit :
/**
* HttpRequest peut renvoyer son contenu en Unicode.
* @throws Exception Si le test échoue
*/
public void returnItsContentInUnicode() lève une exception {
}
La première phrase de votre JavaDoc doit commencer par le nom de la classe que vous souhaitez tester, suivi de can. Par conséquent, votre première phrase devrait être quelque chose comme « quelqu'un peut faire quelque chose ».
Le nom de la méthode est également le même, mais sans le thème. Si j'ajoute un sujet au milieu du nom de la méthode, j'obtiens une phrase complète, comme dans l'exemple ci-dessus : "HttpRequest renvoie son contenu en unicode".
Veuillez noter que le nom de la méthode de test ne commence pas par can. Seuls les commentaires dans JavaDoc commenceront par can. De plus, les noms de méthodes ne doivent pas commencer par un verbe.
En pratique, il est préférable de déclarer la méthode de test pour lancer une exception.
nom de la variable
Évitez de combiner des noms de variables, tels que timeOfDay, firstItem ou httpRequest. Cela est vrai pour les variables de classe et les variables au sein des méthodes. Les noms de variables doivent être suffisamment longs pour éviter toute ambiguïté dans leur portée visible, mais pas trop longs si possible. Le nom doit être un nom au singulier ou au pluriel, ou une abréviation appropriée. Par exemple:
Copiez le code comme suit :
List<String> noms ;
void sendThroughProxy (fichier, protocole de protocole);
Contenu du fichier privé ;
demande publique HttpRequest ;
Parfois, si le constructeur enregistre les paramètres d'entrée dans un objet nouvellement initialisé, les noms de ses paramètres et attributs de classe peuvent entrer en conflit. Dans ce cas, ma suggestion est de supprimer les voyelles et d’utiliser des abréviations.
Exemple:
Copiez le code comme suit :
Message de classe publique {
destinataire de chaîne privé ;
Message public (chaîne reçue) {
this.recipient = rcpt;
}
}
Plusieurs fois, vous pouvez savoir quel nom doit être donné à la variable en regardant son nom de classe. Utilisez simplement sa forme minuscule, qui est fiable comme ceci :
Copiez le code comme suit :
Fichier de fichier ;
Utilisateur utilisateur ;
Succursale ;
Cependant, vous ne devriez jamais faire cela avec des types primitifs, tels qu'un nombre entier ou une chaîne de caractères.
S'il existe plusieurs variables de nature différente, envisagez d'utiliser des adjectifs. Par exemple:
Copiez le code comme suit :
Contact de chaîne (Chaîne à gauche, Chaîne à droite) ;
Constructeur
Sans tenir compte des exceptions, il ne devrait y avoir qu'un seul constructeur utilisé pour stocker les données dans des variables d'objet. D'autres constructeurs appellent ce constructeur avec des paramètres différents. Par exemple:
Copiez le code comme suit :
Serveur de classe publique {
adresse de chaîne privée ;
serveur public (chaîne uri) {
this.address = uri;
}
serveur public (URI uri) {
this(uri.toString());
}
}
variable unique
Les variables jetables doivent être évitées à tout prix. Ce que j'entends par « unique » ici, c'est une variable qui n'est utilisée qu'une seule fois. Par exemple, celui-ci :
Copiez le code comme suit :
Nom de la chaîne = "data.txt" ;
renvoie un nouveau fichier (nom);
Les variables ci-dessus ne sont utilisées qu'une seule fois, ce code peut donc être restructuré comme ceci :
Copiez le code comme suit :
renvoyer un nouveau fichier ("data.txt");
Parfois, dans de rares cas, principalement pour un formatage plus esthétique, des variables jetables peuvent être utilisées. Cependant, cela doit être évité autant que possible.
anormal
Inutile de dire que vous ne devriez jamais avaler une exception vous-même, mais la laisser passer le plus haut possible. Les méthodes privées doivent toujours lever des exceptions vérifiées.
N'utilisez pas d'exceptions pour le contrôle de flux. Par exemple, le code suivant est erroné :
Copiez le code comme suit :
taille entière ;
essayer {
taille = this.fileSize();
} catch (IOException ex) {
taille = 0 ;
}
Alors, que devez-vous faire si IOException vous demande « Le disque est plein » ? Pensez-vous toujours que la taille du fichier est de 0 et poursuivez-vous le traitement ?
échancrure
Concernant l'indentation, la règle principale est que la parenthèse ouvrante se termine à la fin de la ligne ou soit fermée sur la même ligne (l'inverse est vrai pour les parenthèses fermantes). Par exemple, ce qui suit est incorrect car le premier crochet ouvrant n’est pas fermé sur la même ligne et d’autres caractères le suivent. La deuxième parenthèse pose également problème car elle est précédée de caractères mais la parenthèse ouvrante correspondante n'est pas sur la même ligne :
Copiez le code comme suit :
fichier final file = nouveau fichier (répertoire,
"fichier.txt");
L'indentation correcte devrait ressembler à ceci :
Copiez le code comme suit :
StringUtils.join(
Arrays.asList(
"première ligne",
"deuxième ligne",
StringUtils.join(
Arrays.asList("a", "b")
)
),
"séparateur"
);
La deuxième règle importante concernant l'indentation est que vous devez essayer d'écrire autant de caractères que possible sur une ligne en même temps - la limite supérieure est de 80 caractères. L'exemple ci-dessus ne satisfait pas ce point, il peut également être réduit :
Copiez le code comme suit :
StringUtils.join(
Arrays.asList(
"première ligne", "deuxième ligne",
StringUtils.join(Arrays.asList("a", "b"))
),
"séparateur"
);
constantes redondantes
Les constantes de classe doivent être utilisées lorsque vous souhaitez partager des informations au sein des méthodes d'une classe. Les informations doivent être uniques à votre classe. N'utilisez pas de constantes pour remplacer des chaînes ou des littéraux numériques - c'est une très mauvaise pratique et polluera votre code. Les constantes (comme tout objet en POO) devraient avoir leur propre signification dans le monde réel. Voyons ce que signifient ces constantes dans la vraie vie :
Copiez le code comme suit :
Document de classe {
private static final String D_LETTER = "D"; // mauvaise pratique
private static final String EXTENSION = ".doc" // bonne pratique
}
Une autre erreur courante consiste à utiliser des constantes dans les tests unitaires pour éviter les chaînes redondantes ou les littéraux numériques dans les méthodes de test. Ne faites pas cela ! Chaque méthode de test doit avoir ses propres valeurs d'entrée uniques.
Utilisez un nouveau texte ou de nouvelles valeurs dans chaque nouvelle méthode de test. Ils sont indépendants les uns des autres. Alors pourquoi partagent-ils toujours les mêmes constantes d’entrée ?
Couplage des données de test
Voici un exemple de couplage de données dans une méthode de test :
Copiez le code comme suit :
Utilisateur utilisateur = nouvel utilisateur("Jeff");
// peut-être un autre code ici
MatcherAssert.assertThat(user.name(), Matchers.equalTo("Jeff"));
Dans la dernière ligne, « Jeff » est couplé à la même chaîne littérale que dans la première ligne. Si après quelques mois, quelqu'un souhaite modifier la valeur de la troisième ligne, il devra alors passer du temps à découvrir où "Jeff" est également utilisé dans la même méthode.
Pour éviter cela, vous feriez mieux d'introduire une variable.