Le site if
est rarement considéré comme "mauvais" comme goto
ou des variables globales mutables - et même ces dernières ne sont pas universellement et absolument mauvaises. Je suggérerais de considérer cette affirmation comme un peu hyperbolique.
Cela dépend aussi largement de votre langage de programmation et de votre environnement. Dans les langages qui prennent en charge le filtrage de motifs, vous disposerez d'excellents outils pour remplacer if
à votre disposition. Mais si vous programmez un microcontrôleur de bas niveau en C, remplacer if
avec des pointeurs de fonction sera un pas dans la mauvaise direction. Ainsi, je vais surtout envisager de remplacer if
dans la programmation OOP, car dans les langages fonctionnels, if
n'est pas idiomatique de toute façon, alors que dans les langages purement procéduraux, vous n'avez pas beaucoup d'autres options pour commencer.
Néanmoins, les clauses conditionnelles donnent parfois lieu à un code plus difficile à gérer. Cela ne concerne pas seulement les if
mais, plus souvent encore, l'instruction switch
qui comprend généralement plus de branches qu'une déclaration correspondante. if
serait.
Il y a des cas où il est parfaitement raisonnable d'utiliser une if
Lorsque vous écrivez des méthodes utilitaires, des extensions ou des fonctions spécifiques de la bibliothèque, il est probable que vous ne pourrez pas éviter if
(et vous ne devriez pas). Il n'y a pas de meilleure façon de coder cette petite fonction, ni de la rendre plus auto-documentée qu'elle ne l'est :
// this is a good "if" use-case
int Min(int a, int b)
{
if (a < b)
return a;
else
return b;
}
// or, if you prefer the ternary operator
int Min(int a, int b)
{
return (a < b) ? a : b;
}
Le branchement sur un "code de type" est une odeur de code.
D'un autre côté, si vous rencontrez du code qui teste une sorte de code de type, ou qui teste si une variable est d'un certain type, alors il s'agit très probablement d'un bon candidat pour le refactoring, à savoir remplacer le conditionnel par le polymorphisme .
La raison en est qu'en permettant à vos appelants de se brancher sur un certain type de code, vous créez une possibilité de vous retrouver avec de nombreuses vérifications dispersées dans tout votre code, rendant les extensions et la maintenance beaucoup plus complexes. Le polymorphisme, quant à lui, vous permet d'amener cette décision de branchement aussi près que possible de la racine de votre programme.
Pensez-y :
// this is called branching on a "type code",
// and screams for refactoring
void RunVehicle(Vehicle vehicle)
{
// how the hell do I even test this?
if (vehicle.Type == CAR)
Drive(vehicle);
else if (vehicle.Type == PLANE)
Fly(vehicle);
else
Sail(vehicle);
}
En plaçant une fonctionnalité commune mais spécifique à un type (c'est-à-dire spécifique à une classe) dans des classes distinctes et en l'exposant par le biais d'une méthode virtuelle (ou d'une interface), vous permettez aux parties internes de votre programme de déléguer cette décision à une personne située plus haut dans la hiérarchie d'appel (potentiellement à un seul endroit du code), ce qui facilite grandement les tests (mocking), l'extensibilité et la maintenance :
// adding a new vehicle is gonna be a piece of cake
interface IVehicle
{
void Run();
}
// your method now doesn't care about which vehicle
// it got as a parameter
void RunVehicle(IVehicle vehicle)
{
vehicle.Run();
}
Et vous pouvez maintenant facilement tester si votre RunVehicle
fonctionne comme il se doit :
// you can now create test (mock) implementations
// since you're passing it as an interface
var mock = new Mock<IVehicle>();
// run the client method
something.RunVehicle(mock.Object);
// check if Run() was invoked
mock.Verify(m => m.Run(), Times.Once());
Des modèles qui ne diffèrent que par leur if
les conditions peuvent être réutilisées
En ce qui concerne l'argument du remplacement if
avec un "prédicat" dans votre question, Haines voulait probablement mentionner que parfois des modèles similaires existent dans votre code, qui ne diffèrent que par leurs expressions conditionnelles. Les expressions conditionnelles apparaissent en conjonction avec if
mais l'idée générale est d'extraire un motif répétitif dans une méthode distincte, en laissant l'expression comme paramètre. C'est ce que fait déjà LINQ, généralement résultant en un code plus propre par rapport à une alternative foreach
:
Considérez ces deux méthodes très similaires :
// average male age
public double AverageMaleAge(List<Person> people)
{
double sum = 0.0;
int count = 0;
foreach (var person in people)
{
if (person.Gender == Gender.Male)
{
sum += person.Age;
count++;
}
}
return sum / count; // not checking for zero div. for simplicity
}
// average female age
public double AverageFemaleAge(List<Person> people)
{
double sum = 0.0;
int count = 0;
foreach (var person in people)
{
if (person.Gender == Gender.Female) // <-- only the expression
{ // is different
sum += person.Age;
count++;
}
}
return sum / count;
}
Cela indique que vous pouvez extraire la condition dans un prédicat, ce qui vous laisse une seule méthode pour ces deux cas (et de nombreux autres cas futurs) :
// average age for all people matched by the predicate
public double AverageAge(List<Person> people, Predicate<Person> match)
{
double sum = 0.0;
int count = 0;
foreach (var person in people)
{
if (match(person)) // <-- the decision to match
{ // is now delegated to callers
sum += person.Age;
count++;
}
}
return sum / count;
}
var males = AverageAge(people, p => p.Gender == Gender.Male);
var females = AverageAge(people, p => p.Gender == Gender.Female);
Et comme LINQ dispose déjà d'un grand nombre de méthodes d'extension pratiques comme celle-ci, vous n'avez même pas besoin d'écrire vos propres méthodes :
// replace everything we've written above with these two lines
var males = list.Where(p => p.Gender == Gender.Male).Average(p => p.Age);
var females = list.Where(p => p.Gender == Gender.Female).Average(p => p.Age);
Dans cette dernière version de LINQ, le if
a complètement "disparu", bien que :
- pour être honnête, le problème n'était pas dans la
if
par lui-même, mais dans l'ensemble du modèle de code (simplement parce qu'il a été dupliqué), et
- le site
if
existe toujours, mais il est écrit à l'intérieur du LINQ Where
méthode d'extension, qui a été testée et fermée pour modification. Avoir moins de code propre est toujours une bonne chose : moins de choses à tester, moins de choses qui peuvent mal tourner, et le code est plus simple à suivre, à analyser et à maintenir.
D'énormes séries d'imbrications if
/ else
déclarations
Lorsque vous voyez une fonction qui s'étend sur 1000 lignes et qui comporte des douzaines d'éléments imbriqués if
il y a de grandes chances qu'elle puisse être réécrite pour
- utiliser une meilleure structure de données et organiser les données d'entrée d'une manière plus appropriée (par exemple, une table de hachage, qui fera correspondre une valeur d'entrée à une autre en un seul appel),
- utiliser une formule, une boucle, ou parfois simplement une fonction existante qui exécute la même logique en 10 lignes ou moins (par ex. cet exemple notoire me vient à l'esprit, mais l'idée générale s'applique à d'autres cas),
- utiliser des clauses de garde pour éviter l'imbrication (les clauses de garde donnent plus de confiance dans l'état des variables tout au long de la fonction, car elles se débarrassent des cas exceptionnels dès que possible),
- au moins remplacer par un
switch
le cas échéant.
Refaire le code quand vous le sentez, mais ne pas sur-ingénier.
Ayant dit tout cela, vous ne devrait pas passer des nuits blanches que d'avoir quelques conditionnels de temps en temps. Bien que ces réponses puissent fournir quelques règles générales, la meilleure façon de détecter les constructions qui nécessitent une refactorisation est l'expérience. Avec le temps, certains schémas émergent et conduisent à modifier les mêmes clauses encore et encore.
137 votes
if
Les déclarations sont diaboliques comme le sont les marteaux. Certaines personnes peuvent en faire un mauvais usage, mais c'est un outil essentiel.26 votes
Alors un type balance une affirmation sans la soutenir ou expliquer pourquoi ?
11 votes
Je me méfie beaucoup des déclarations générales comme celles qui précèdent. Je n'ai rien contre les préférences et les suggestions sur les raisons pour lesquelles vous ne souhaitez pas utiliser tel ou tel outil ou fonctionnalité, etc. Mais un sceau dogmatique du "mal" me rebute (je ne sais pas s'il a qualifié cela du tout, d'ailleurs).
7 votes
@e.c.ho - il n'y a jamais rien de mal à une affirmation sans justification ;)
1 votes
Ne sommes-nous pas en train de perdre le contexte dans lequel la déclaration a été faite ? IMO, c'est une question de langage. Le C, par exemple, n'a pas le concept de délégués (bien sûr, vous pouvez fabriquer quelque chose, mais ce n'est pas la question).
1 votes
Je serais intéressé de savoir s'il existe des interviews ou des transcriptions de la présentation de Corey. J'aimerais connaître l'histoire complète de cette affaire.
0 votes
@Vadim Êtes-vous son agent publicitaire ?
2 votes
@dirkgently : il n'a pas de délégués pour garder les choses en ordre, mais il a des pointeurs de fonction. Mais je ne sais pas quelqu'un qui préconiserait un pointeur de fonction plutôt qu'une instruction if.
0 votes
Re "sweeping statements" : mais comment qualifier vos déclarations sans "if" ? ;)
0 votes
Cela montre à quel point vous pouvez attirer l'attention en commençant par une déclaration en noir et blanc, alors que la vérité se décline généralement en plusieurs nuances de gris.
10 votes
Les instructions "Si" ne tuent pas les programmes : Les mauvais programmeurs tuent les programmes !
0 votes
@e.c.ho Je me demande d'où viennent vos questions. La déclaration de Corey me choque un peu. Je me demandais ce que je ne comprenais pas. Stackoverflow est génial pour moi pour obtenir des réponses.
0 votes
Le truc du publicitaire était facétieux. N'ayant jamais entendu parler de ce type avant, sa déclaration générale sans raison est un peu comme une flamme pour les papillons de nuit.
1 votes
@Dominic, de nos jours
if
les déclarations sont beaucoup utiles que les marteaux2 votes
@Vadim Quelle réponse avez-vous obtenue de Corey Haines lorsque vous lui avez demandé d'expliquer pourquoi ?
1 votes
-1 pour avoir posé une question dont la réponse est claire : bien sûr qu'ils sont mauvais ! ;) +1 en fait
0 votes
Toutes les déclarations radicales sont mauvaises.