anti-si la campagne

J'ai récemment couru contre un site très intéressant qui exprime une idée très intéressante - la campagne anti-if. Vous pouvez le voir ici à www.antiifcampaign.com . je dois convenir que les déclarations si imbriquées complexes sont une douleur absolue à l'arrière. Je suis actuellement sur un projet qui, jusqu'à très récemment, avait des IFs imbriqués fous qui défilaient vers la droite pour de nombreuses façons. Nous avons résolu nos problèmes de deux façons: nous avons utilisé Windows Workflow Foundation pour traiter le routage (ou workflow) préoccupation. Et nous sommes en train de mettre en œuvre toutes nos règles métier en utilisant les règles ILOG pour. NET (récemment acheté par IBM!!). Ceci pour la plupart a guéri nos douleurs si imbriquées...mais je me demande combien de personnes guérissent leurs douleurs de la manière que suggèrent les bonnes personnes de L'AntiIfCampaign ( voir un exemple ici) en créant de nombreuses quantités de classes abstraites pour représenter un scénario donné qui a été initialement couvert par le si imbriqué. Je me demande si un autre la façon de résoudre la suppression de cette complexité peut également être d'utiliser un conteneur IoC tel que StructureMap pour entrer et sortir de différents bits de fonctionnalité. De toute façon...

Question: étant donné un scénario où j'ai une instruction complexe imbriquée IF ou SWITCH qui est utilisée pour évaluer un type donné de chose (disons évaluer une énumération) pour déterminer comment je veux gérer le traitement de cette chose par le type enum-quelles sont les façons de faire la même forme de traitement sans utiliser la structure hiérarchique IF ou SWITCH?

public enum WidgetTypes
{
    Type1,
    Type2,
    Type3,
    Type4
}

...

WidgetTypes _myType = WidgetTypes.Type1;

...

switch(_myType)
{
    case WidgetTypes.Type1:
        //do something
        break;

    case WidgetTypes.Type2:
        //do something
        break;

    //etc...
}
27
demandé sur Andrew Siemer 2009-07-22 23:23:09

15 réponses

Le problème n'est pas l'instruction 'if', ce sont les programmeurs qui écrivent du mauvais code.

EDIT: aussi, comme d'autres l'ont souligné, vous devriez utiliser le polymorphisme (si disponible) lorsque vous utilisez des instructions if pour vérifier le type d'un objet, mais les instructions if en elles-mêmes sont des constructions très utiles et fondamentales.

58
répondu Ed S. 2009-07-22 19:43:25

En Java, il est facile d'utiliser des énumérations en tant qu'agents anti-if polymorphes.

public class AntiIf {
  public enum WidgetTypes {
    Type1 {
      public void doSomething() {
        //...
      }},
    Type2 {
      public void doSomething() {
        //...
      }},
    Type3 {
      public void doSomething() {
        //...
      }},
    Type4 {
      public void doSomething() {
        //...
      }};

    public abstract void doSomething();
  }

  WidgetTypes _myType; // set by someone to one of the types.

  public void someFunction() {
    //...
    _myType.doSomething();
    //...
  }
}
36
répondu Robert C. Martin 2010-03-17 11:37:04

Être anti-if est idiot.

Parfois, remplacer un polymorphisme via conditionnel est la bonne chose à faire, mais dans ces cas, ce n'était pas l'instruction if qui était le vrai problème. Le vrai problème était de travailler avec des types abstraits de manière non abstraite, c'est-à-dire de ne pas penser au niveau de l'abstraction de la classe de base.

D'autres fois, il est possible de remplacer un conditionnel via le polymorphisme, mais cela serait une mauvaise idée. La logique qui vous amène à traiter un type différent d'un autre peut appartenir à l'algorithme lui-même, pas les classes individuelles. Déplacer cette logique vers les classes peut amener le code des classes à être trop conscient du contexte dans lequel elles sont utilisées.

Mais le plus souvent, une instruction if n'a rien à voir avec le polymorphisme. Se débarrasser de si déclarations est pas l'objectif ici.

Être anti-if est idiot.

19
répondu Darryl 2009-07-22 19:52:52
16
répondu William 2009-07-22 19:30:08

Les instructions Switch peuvent souvent être remplacées par polymorphisme:

abstract class WidgetBase {
  public abstract void DoSomething();
}

class Widget1 : WidgetBase {
  public override void DoSomething() {
    // Do something ...
  }
}

class Widget2 : WidgetBase {
  public override void DoSomething() {
    // Do something ...
  }
}

...

Widget widget = new Widget1(); // Use a factory pattern instead.
widget.DoSomething();
14
répondu Martin Liversage 2009-07-22 19:28:45

Vous pouvez utiliser le modèle de commande pour exécuter des commandes basées sur une valeur ou une entrée.

En réponse à votre question, vous pouvez utiliser une table de recherche simple ou un polymorphisme pour y parvenir, bien qu'il place la logique dans une partie différente du programme, ce qui peut en effet être la bonne chose à faire. Voici quelques exemples en C#:

var commands = new Dictionary<WidgetTypes, Action>()
    {
        { WidgetTypes.Type1, () => Console.WriteLine("Type 1") },
        { WidgetTypes.Type2, () => Console.WriteLine("Type 2") },
        { WidgetTypes.Type3, () => Console.WriteLine("Type 3") },
        { WidgetTypes.Type4, () => Console.WriteLine("Type 4") }
    };

commands[WidgetTypes.Type1]();

public interface ICommandHandler
{
    void HandleCommand();
}

public class Command1Handler : ICommandHandler
{
    public void HandleCommand()
    {
        Console.WriteLine("Type 1");
    }
}

// ...

var commands = new Dictionary<WidgetTypes, ICommandHandler>()
    {
        { WidgetTypes.Type1, new Command1Handler() },
        { WidgetTypes.Type2, new Command2Handler() },
        { WidgetTypes.Type3, new Command3Handler() },
        { WidgetTypes.Type4, new Command4Handler() }
    };

commands[WidgetTypes.Type1].HandleCommand();

Et une méthode de réflexion bonus pour appeler la méthode sur l'objet courant avec la même nom:

public void Type1()
{
    Console.WriteLine("Type 1");
}

//...

var type = WidgetTypes.Type2;
typeof(MyClass).GetMethod(type.ToString()).Invoke(this, new object[] { });

Remarque: ne font en réalité que

12
répondu IRBMe 2009-07-22 19:46:35

...Comment?

Je ne peux pas imaginer comment faire quelque chose comme ça sans une sorte de comparaison. À tous.

Une campagne anti-goto que je peux imaginer, mais si cela semble être une structure de flux de contrôle sacrément requise pour moi.

6
répondu Kawa 2009-07-22 19:26:26

Ne vous inquiétez pas de l'instruction simple if ou de l'instruction straight forward switch.

S'inquiéter à ce sujet:

  1. Instructions If horriblement imbriquées (L'anti-motif de pointe de flèche). Ces choses sont impossibles à lire et à déboguer.
  2. Le SI (ou le cas) avec une ligne de code bagilion en eux. "OK, nous faisons ceci et cela.... dans ce cas, et ça et ça... dans ce cas. Aussi très difficile à lire. Presque impossible de tester ou de déboguer.
  3. conditions Super compliquées: si (bol et KitchenSink ou BathroomTub et ...). De quoi êtes-vous des tests pour les nouveau? Enveloppez cela dans une méthode-dans ce cas: CanHoldWater ().
5
répondu Chris Brandsma 2009-07-22 20:25:09

Cela dépend de la façon dont vous gérez la situation. Si vous appelez une fonction différente en utilisant la même signature, vous pourriez avoir un dictionnaire où la clé serait le type et la valeur d'un délégué pointant vers le gestionnaire réel. Ainsi, vous pouvez vous débarrasser du commutateur et juste faire

_myDictionary[ _myType ]( param1, ... );
3
répondu Trap 2009-07-22 19:30:26

Mettre en œuvre cette idée dans un langage de type C est très boiteux... mais dans un langage fonctionnel approprié avec une correspondance de motif gardée est beaucoup plus idiomatique et une façon plus riche d'exprimer des conditions, j'ose ajouter.

2
répondu fortran 2009-07-22 19:31:11

Le polymorphisme est la bonne façon d'aller quand la logique Doit être cuite dans les classes.

Mais comme de nombreuses applications interagissent déjà avec leur propre base de données, une autre solution consiste à utiliser une logique basée sur une table. Avantages de l'utilisation des tables:

  1. ajuster la logique métier sans recompiler.
  2. les instances de base de données peuvent partager le même code mais une logique différente.

Je ne parle pas de l'intégration directe du code source dans une base de données varchar () et le compiler à la volée.

, Mais disons que vous avez une liste de champs de saisie, et pour chacun, vous devez effectuer (a) quelques RegEx pour frotter la valeur, et (b) un ou plusieurs tests pour valider le résultat.

Dans un monde de code uniquement, vous auriez une sous-classe pour chaque type de champ, chaque implémentant, disons, ses propres méthodes appelées ScrubValue() et Validate().

Dans un modèle basé sur une table, une seule classe de champ atteindrait la base de données (mise en cache bien sûr) sur une propriété FieldType et récupérer un ensemble de chaînes de recherche/remplacement RegEx, et une liste de paramètres de test de validation (longueur minimale et maximale, motifs regex à faire correspondre, etc.).

Cela fonctionne bien pour toute situation où le nombre de sous-types augmente avec le temps et la logique peut être exprimée comme un ensemble de chaînes configurables, de nombres, etc. plutôt que d'avoir à être directement implémenté en tant que code.

2
répondu richardtallent 2009-07-22 19:46:51

Wow... est-ce pour de vrai?

Vous pouvez essayer catch absolument toutes les exceptions possibles lorsque vous descendez le mauvais chemin d'exécution jusqu'à ce que vous atteigniez un chemin d'exécution qui ne se casse pas, mais C'est bien pire que si.

Vous pouvez utiliser un modèle de commande ou une carte lorsque l'orientation et le polymorphisme de l'objet sont naturels, mais que se passe-t-il si vous avez affaire à des résultats d'entrée utilisateur complexes et non à des objets en eux-mêmes?

1
répondu AlbertoPL 2009-07-22 19:29:46

Une fois que vos instructions if commencent à devenir grandes et imbriquées, l'une des possibilités peut être que le nombre de conditions que vous devez tester a augmenté. Dans ce cas, comme vous ajoutez plus de conditions à la situation, il commence à devenir exponentiellement plus facile de laisser une combinaison de conditions tomber à travers les mailles du filet, puis bam, vous avez vous-même un bug.

J'ai toujours pensé que dans des cas comme ceux - ci, une sorte de table de vérité serait bonne. A propos de l'approximation la plus proche je peux venir cependant finit par être un peu déconcertant pour le programmeur moyen. Cela implique d'assembler toutes vos conditions en un seul entier, puis d'essayer de couvrir chaque nombre de la plage qui crée avec une instruction switch ou une table de recherche.

Je ne suis pas sûr que le polymorphisme soit la bonne réponse pour celui-ci. Tout le monde a tout de meilleures idées?

1
répondu Breton 2009-07-22 20:52:42

Les instructions Case fonctionnent très bien pour organiser les ifs imbriqués.

0
répondu Lance Roberts 2009-07-22 19:28:15

Je pense que je comprends ce qu'ils veulent dire. Nous ne survivrons probablement pas sans IF et switches, mais il estvrai que les blocs conditionnels profondément imbriqués sont difficiles à lire.

Un exemple de mon propre travail: une fonction pour calculer la déformation simulée d'un pont de wheatstone shunté. Cette fonction prend comme 5 paramètres de configuration (quelle branche de shunt, y a-t-il des lignes de sens de shunt, des lignes de sens d'approvisionnement, ...), et je n'étais pas tout à fait impatient d'écrire le dispatching routine.

Je suis venu avec une structure de code template qui m'a permis de "déclarer" les fonctions appropriées (environ 30) au lieu d'avoir à les croquer dans une routine d'expédition.

Cela nous a permis de très ajouter rapidement de nouveaux cas de paramètres de configuration, et de "copier" un peu les équations de l'aperçu mathématique. Tout à fait une victoire, vraiment.

0
répondu xtofl 2009-07-22 19:41:09