Lequel de ces styles de programmation est le meilleur?
var result = methodOne(methodTwo(a, methodThree(b)), c, d);
ou
var result3 = methodThree(b);
var result2 = methodTwo(a, result3);
var result = methodOne(result2, c, d);
Dans les mots du profane:
L'important n'est pas le nombre de lignes mais la lisibilité du code.
Tout imbécile peut écrire du code qu'un ordinateur peut comprendre. Les bons programmeurs écrivent du code que les humains peuvent comprendre. ( M. Fowler )
Dans les exemples que vous avez donnés, le second est définitivement plus facile à lire.
Le code source est à lire.
De plus, les valeurs intermédiaires facilitent le débogage du code.
Le code à une ligne, d'autre part, est utile pour montrer aux autres que vous êtes "intelligent" et que vous ne vous en souciez pas.
Cela dépend, et votre exemple n'est pas utile pour prendre la décision.
Alors que moins de lignes de code ne sont pas toujours meilleures (à un moment donné, cela conduit à de l'obscurcissement), elles généralement le sont, simplement parce qu'il y a moins de choses à garder à l'esprit lorsque l'on essaie de comprendre le code.
Dans votre exemple spécifique:
Si les noms des valeurs intermédiaires véhiculent réellement une signification qui n'est pas rendue évidente par les noms des méthodes et des paramètres utilisés pour calculer ces valeurs intermédiaires, alors ils aident à comprendre le code.
Cependant, si les noms des valeurs intermédiaires n'ajoutent pas d'informations, ils rendent le code plus difficile à comprendre, car ils constituent un état supplémentaire à suivre. Même s'ils sont immuables, ils vous obligent à vous demander s'ils ne sont utilisés qu'une seule fois ou à des endroits différents.
Code with as few lines as possible is definitely the best code and every semi-colon you see is basically the developer admitting they weren't clever enough to use advanced constructions like the comma operator or short-circuiting operators to keep the line going as long as possible like you can say `(x++ && false) || y += 2` instead of `x++; y += 2` and the first variant is just so much more concise and clearer as you can tell because it contains no semi-colons honestly I almost threw up writing the second one I mean who wants to use a character whose name contains a Word related to your butt that's just dumb and anyways semi-colons introduce lots of vulnerabilities like what if you typed `while(true) ;` that would just keep running forever and so its clearly better to have no semi-colons to avoid problems like that overall I'd say programmer salaries should be inversely proportional to the number of newlines and semi-colons in their code because everyone knows as soon as you start using a metric like that it starts working even better than before you incentivized it
Mesurer la progression de la programmation par des lignes de code, c'est comme mesurer la progression du bâtiment d'un avion en poids. (Bill Gates)
Bien sûr, moins de lignes sont pas toujours meilleures. Mais d'après mon expérience, moins de lignes sont souvent meilleures que plus de lignes en termes de lisibilité et de maintenabilité. Il y a des exceptions, bien sûr, et votre exemple pourrait en être une.
Le poids est nécessaire pour construire un avion. Mais cela devrait être au bon endroit.
Dans la première version, il est difficile de voir quels paramètres sont passés à quelle méthode. La deuxième version est donc nettement meilleure. Dans d'autres cas, cependant, cela peut ne pas être aussi évident.
La principale différence de la deuxième version est qu'elle permet de nommer les valeurs intermédiaires. Il peut y avoir des cas où ce nom est crucial pour comprendre le code.
Si toutefois les valeurs intermédiaires sont de toute façon simplement appelées quelque chose comme varOne
, vous pouvez également les omettre en les alignant.
Il semble que vous utilisiez JavaScript, ce qui modifie légèrement les paramètres.
À moins d'avoir une raison de stocker ces var
, il vaut mieux ne pas les initaliser du tout. Mais, vous devez rendre la version à instruction unique aussi lisible que possible, en ajoutant des espaces et un commentaire.
//Do X, Y, Z....
var result = methodOne(
methodTwo(
a,
methodThree(b)
),
c,
d
);
Moins de lignes de code sont-elles toujours meilleures?
Le court est simplement non. Le plus long, non, car cela dépend.
Lequel de ces styles de programmation est le meilleur?
Eh bien, le second est beaucoup mieux sous un aspect. Si vous devez déboguer ce qui se passe, vous avez les variables à inspecter et plus de possibilités pour placer le point d'arrêt.
Par contre, je n'aime pas trop utiliser de variables locales, car je dois leur inventer des noms. Je déteste des noms tels que result1/2/3, car après un certain temps, je ne sais pas pour quoi. Mais je ne veux pas passer trop de temps à inventer les noms ... Le premier exemple est (pour moi) en fait plus lisible à cause des mauvais noms de variables dans le deuxième exemple.
Habituellement, je commence par le premier code et le convertis en deuxième premier lorsque j'ai besoin de déboguer.
Cependant, si les expressions sont plus complexes, je préfère garder les résultats intermédiaires dans des variables locales - mais ces variables locales doivent avoir des noms explicites. Ce n'est qu'alors que le code est plus facile à lire.
Ma règle d'or est de mettre chaque opération sur sa propre ligne. Cela est particulièrement utile lorsque vous parcourez le code avec le débogueur. J'aime aussi assigner le résultat d'une opération à une variable avant de la transmettre pour pouvoir casser et examiner son contenu. donc au lieu de
var result = methodOne(methodTwo(a, methodThree(b)), c, d);
J'irais
var result3 = methodThree(b);
var result2 = methodTwo(a, result3);
var result = methodOne(result2, c, d);
Si je ne me trompe pas, les compilateurs Java et .Net JIT compileraient les deux exemples de la même manière.
alternativement, j'écrirais un commentaire très détaillé avant la ligne expliquant ce que je fais.
Je soupçonne que tout le monde veut entendre "non".
Le fait est que votre exemple n'est pas assez clair pour donner une réponse solide.
resultThirdMethod
est un nom horrible. Je trouve la première version beaucoup plus claire que la seconde.
Cependant, si vous pouviez donner à vos variables des noms - des noms qui donnent plus d'informations que la simple provenance des données - cela pourrait être mieux. Tout dépend de la situation réelle.
Dans cet exemple particulier, je ne trouve pas la première version plus difficile à lire, c'est comme lire une simple expression arithmétique telle que (a + f(b)) * c * d
. La deuxième version n'ajoute aucune information qui aide à la comprendre (sauf si les variables ont des noms significatifs qui aident à comprendre un processus compliqué).
Ce que je ressens un peu problématique avec la deuxième version, c'est qu'elle introduit une complexité supplémentaire lors de l'analyse du code. Un programmeur qui lit le code doit penser si resultThirdMethod
ou resultSecondMethod
est utilisé ailleurs, juste pour calculer result
. Et si vous écrivez une méthode remplie de telles déclarations, il n'y a pas de ligne de calcul claire, vous devez vérifier constamment ce qui est calculé à partir de quoi et où. Donc, dans ce cas, si vous voulez la version la plus verbeuse, je suggère de l'encapsuler dans une fonction distincte:
function methodCombined(a, b, c, d)
var resultThirdMethod = methodThree(b);
var resultSecondMethod = methodTwo(a, resultThirdMethod);
return methodOne(resultSecondMethod, c, d);
}
// ...
result = methodCombined(a, b, c, d);
ou au moins l'enfermer dans un bloc de code pour que la portée des variables soit immédiatement visible:
var result;
{
var resultThirdMethod = methodThree(b);
var resultSecondMethod = methodTwo(a, resultThirdMethod);
result = methodOne(resultSecondMethod, c, d);
}
Cela dépend de la familiarité probable du lecteur avec ces opérateurs et leur domaine de problème. Même si toutes les méthodes sont des fonctions arithmétiques, cela peut aider à les décomposer.
Décomposer une formule plus grande en petites étapes peut gâcher la lisibilité:
(let* ((term1 (* b b))
(term2 (* 4 a c))
(discriminant (- term1 term2))
(twoa (* 2 a))
(top1 (+ b (sqrt discriminant)))
(top2 (- b (sqrt discriminant)))
(root1 (/ top1 twoa))
(root2 (/ top2 twoa)))
(list root1 root2))
versus
(let* ((root1 (/ (+ b (sqrt (- (* b b) (* 4 a c))))
(* 2 a)))
(root2 (/ (- b (sqrt (- (* b b) (* 4 a c))))
(* 2 a)))
(list root1 root2))
La décomposition des formules en petites étapes est ce que fait un compilateur lorsqu'il génère du code intermédiaire. (Fortran: FORmula TRANslator.)
Rompre les expressions pour la lisibilité aide, mais ne "Fortran it" (allez par dessus bord).
Le meilleur style est celui qui rend le code le plus lisible (pour un humain, c'est-à-dire que les ordinateurs s'en moquent). Mais "lisible" dépend beaucoup de qui fait la lecture donc cela dépend du contexte. Cela suffit cependant pour répondre à votre question exacte: "Est-ce que [quoi que ce soit] est toujours meilleur"? Alors non , cela dépend du contexte.
La première personne qui doit lire le code est le développeur lui-même. Si vous écrivez du code de telle sorte que vous vous-même pourrez le lire et le comprendre dans quatre semaines, alors vous aurez déjà fait du bon travail - un bien meilleur travail que la majorité des développeurs de l'industrie (hélas).
On peut cependant ajouter quelques généralités. La lecture du code implique l'utilisation de l'œil et du cerveau. Les yeux humains se fatiguent plus vite lorsqu'ils doivent beaucoup bouger, surtout lorsqu'ils doivent se déplacer verticalement (le mouvement latéral est moins un problème). L'utilisation de moins de lignes de code, toutes autres choses étant égales par ailleurs, permettra au lecteur de saisir le code avec moins de mouvements oculaires verticaux, ce qui est plutôt bon.
En revanche, l'analyse syntaxique qui va dans le cerveau voudra utiliser autant "d'ancres visuelles" que possible, et souffrira de certaines activités, notamment le comptage des parenthèses. La diffusion du code sur plusieurs lignes, avec indentation, aide le cerveau. C'est vraiment un compromis entre la fatigue oculaire et la fatigue cérébrale, et, j'insiste, le bon équilibre n'est pas le même pour tout le monde.
En fin de compte, c'est une question de "bon goût". Les gens qui écrivent du code laid sont aussi des gens qui sont heureux de manger de la mauvaise pizza.
Épeler les choses peut aider non seulement en termes de lisibilité, mais aussi d'écriture.
Cela peut sembler une question d'entrevue idiote, mais on m'a demandé une fois dans une interview d'écrire une fonction pour faire pivoter une image de 90 degrés. La source et la destination sont fournies (vous n'avez pas besoin de faire d'allocation de mémoire) ainsi que la largeur et la hauteur de la source. Oui, c'est un problème facile. Je vous suggère de le faire avant de continuer et de voir si vous avez des problèmes.
.
As tu? L'avez-vous testé? Comme on m'a posé la question dans une interview, j'ai également utilisé la question dans les interviews et ce que j'ai trouvé, c'est que de nombreux programmeurs se confondaient. Ils écrivaient du code comme ça
void Rotate90(const Pixel* src, Pixel* dst, int width, int height)
{
for (int y = 0; y < height; ++y)
{
for (int x = 0; x < width; ++x)
{
dst[???] = src[???];
Et c'est là qu'ils se coinceraient. Ils passaient parfois jusqu'à 20 minutes sur le tableau blanc pour essayer de comprendre ce qui se passait dans ces ??? région. Beaucoup de fois ils penseraient avoir la réponse et écrire des choses comme
dst[x * width + (height - 1 - y)] = src[y * width + x];
obtenir le fait que l'utilisation de width
avec dst
est incorrect. Ou souvent ils mélangeaient les x
et y
d'un côté ou de l'autre.
Le problème est que x
, y
, width
et height
changent tous de sens, car lors de la rotation à 90 degrés, la destination devient large et haute, ce qui fait se référant à ces variables prêtant à confusion. J'ai toujours souhaité qu'un programmeur ajoute plus de variables pour aider à le rendre plus clair pour lui, comme ceci
void Rotate90(const Pixel* src, Pixel* dst, int src_width, int src_height)
{
int dst_width = src_height;
int dst_height = src_width;
for (int src_y = 0; src_y < src_height; ++src_y)
{
for (int src_x = 0; src_y < src_width; ++src_x)
{
int dst_x = src_height - src_y - 1;
int dst_y = src_x;
dst[dst_y * dst_width + dst_x] = src[src_y * src_width + src_x];
}
}
}
Il m'a semblé décomposer les choses dans leur signification explicite, il serait beaucoup plus facile d'écrire le code et beaucoup plus facile d'éviter les erreurs. J'avais l'impression que si je voyais un programmeur faire cela, je les considérerais comme sages et les évaluerais plus haut.
Oui, vous pouvez effectuer quelques optimisations. Ce n'est pas le propos. Oui, les calculs à l'intérieur de la dernière instruction peuvent également être divisés en lignes distinctes. Ce serait bien aussi. Le fait est que l'ajout de lignes a facilité la compréhension de la personne qui écrit le code, pas seulement de la personne qui le lit plus tard. Dans ce cas, car la signification de x
, y
, width
et height
est différente par rapport à dst
vs src
la création de variables distinctes pour chacune rend le code beaucoup plus clair à lire ET écrire.
Continuer ... de même, il est souvent utile de pouvoir inspecter les valeurs avant d'appeler une fonction, surtout si c'est une fonction dans une bibliothèque ou le système dans lequel vous ne pouvez pas entrer. Si vous écrivez du code comme celui-ci
ctx.lineTo(x + Math.cos(angle) * radius, y + Math.sin(angle) * radius);
vs this
var circleX = x + Math.cos(angle) * radius;
var circleY = y + Math.sin(angle) * radius;
ctx.lineTo(circleX, circleY);
Dans le deuxième style, vous pouvez maintenant arrêter le débogueur sur le ctx.lineTo
line et inspectez circleX
et circleY
. Vous pourriez trouver qu'ils sont NaN
ce qui vous amènerait probablement à vérifier que x
, angle
et radius
sont en fait les noms corrects, ce que vous ne feriez pas ' t pouvoir faire dans le débogueur. (Le mode "strict" attrape probablement cela). Il vous permet également d'enregistrer facilement les valeurs
console.log("cx: " + circleX = ", cy: " + circleY);
Encore un autre exemple,
class Person
{
public:
...
const String& name() const { return m_name; }
int age() const { return m_age; }
private:
string m_name;
int m_age;
};
contre
class Person
{
public:
...
const String& name() const
{
return m_name;
}
int age() const
{
return m_age;
}
private:
string m_name;
int m_age;
};
Je sais que certains d'entre vous crieront à cet exemple parce que votre style personnel veut que les getters et les setters soient à 1 ligne mais écoutez-moi ... lors du débogage, selon le débogueur, dans le premier style si vous mettez un point d'arrêt sur age
ou name
le débogueur ne pourra pas inspecter this
ni m_age
ni m_name
vous n'aurez donc aucun moyen de vérifier avant que la fonction ne retourne ce qu'elle va retourner ou quel objet elle référence. Dans le deuxième style, vous pouvez placer des points d'arrêt sur les lignes return
et vous pourrez tout inspecter.
Le point de cette réponse est que plus de lignes sont souvent non seulement bonnes pour la lisibilité. Il est également souvent bon pour l'écriture et le débogage.
C'est l'une de ces questions, je pense, si vous deviez décider de la façon dont cela fonctionne pour chaque cas distinct. Décider si un résultat particulier doit être stocké dans une variable intermédiaire fait partie d'être un programmeur, une partie de l'artisanat. Vous utilisez les compétences que vous avez acquises ainsi que les normes de codage sur votre lieu de travail.
Dans les deux exemples artificiels ci-dessous, j'irais probablement avec 1A et 2A, en raison de la lisibilité (trop de bruit et de redondance dans 1B, utilisation trop longue et indirecte d'un tableau anonyme dans 2B). Mais vous ne pouvez pas vraiment trouver une liste exhaustive de modèles où vous pouvez stipuler un sens plutôt que l'autre - de nouvelles situations surviennent tout le temps à mesure que l'art de la programmation progresse.
1A:
startManufacture(loadProductName(), isProductOk(loadProductState()));
vs.
1B:
String productName = loadProductName();
ProductState state = loadProductState();
boolean productIsOk = isProductOk(state);
startManufacture(productName, productIsOk);
2A:
boolean antennaCount = storage.getCount(currentDealer, items[ANTENNA_INDEX]);
boolean chassisCount = storage.getCount(currentDealer, items[CHASSIS_INDEX], EXTERNAL_SHIPPING);
orderManufacture(RADIO, antennaCount, chassisCount);
vs.
2B:
orderManufacture(RADIO, storage.getCount(currentDealer, items[ANTENNA_INDEX]), storage.getCount(currentDealer, items[CHASSIS_INDEX], EXTERNAL_SHIPPING));
Je dirais que les deux exemples sont également mauvais, mais c'est simplement à cause du code d'exemple insuffisant. Pour pouvoir dire si l'un des deux est meilleur, vous devez fournir un exemple réel.
Il n'y a absolument aucune corrélation entre le nombre de lignes de code source et la qualité du code.
En supposant que nous ignorions le fait évident que le code doit faire ce qu'il est censé faire, l'aspect le plus important est qu'il est facilement compréhensible par les lecteurs.
La manière "classique" d'y parvenir consiste à ajouter des commentaires de code qui décrivent la fonctionnalité du code. Dans mon expérience personnelle, il est encore mieux d'utiliser des identifiants descriptifs pour les méthodes, les variables et les constantes.
Si, par exemple, vous voyez une séquence de code source comme celle-ci, vous comprendrez immédiatement ce qu'elle est censée faire et vous pouvez examiner les méthodes et variables individuelles pour (espérons-le) tout aussi facilement déterminer qu'elles font ce qu'elles sont censées faire:
if (a_new_game_is_starting && hand_not_dealt_yet()) {
deal_new_hand_to(all_players_at_table);
}
Dans l'exemple ci-dessus, il n'est pas du tout nécessaire d'ajouter des commentaires. Que dirait un commentaire qui n'est pas facilement compris à partir du code réel? C'est quelque chose à rechercher - à pas besoin pour ajouter des commentaires au code pour décrire ce qu'il fait.
Ce n'est pas quelque chose de difficile à faire. Tout ce qu'il faut, c'est que vous réfléchissiez à ce que vous essayez vraiment d'accomplir et que vous nommiez vos identifiants d'un point de vue fonctionnel.
Plus important que lisibilité est maintenabilité, car la principale raison pour laquelle nous lisons le code est de le maintenir. Maintenir signifie souvent apporter des modifications au code en raison de modifications mineures ou importantes des exigences.
ALORS, réfléchissez aux changements dans les exigences et essayez de les rendre aussi simples que possible. Si vous pouvez diriger le responsable directement vers le code approprié et le laisser faire les modifications avec un minimum d'édition, il pensera que vous êtes un excellent programmeur!
OTOH, si les gens lisent le code pour vérifier son exactitude, assurez-vous que votre raisonnement explique clairement pourquoi il est correct. Si les gens doutent de l'exactitude de votre code, ils sont susceptibles de le réécrire, ce qui risque de le casser. Pour éviter cela, assurez-vous que le code s'explique lui-même.
Assurez-vous également d'expliquer vos décisions en matière de performances, car un bon code est détruit en étant réécrit dans le but de le rendre plus rapide.
return ($this->main->context != "ajax" || in_array($this->type, $this->definition->ajax)) ? eval('return method_exists($this,"Show'.ucfirst($this->type).'") ? $this->Show'.ucfirst($this->type).'('.(count($args) ? join(",",array_map(create_function('$a','return (is_numeric($a) || preg_match("/^array/",$a)) ? $a : "\"".$a."\"";'),$args)) : "").') : null;') : '';
J'ai rencontré ce code sur un projet dont j'ai hérité il y a quelque temps. Je pense que cela montre bien pourquoi c'est une mauvaise idée d'essayer d'utiliser le moins de lignes possible. C'est complètement illisible et impossible à déboguer.
Vous donnez un mauvais exemple.
La solution que vous donnez pour mieux résoudre un problème a une bonne conception afin qu'elle soit compréhensible et suffisamment bonne pour que le code soit court.
Si vous pouvez rendre le code plus compréhensible en l'allongeant, c'est quelque chose à faire, mais il y a une limite.
Je penserai toujours que le code qui a à la fois un bon design ET est court, garantit plus de compréhensibilité plus il est court.
Vous devez vous concentrer sur une bonne conception, puis trouver des moyens de rendre le code suffisamment court pour que les personnes qui le liront puissent le comprendre sans les faire défiler et les faire défiler.
Donc, pour résumer, il n'y a pas vraiment de bonne réponse à cela, ce qui est important, c'est la façon dont vous divisez votre code en fonctions et en sections, et comment vous pouvez le rendre à la fois court et facile à comprendre.
La citation de Bill Gates est bonne, mais il y a aussi la citation qui parle d'un morceau de code parfait, qui est celui où vous ne pouvez rien retirer.