Le contexte
Je me suis récemment intéressé à produire un meilleur code formaté. Et mieux je veux dire "suivre des règles approuvées par suffisamment de gens pour le considérer comme une bonne pratique" (car il n'y aura jamais une "meilleure" façon unique de coder, bien sûr).
Ces jours-ci, je code principalement en Ruby, j'ai donc commencé à utiliser un linter (Rubocop) pour me fournir des informations sur la "qualité" de mon code (cette "qualité" étant définie par le projet de style communautaire ruby-guide-style ).
Notez que j'utiliserai la «qualité» comme dans la «qualité de la mise en forme», pas tant sur l'efficacité du code, même si dans certains cas, l'efficacité du code est en fait affectée par la façon dont le code est écrit.
Quoi qu'il en soit, en faisant tout cela, j'ai réalisé (ou du moins, je me suis souvenu) quelques choses:
- Certains langages (notamment Python, Ruby et autres) permettent de créer de grandes lignes de code
- Suivre certaines directives pour votre code peut le rendre beaucoup plus court et pourtant très clair
- Pourtant, suivre ces directives trop strictement peut rendre le code moins clair / facile à lire
- Le code peut respecter presque parfaitement certaines directives et être toujours de mauvaise qualité
- La lisibilité du code est principalement subjective (comme dans "ce que je trouve clair peut être complètement obscur pour un collègue développeur")
Ce ne sont que des observations, pas des règles absolues bien sûr. Vous remarquerez également que la lisibilité du code et les directives suivantes peuvent sembler sans rapport à ce stade, mais ici, les directives sont un moyen de réduire le nombre de façons de réécrire un morceau de code.
Maintenant, quelques exemples, pour rendre tout cela plus clair.
Exemples
Prenons un cas d'utilisation simple: nous avons une application avec un User
modèle " ". Un utilisateur a une adresse facultative firstname
et surname
obligatoire email
.
Je veux écrire une méthode " name
" qui retournera alors le nom ( firstname + surname
) de l'utilisateur si au moins son firstname
ou surname
est présent, ou sa email
valeur de repli sinon.
Je veux aussi que cette méthode prenne un " use_email
" comme paramètre (booléen), permettant d'utiliser l'email de l'utilisateur comme valeur de secours. Ce use_email
paramètre " " devrait par défaut (s'il n'est pas passé) comme " true
".
La façon la plus simple d'écrire cela, en Ruby, serait:
def name(use_email = true)
# If firstname and surname are both blank (empty string or undefined)
# and we can use the email...
if (firstname.blank? && surname.blank?) && use_email
# ... then, return the email
return email
else
# ... else, concatenate the firstname and surname...
name = "#{firstname} #{surname}"
# ... and return the result striped from leading and trailing spaces
return name.strip
end
end
Ce code est le moyen le plus simple et le plus facile à comprendre de le faire. Même pour quelqu'un qui ne "parle" pas Ruby.
Essayons maintenant de raccourcir cela:
def name(use_email = true)
# 'if' condition is used as a guard clause instead of a conditional block
return email if (firstname.blank? && surname.blank?) && use_email
# Use of 'return' makes 'else' useless anyway
name = "#{firstname} #{surname}"
return name.strip
end
C'est plus court, toujours facile à comprendre, sinon plus facile (la clause de garde est plus naturelle à lire qu'un bloc conditionnel). La clause Guard la rend également plus conforme aux directives que j'utilise, donc gagnant-gagnant ici. Nous réduisons également le niveau de retrait.
Maintenant, utilisons un peu de magie Ruby pour le raccourcir encore:
def name(use_email = true)
return email if (firstname.blank? && surname.blank?) && use_email
# Ruby can return the last called value, making 'return' useless
# and we can apply strip directly to our string, no need to store it
"#{firstname} #{surname}".strip
end
Encore plus court et en suivant parfaitement les directives ... mais beaucoup moins clair car le manque de déclaration de retour le rend un peu déroutant pour ceux qui ne connaissent pas cette pratique.
C'est ici que nous pouvons commencer à poser la question: cela en vaut-il vraiment la peine? Faut-il dire "non, rendez-le lisible et ajoutez return
" "(sachant que cela ne respectera pas les directives). Ou devrions-nous dire "ça va, c'est la façon Ruby, apprends la putain de langue!"?
Si nous prenons l'option B, alors pourquoi ne pas la raccourcir encore plus:
def name(use_email = true)
(email if (firstname.blank? && surname.blank?) && use_email) || "#{firstname} #{surname}".strip
end
Le voici, le one-liner! Bien sûr, il est plus court ... ici nous profitons du fait que Ruby retournera une valeur ou l'autre en fonction de celle qui est définie (puisque l'email sera défini dans les mêmes conditions qu'auparavant).
On peut aussi l'écrire:
def name(use_email = true)
(email if [firstname, surname].all?(&:blank?) && use_email) || "#{firstname} #{surname}".strip
end
C'est court, pas si difficile à lire (je veux dire, nous avons tous vu à quoi peut ressembler un laid), bon Ruby, il est conforme à la directive que j'utilise ... Mais quand même, par rapport à la première façon d'écrire ça, c'est beaucoup moins facile à lire et à comprendre. Nous pouvons également affirmer que cette ligne est trop longue (plus de 80 caractères).
Question
Quelques exemples de code peuvent montrer que le choix entre un code "pleine taille" et bon nombre de ses versions réduites (jusqu'au fameux one-liner) peut être difficile car, comme nous pouvons le voir, les one-liners ne sont pas si effrayants mais encore, rien ne battra le code "full-size" en termes de lisibilité ...
Voici donc la vraie question: où s'arrêter? Quand est court, assez court? Comment savoir quand le code devient "trop court" et moins lisible (en gardant à l'esprit qu'il est assez subjectif)? Et encore plus: comment toujours coder en conséquence et éviter de mélanger des lignes simples avec des morceaux de code "pleine taille" quand j'en ai envie?
TL; DR
La principale question ici est la suivante: quand il s'agit de choisir entre un "morceau de code long mais clair, lisible et compréhensible" et un "puissant, plus court mais plus difficile à lire / comprendre", sachant que ces deux sont le top et le bas d'une échelle et pas les deux seules options: comment définir où est la frontière entre "assez clair" et "pas aussi clair qu'il devrait être"?
La question principale n'est pas le classique "One-liners vs lisibilité: lequel est le meilleur?" mais "Comment trouver l'équilibre entre ces deux?"
Modifier 1
Les commentaires dans les exemples de code sont censés être "ignorés", ils sont là pour clarifier ce qui se passe, mais ne doivent pas être pris en compte lors de l'évaluation de la lisibilité du code.
return
mot - clé ajouté . Ces sept personnages ajoutent un peu de clarté à mes yeux.
[firstname,surname,!use_email].all?(&:blank?) ? email : "#{firstname} #{surname}".strip
... car false.blank?
retourne vrai et l'opérateur ternaire vous enregistre quelques caractères ... ¯ \ _ (ツ) _ / ¯
return
mot clé est-il censé ajouter?! Il ne fournit aucune information . C'est du fouillis pur.