2010-11-12 9 views
2

J'ai un ENUM avec certains Etats en elle:Refactor instruction if utiliser modèle approprié

enum State 
{ 
    A, 
    B, 
    C, 
    D 
} 

et un objet qui a un état correspondant:

class MyObject 
{ 
    State state; 
} 

J'ai besoin d'écrire un algorithme prend deux instances myObject et fait quelque chose en fonction des états particuliers de ces instances:

void doWork(MyObject o1, MyObject o2) 
{ 
    if (o1.state == A && o2.state == A) 
    { 
      // do something 
    } 
    else if (o1.state == A && o2.state == B) 
    {} 
    // etc for all combinations... 

} 

Il est évident que cette approche a beaucoup de problèmes et j'aimerais la changer pour idéalement se débarrasser de l'instruction if/else.

Existe-t-il un modèle pour une telle exigence?

Merci

+3

@ org.life.java: non-sens - utiliser == est complètement sûr avec des énumérations. –

+1

@ Michael Borgwardt Je m'excuse, supprimé le commentaire également pour le même scénario '==' est plus préférable, a appris de cela http://stackoverflow.com/questions/1750435/comparing-java-enum-members-or-equals –

Répondre

3

Qu'est-ce que vous pourriez ne, même si je ne suis pas sûr que ce serait beaucoup mieux, est une sorte de matrice de toutes les combinaisons possibles de deux state valeurs; vous pouvez alors utiliser o1.state et o2.state comme index dans cette matrice.

Vous pouvez stocker des choses différentes dans cette matrice:

  • une valeur unique que vous pouvez utiliser comme valeur discriminante pour un bloc switch qui remplacerait vos if .. else if .. else blocs - pas beaucoup d'une amélioration, vraiment.

Ou votre matrice pourrait contenir ...

Si vous voulez vraiment vous débarrasser des déclarations if, cette deuxième option pourrait être le meilleur; Prenez note, cependant, que votre code ne sera plus rapproché à un endroit, comme ce serait le cas avec les blocs if/switch, mais répartis sur plusieurs objets/classes de commande différents.

// forgive my syntax errors etc., my Java has definitely gone a little rusty! 

interface WorkCommand { 
    public abstract void run(MyObject o1, MyObject o2); 
} 

... 

Map<Pair<State,State>, WorkCommand> commands; 
//^pseudo-code type for your command look-up map; type Pair<X,Y> doesn't exist, 
// so replace this with something sensible! 

void doWork(MyObject o1, MyObject o2) 
{ 
    WorkCommand worker = commands.get(new Pair<State,State>(o1, o2)); 
    worker.run(o1, o2); 
} 
1

J'avais fait cela sans doute, son au moins plus lisible

void doWork(MyObject o1, MyObject o2) { 
    switch (o1.state) { 
     case A: { 
      switch (o2.state) { 
       case A: { 

        break; 
       } 
       case B: { 

        break; 
       } 

      } 
      break; 
     } 
    } 
} 
2

Une façon de structurer, est que vous pouvez avoir une méthode abstraite dans votre ENUM chaque élément mettrait en œuvre :

enum State 
{ 
    A{ 
     public void void doSomeWork(State state){ 
     switch(state){ 
      case A: 
      case B: 
      ... 
     } 
     } 
    }, 
    B, 
    C, 
    D 

    abstract void doSomeWork(State state); 
} 

Ensuite, votre méthode peut ressembler à

void doWork(MyObject o1, MyObject o2){ 
    o1.state.doSomeWork(o2.state); 
} 
0

en utilisant seulement deux combinaisons d'état, un commutateur imbriqué est probablement le plus rapide à mettre en œuvre et comprendre:

switch (o1.state) { 
case X: switch(o2.state) { } 
//..etc 
} 

Si l'ordre de cas est hors de propos pour certaines combinaisons, vous pourrez peut-être échanger o1 et o2 dans les cas, puis déposez-les dans le switch (et évitez le code en double). De plus, pour toutes les combinaisons de cas qui ont le même comportement, vous pouvez tirer parti du comportement de l'effondrement. Enfin, en l'implémentant de cette manière, vous pouvez rendre plus évident ce qui se passe réellement avec ces combinaisons, de sorte que vous puissiez mettre en œuvre une approche beaucoup plus intelligente.

2

Oui, il s'appelle le ... state pattern. L'important est de n'avoir qu'un seul état pour définir le comportement, c'est-à-dire que vous devrez peut-être combiner vos objets object1.state et object2.state dans un méta-état. Enregistrez ce méta-état avec un état-stat, de sorte que lorsque Myobject.state change, le méta-état soit mis à jour.

interface MyObjectStates { 
    void doWork(MyObject o1, MyObject o2); 
} 

class MyObjectStatesAA implements MyObjectStates { 
    void doWork(MyObject o1, MyObject o2) { 
    // do dowork for both states A 
    } 

class MyObjectStatesBB implements MyObjectStates { 
    void doWork(MyObject o1, MyObject o2) { 
    // do dowork for both states B 
    } 

// etc 

Vous devez ensuite tenir un objet dans une MyObjectStates statecontext et mettre à jour quand un MyObject.state est modifié. Vous pouvez même être en mesure de supprimer l'état enum ensemble. Si cette approche vous semble intéressante, donnez-moi une note et j'élabore si vous le souhaitez. Le modèle d'état présente l'avantage que vous n'avez pas besoin d'enregistrer et de relire une énumération et que vous avez choisi un chemin de code différent en conséquence, mais que vous fournissez un code séparé avec chaque état que vous voulez gérer différemment.

+0

Ce est la seule solution qui se débarrasse vraiment des instructions conditionnelles - switch/case est toujours une déclaration conditionnelle laide (sauf si vous écrivez des analyseurs syntaxiques). –

0

La solution OO à votre problème est le modèle d'état. Il s'agit d'encapsuler des conditionnels alternatifs avec un objet d'état. Vous pouvez trouver plus d'informations sur le modèle here et here. Au fait, je suggérerais FORTEMENT d'acheter ce livre si vous ne le possédez pas déjà.

À la votre!