views:

345

answers:

3

Hello,

An enum structure declared in its own class is a member variable to the business logic class. That enum basically represents the state of that other class.

Although I have revisited the issue several times, replacing, or getting rid of those case statements proves quite frustrating to me.

Several business logic methods simple iterate over the enum and change the state of that class by assigning another value of the same enum, and other properties.

    public enum MyEnum{ A,B,C,D }

The business logic class has this enum as a member:

    public class BusinessLogic {
             private MyEnum CurrentSelection;
             private int propertyX;
             private int propertyY;

        public void operation1(){
             switch(CurrentSelection){
             case A: {alter propertyX this way; break;}
             case B: {alter propertyY this way; break;}
             case C: {alter propertyX that way; break;}
             case D: {alter propertyY that way; break;}
             }

            }

            public void operation2(){
             switch(CurrentSelection){
             case A: {CurrentSelection=MyEnum.B; break;}
             case B: {CurrentSelection=MyEnum.C; break;}
             ....etc
             }
            }

            public void operation3(){
             switch(CurrentSelection){
             case A: {CurrentSelection=MyEnum.D; break;}
             case B: {CurrentSelection=MyEnum.A; break;}
             ....etc
             }
            }
}

Another client class will instatiate the business logic class, initialing its properties and then using its operation methods.

What I have successfully done (with help from SO) is encapsulate the operation methods into a command pattern structure so I can call the operations without any case statements. (here).

I guess my trouble is how to encapsulate the case statements in my business logic class. I suspect that I would need polymorphism and the appropriate data structures.

The refactoring experts suggest each case statement should be an implementation of a common interface. But if I have 3 methods iterating over a 4-member enum that means I'd probably need 3 interfaces with 4 implementations each, giving me 12 classes (plus 3 interfaces). Wouldn't that be a class overload? The logic is working fine with those 3 methods as they are, but the issue are the repeating switch/case statements.

Is there a way to refactor those switch statements but avoid a myriad of other classes in the name of polymorphism? Would it be possible to factor out the iterate-over-the-enum part? Or just the cases (where the logic is) should be refactored?

Many thanks in advance.


As a first step I removed those methods completely my having them implement a simple interface:

public interface Command {
void execute();

}

So, its operation method implemented the command interface:

public class Operation1 implements Command {
            private void doOperation1(){
                 switch(CurrentSelection){
                 ..all the cases here
                 }

                }

        public void execute() {
     doOperation1();
    }

}

As I see this, it will buy my a cleaner business logic class, but the trail of the switch cases will remain, right?

+1  A: 

you could include all three methods in an interface and have different implementations of that interface (one for each enum), and have this buisness class delegate the calls..

also for changing the state, have the return value from the operation be that common interface so that upon invoking these methods, the invoking class could return the appropriate object for the next state.

rmn
Thank you for the reply.Yes, but wouldn't those individual implementations still carry the switch/case statements? I would be cleaning up the business logic and client classes , but the new classes implementing each of the 3 interfaces would still have the switch cases in them.Is my reasoning correct, or perhaps you wouldn't mind elaborating a little bit further
denchr
You could have the different class accessed through the enum itself - i.e. the enum implements the interface, or something similar, and then it performs the operation, so you don't have to switch on it at all. This is similar to the command pattern.
aperkins
+1  A: 

To elaborate on my comment, and approach what I think rmn is talking about, you could do the following:

public interface MyInterface {
    operation1(property);
    operation2(property);
    operation3(property);
}

public enum MyEnum implements MyInterface {
    A {
        operation1(property) {
            //Do Stuff
        }
        operation2(property) {
            //Do Stuff
        }
        operation3(property) {
            //Do Stuff
        }
    },
    B {
        operation1(property) {
            //Do Stuff
        }
        operation2(property) {
            //Do Stuff
        }
        operation3(property) {
            //Do Stuff
        }
    },
    C {
        operation1(property) {
            //Do Stuff
        }
        operation2(property) {
            //Do Stuff
        }
        operation3(property) {
            //Do Stuff
        }
    },
    D {
        operation1(property) {
            //Do Stuff
        }
        operation2(property) {
            //Do Stuff
        }
        operation3(property) {
            //Do Stuff
        }
    } 
}

This makes it so that you have a command pattern - i.e. something that does the work for you based on the different element selected. It is also similar to a state pattern, although the focus here is on the execution rather than the data or the state of data.

Hope this helps.

aperkins
this is exactly what i suggested, just have the operatios return MyInterface, so that you could change the state upon invoking a function. something likeMyInterface operation1 (property) {// do stuff, dont change statereturn this;}and:MyInterface operation2 (property) {//do stuff and change statereturn A();}and then u can use it as:currstate = currstate.operation1()and you're done
rmn
Thanks I will try to incorporate that to the problem logic. I admit I can't see right off the bat how that would help, because the operation methods are part of the behavior of the business logic object. I'd still need to delegate from that object to the enum structure right? -- apologies if my question is a bit confusing. I will need to experiment with your suggestion, draw some conclusions and then formulate a more accurate question.
denchr
@rmn I know this is what you had suggested, that is why I upvoted you. I just thought he might need a better example, and wanted to give it, which these comment boxes don't really allow.
aperkins
@rmn: Sorry wrote my comment before seeing yours. That makes more sense. Thank you for the elaboration! Will go ahead and try it
denchr
@denchr yes, it does separate some of the logic, however that is something of the point for a command pattern. You are swapping in and out behavior on a particular object based on a different need. At some level, the work has to get done, and the work code will need to live somewhere. You could also delegate to an instance object through the enum that has the logic in it - i.e. you get that object off of the enum, if that would make it easier.
aperkins
denchr, i understand :) thanks for taking the time and writing down the code, i'm sure its of great help to the OP.
rmn
@aperkins: My intent was to try and keep state and behavior encapsulated to the business logic object. I would need to make the enum structure (which basically holds all the operations and their alternatives) agnostic to my business object. I can see better how you and rmn suggest this, and I hope it will be possible to implement without exposing my main object's state or behavior. Thanks for the great comments and suggestions
denchr
@denchr Good luck. I remember the first time I implemented a command pattern - it was a very different way of handling logic than I was used to. :)
aperkins
@rmn: Thanks for the answer!@aperkins: Thanks for the elaboration!
denchr
A: 

As you may have guessed from the other responses, this is a common problem and has been codified into a refactoring by Martin Fowler. Take a look at the refactoring Replace Conditional with Polymorphism. There is also a similar codified refactoring by Joshua Kerievsky (Replace Conditional with Strategy).

SingleShot
Yes, that is what I was referring to by saying: "The refactoring experts suggest each case statement should be an implementation of a common interface" in my original post. However, in this one, it wasn't straightforward to me.
denchr