Lequel de ces modèles est le meilleur? Quels sont les avantages et les inconvénients de chacun? Lequel utiliseriez-vous? Toute autre suggestion sur la façon de traiter des méthodes comme celle-ci est appréciée.
Il est raisonnable de supposer que Draw () est le seul endroit à partir duquel les autres méthodes de dessin sont appelées. Cela doit s'étendre à de nombreuses autres méthodes Draw * et propriétés Show *, et pas seulement aux trois indiquées ici.
public void Draw()
{
if (ShowAxis)
{
DrawAxis();
}
if (ShowLegend)
{
DrawLegend();
}
if (ShowPoints && Points.Count > 0)
{
DrawPoints();
}
}
private void DrawAxis()
{
// Draw things.
}
private void DrawLegend()
{
// Draw things.
}
private void DrawPoints()
{
// Draw things.
}
Ou
public void Draw()
{
DrawAxis();
DrawLegend();
DrawPoints();
}
private void DrawAxis()
{
if (!ShowAxis)
{
return;
}
// Draw things.
}
private void DrawLegend()
{
if (!ShowLegend)
{
return;
}
// Draw things.
}
private void DrawPoints()
{
if (!ShowPoints || Points.Count <= 0))
{
return;
}
// Draw things.
}
Réponses:
Je ne pense pas que vous puissiez avoir une règle générale pour ce genre de chose, cela dépend de la situation.
Dans ce cas, je suggérerais d'avoir les clauses if en dehors des méthodes car les noms des méthodes Draw impliquent qu'elles dessinent simplement les choses sans conditions spéciales.
Si vous constatez que vous devez effectuer les vérifications avant d'appeler les méthodes à de nombreux endroits, vous souhaiterez peut-être placer la vérification à l'intérieur des méthodes et les renommer pour clarifier que cela se produit.
la source
Je dis le deuxième.
Les méthodes doivent avoir le même niveau d'abstraction.
Dans le deuxième exemple, la responsabilité de la
Draw()
méthode consiste à agir comme un contrôleur, en appelant chacune des méthodes de dessin individuelles. Tout le code de cetteDraw()
méthode est au même niveau d'abstraction. Cette directive entre en jeu dans les scénarios de réutilisation. Par exemple, si vous étiez tenté de réutiliser laDrawPoints()
méthode dans une autre méthode publique (appelons-laSketch()
), vous n'auriez pas besoin de répéter la clause guard (l'instruction if décidant de dessiner ou non les points).Dans le premier exemple, cependant, la
Draw()
méthode est chargée de déterminer s'il faut appeler chacune des méthodes individuelles, puis d'appeler ces méthodes.Draw()
a quelques méthodes de bas niveau qui fonctionnent, mais il délègue d'autres travaux de bas niveau à d'autres méthodes, et a donc duDraw()
code à différents niveaux d'abstraction. Pour réutiliserDrawPoints()
dansSketch()
, vous devez également répliquer la clause de gardeSketch()
.Cette idée est discutée dans le livre de Robert C. Martin "Clean Code", que je recommande fortement.
la source
DrawAxis()
et. Al. pour indiquer que leur exécution est conditionnelle, peut-êtreTryDrawAxis()
. Sinon, vous devez naviguer de laDraw()
méthode à chaque sous-méthode individuelle pour confirmer son comportement.Mon opinion sur le sujet est assez controversée, mais restez avec moi, car je crois que tout le monde est d'accord sur le résultat final. J'ai juste une approche différente pour y arriver.
Dans mon article Function Hell , j'explique pourquoi je n'aime pas diviser les méthodes juste pour créer des méthodes plus petites. Je ne les sépare que lorsque je sais , ils seront réutilisés, ou bien sûr, quand je pourrai les réutiliser.
Le PO a déclaré:
Cela m'amène à une troisième option (intermédiaire) non mentionnée. Je crée des «blocs de code» ou des «paragraphes de code» pour lesquels d'autres créeraient des fonctions.
Le PO a également déclaré:
... donc cette méthode grandira très rapidement. Presque tout le monde convient que cela diminue la lisibilité. À mon avis, la bonne solution n'est pas seulement de diviser en plusieurs méthodes, mais de diviser le code en classes réutilisables. Ma solution ressemblerait probablement à quelque chose comme ça.
Bien entendu, il faudrait encore passer des arguments, etc.
la source
Pour les cas où vous vérifiez un champ qui contrôle s'il faut ou non dessiner, il est logique de l'avoir à l'intérieur
Draw()
. Lorsque cette décision devient plus compliquée, j'ai tendance à préférer cette dernière (ou à la diviser). Si vous avez besoin de plus de flexibilité, vous pouvez toujours l'étendre.Notez que les méthodes supplémentaires sont protégées, ce qui permet aux sous-classes d'étendre ce dont elles ont besoin. Cela vous permet également de forcer le dessin pour les tests ou pour toute autre raison que vous pourriez avoir.
la source
DrawPointsIfItShould()
méthode qui raccourcit leif(should){do}
:)De ces deux alternatives, je préfère la première version. Ma raison est que je veux une méthode pour faire ce que son nom implique, sans dépendances cachées. DrawLegend devrait dessiner une légende, pas peut-être dessiner une légende.
La réponse de Steven Jeuris est cependant meilleure que les deux versions de la question.
la source
Au lieu d'avoir des
Show___
propriétés individuelles pour chaque partie, je définirais probablement un champ de bits. Cela simplifierait un peu les choses pour:En dehors de cela, je pencherais pour vérifier la visibilité dans la méthode extérieure, pas intérieure. C'est, après tout,
DrawLegend
et nonDrawLegendIfShown
. Mais cela dépend en quelque sorte du reste de la classe. S'il existe d'autres endroits qui appellent cetteDrawLegend
méthode et qu'ils doiventShowLegend
également vérifier , je déplacerais probablement le chèque dansDrawLegend
.la source
J'irais avec la première option - avec le "si" en dehors de la méthode. Il décrit mieux la logique suivie, plus vous donne la possibilité de dessiner réellement un axe, par exemple, dans les cas où vous voulez en dessiner un quel que soit un paramètre. De plus, il supprime la surcharge d'un appel de fonction supplémentaire (en supposant qu'il n'est pas intégré), qui peut s'accumuler si vous optez pour la vitesse (votre exemple semble qu'il peut simplement dessiner un graphique où il peut ne pas être un facteur, mais dans une animation ou jeu, il pourrait être).
la source
En général, je préfère que les niveaux de code inférieurs supposent que les conditions préalables sont remplies et effectuent le travail qu'ils sont censés faire, les vérifications étant effectuées plus haut dans la pile des appels. Cela a l'avantage secondaire d'économiser des cycles en ne faisant pas de vérifications redondantes, mais cela signifie également que vous pouvez écrire du bon code comme celui-ci:
au lieu de:
Et bien sûr, cela devient encore plus désagréable si vous appliquez une sortie unique, avez de la mémoire que vous devez libérer, etc.
la source
Tout dépend du contexte:
la source