J'ai reçu un examen du code d'un développeur senior aujourd'hui demandant "Au fait, quelle est votre objection à la répartition des fonctions par le biais d'une instruction switch?" J'ai lu à de nombreux endroits sur le fait que pomper un argument via des méthodes de basculement vers un appel est une mauvaise POO, pas aussi extensible, etc. Cependant, je ne peux pas vraiment trouver de réponse définitive pour lui. Je voudrais régler cela une fois pour toutes.
Voici nos suggestions de codes concurrents (php utilisé comme exemple, mais peut s'appliquer de manière plus universelle):
class Switch {
public function go($arg) {
switch ($arg) {
case "one":
echo "one\n";
break;
case "two":
echo "two\n";
break;
case "three":
echo "three\n";
break;
default:
throw new Exception("Unknown call: $arg");
break;
}
}
}
class Oop {
public function go_one() {
echo "one\n";
}
public function go_two() {
echo "two\n";
}
public function go_three() {
echo "three\n";
}
public function __call($_, $__) {
throw new Exception("Unknown call $_ with arguments: " . print_r($__, true));
}
}
Une partie de son argument était "Il (méthode switch) a une manière beaucoup plus propre de gérer les cas par défaut que ce que vous avez dans la méthode magique générique __call ()."
Je ne suis pas d'accord sur la propreté et préfère en fait appeler, mais j'aimerais entendre ce que les autres ont à dire.
Arguments que je peux trouver à l'appui du Oop
schéma:
- Un peu plus propre en termes de code que vous devez écrire (moins, plus facile à lire, moins de mots clés à considérer)
- Toutes les actions ne sont pas déléguées à une seule méthode. Pas beaucoup de différence d'exécution ici, mais au moins le texte est plus compartimenté.
- Dans le même esprit, une autre méthode peut être ajoutée n'importe où dans la classe au lieu d'un emplacement spécifique.
- Les méthodes sont à espace de noms, ce qui est bien.
- Ne s'applique pas ici, mais considérons un cas où
Switch::go()
opéré sur un membre plutôt qu'un paramètre. Vous devez d'abord changer le membre, puis appeler la méthode. CarOop
vous pouvez appeler les méthodes indépendamment à tout moment.
Arguments que je peux trouver à l'appui du Switch
schéma:
- Pour des raisons d'argument, une méthode plus propre pour traiter une demande par défaut (inconnue)
- Semble moins magique, ce qui pourrait rendre les développeurs peu familiers plus à l'aise
Quelqu'un a-t-il quelque chose à ajouter de chaque côté? J'aimerais avoir une bonne réponse pour lui.
la source
Oop
permet d'avoir phpdoc pour décrire chaque méthode, qui peut être analysée par certains IDE (par exemple, NetBeans).Réponses:
Un commutateur n'est pas considéré comme une POO car souvent le polymorphisme peut faire l'affaire.
Dans votre cas, une implémentation POO pourrait être la suivante:
la source
pour cet exemple:
OK, je ne plaisante que partiellement ici. L'argument pour / contre l'utilisation d'une instruction switch ne peut pas être fortement avancé avec un exemple aussi trivial, car le côté POO dépend de la sémantique impliquée, et pas seulement du mécanisme de répartition .
Les instructions switch sont souvent une indication de classes ou de classifications manquantes, mais pas nécessairement. Parfois, une instruction switch n'est qu'une instruction switch .
la source
Peut-être pas une réponse, mais dans le cas du code non-switch, il semble que ce serait une meilleure correspondance:
Une grande partie du puzzle à évaluer est ce qui se passe lorsque vous devez créer
NewSwitch
ouNewOop
. Vos programmeurs doivent-ils sauter à travers des cerceaux d'une manière ou d'une autre? Que se passe-t-il lorsque vos règles changent, etc.la source
is_callable
, mais j'ai laissé call_user_func car (ne faisant pas partie de cette question), il peut y avoir d'autres arguments à transmettre. Mais maintenant que cela est passé aux programmeurs, je suis définitivement d'accord pour dire que la réponse de @ Andrea est bien meilleure :)Au lieu de simplement mettre votre code d'exécution en fonctions, implémentez un modèle de commande complet et placez chacun d'eux dans leur propre classe qui implémente une interface commune. Cette méthode vous permet d'utiliser IOC / DI pour câbler les différents «cas» de votre classe et vous permet d'ajouter et de supprimer facilement des cas de votre code au fil du temps. Il vous donne également un bon bout de code qui ne viole pas les principes de programmation SOLID.
la source
Je pense que l'exemple est mauvais. S'il y a une fonction go () qui accepte l'argument $ where, if est parfaitement valide pour utiliser switch dans la fonction. La simple fonction go () facilite la modification du comportement de tous les go_where () scénarious. De plus, vous préserverez l'interface de classe - si vous utilisez différentes méthodes, vous modifiez l'interface de la classe avec chaque nouvelle destination.
En fait, switch ne doit pas être remplacé par un ensemble de méthodes, mais par un ensemble de classes - c'est du polymorphisme. Ensuite, la destination sera gérée par chaque sous-classe, et il y aura une seule méthode go () pour toutes. Remplacer le conditionnel par le polymorphisme est l'un des refactorings de base, décrit par Martin Fowler. Mais vous n'avez peut-être pas besoin de polymorphisme, et le changement est la voie à suivre.
la source
go()
elle peut facilement violer le principe de substitution de Liskov. Si vous ajoutez plus de fonctionnalitésgo()
, vous ne voulez changer l'interface pour autant que je suis concerend.