2010-09-22 3 views
3

J'ai hérité d'une application JavaScript (MooTools) assez volumineuse pour naviguer et configurer des produits.Comment refactoriser la logique conditionnelle en javascript?

Il y a une fonction qui suit l'état configuré des produits. D'autres fonctions sont appelées en fonction de l'état du produit.

Je l'ai étendu pour ajouter de nouvelles fonctionnalités, mais j'ai fini par avoir un tas de conditions imbriquées dans une grosse instruction switch.

Ma question est, comment refaçonnez-vous la logique conditionnelle? Existe-t-il un modèle/une technique générale (ou spécifique à JS) pour le suivi de l'état?

Mise à jour: est ici une pseudo-version de la fonction pour illustrer le problème:

switchToVariation: function(link) { 

    // set the variables  
    // before switch, set state  

    switch (nextType) { 
    case 'type1': 
     if (prevType == 'type1') {   
     if (nextIsSameVariation) {    
      // do stuff           
     } else {                    
      if (nextIsSomethingElse) {    
      // do stuff 
      }   
      // more stuff      
     } 
     } else {   
     // different stuff   
     } 
     break; 

    case 'type2':   
     if (prevWasType1) { 
     // stuff 
     } else { 
     if (nextIsSameStyle) { 
      // stuff 
     } 
     // more stuff 
     }  
     break;  

    default:      
     // default stuff   
    }   

    // after switch, set state 

} 
+1

Il existe de nombreux modèles de conception qui peuvent être utilisés pour résoudre le problème de conditions trop imbriquées/trop profondément imbriquées, cependant, certains exemples de code ou de maquette seront utiles en premier. – Anurag

+0

a ajouté un exemple – meleyal

Répondre

3

La réponse primaire, bien sûr, est: diviser en petits morceaux, chaque morceau fait une chose bien.

Il est difficile d'être plus précis sans voir un exemple, mais vous avez dit que vous avez un "... état de condition dans une grande déclaration de changement ..." qui offre certainement une opportunité d'amélioration: Vous pouvez déplacer le contenu de chaque cas dans sa propre fonction. Si vous faites cela, vous avez deux choix: soit faire fonctionner les fonctions uniquement avec les arguments que vous leur transmettez, ce qui tend à rendre les choses plus modulaires, soit à rendre les fonctions de fermeture qui manipulent les données dans la fonction externe. Je dirais que ce dernier est moins idéal   — pour ces fins   — mais il peut être plus rapide à faire et ainsi être une "victoire rapide".

Disons que nous avions à l'origine:

function myBigFunction() { 
    var v1, v2, v3; 

    /* ...set v1, v2, and v3 to various things...*/ 

    switch (condition) { 
     case foo: 
      /* ...do lots of stuff involving v1 and v2...*/ 
      break; 

     case bar: 
      /* ...do lots of stuff involving v1 only...*/ 
      break; 

     case charlie: 
      /* ...do lots of stuff involving v2 and v3...*/ 
      break; 
    } 
} 

Puis:

La première option: Complètement fonctions indépendantes:

function myBigFunction() { 
    var v1, v2, v3; 

    /* ...set v1, v2, and v3 to various things...*/ 

    switch (condition) { 
     case foo: 
      doFooStuff(v1, v2); 
      break; 

     case bar: 
      doBarStuff(v1); 
      break; 

     case charlie: 
      doCharlieStuff(v2, v3); 
      break; 
    } 
} 

function doFooStuff(v1, v2) { 
} 

function doBarStuff(v1) { 
} 

function doCharlieStuff(v2, v3) { 
} 

... et bien sûr, les chances sont que vous auriez besoin de ces fonctions retourner quelque chose, puis gérer cela, par exemple:

case foo: 
     v1 = doFooStuff(v1, v2); 
     break; 

... si doFooStuff doit mettre à jour v1. Si elle a besoin de mettre à jour v1 et v2, vous pouvez retourner un tableau, ou faire v1 et v2 propriétés sur un objet que vous passez dans doFooStuff, etc. Voici un exemple d'utilisation des propriétés: Remplacer les v1 et v2 propriétés avec un objet (vdata) qui a v1 et v2propriétés sur elle:

var vdata = { 
    v1: "someInitialValue", 
    v2: "someInitialValue" 
}; 

... alors votre appel à doFooStuff:

case foo: 
     doFooStuff(vdata); 
     break; 

...qui permet doFooStuff de mettre à jour à la fois v1 et v2. Mais attention, vous ne voulez pas créer un évier avec toutes les variables de myBigFunction, cela sacrifierait la modularité.

La deuxième option: Utilisation de fermetures et de travailler directement avec les données de la fonction parent:

function myBigFunction() { 
    var v1, v2, v3; 

    /* ...set v1, v2, and v3 to various things...*/ 

    switch (condition) { 
     case foo: 
      doFooStuff(); 
      break; 

     case bar: 
      doBarStuff(); 
      break; 

     case charlie: 
      doCharlieStuff(); 
      break; 
    } 

    function doFooStuff() { 
     // work with v1 and v2 directly 
    } 

    function doBarStuff() { 
     // work with v1 directly 
    } 

    function doCharlieStuff() { 
     // work with v2 and v3 directly 
    } 
} 

Notez que ici, les différents sous-programmes sont des fermetures à l'intérieur myBigFunction et ils ont un accès direct à tous myBigFunction ' s variables locales. Ceci est plus modulaire, mais pas tout à fait l'approche modulaire complète de la première option. C'est aussi comme une version locale du problème des fonctions fonctionnant avec des variables globales: Les effets secondaires sont faciles à introduire et peuvent causer des problèmes.

La première option est préférée, mais si elle n'est pas pratique, la deuxième option peut au moins vous aider à rendre la logique de votre ligne principale plus claire. Et bien sûr, une combinaison des deux peut fonctionner.

+0

c'était un excellent conseil, merci! Je suis allé pour l'option des fermetures pour le moment et c'est déjà beaucoup plus facile à suivre. – meleyal

+0

@melayal: Excellent, content que cela ait aidé! Vous pouvez graduellement continuer à modulariser sur votre propre calendrier (l'une des grandes choses sur la façon dont les fermetures sont faciles dans JS). –

2

Consultez le State pattern.

J'ai écrit un answer en l'expliquant avec un exemple dans Java il y a quelque temps, mais il peut être facilement implémenté en JavaScript. Il s'agit essentiellement de modéliser le système en tant que machine à états finis.

+0

Merci pour le pointeur, JS.State semble être une bonne implémentation: http://jsclass.jcoglan.com/state.html – meleyal

Questions connexes