views:

171

answers:

4

Assume you have controls A, B, C, D and E all with a Visibility property. You also have states 1,2,3,4,5 and 6 in which various combinations of your controls would be displayed.

Currently this is being handled by switch statements for each state: i.e.

Select Case PageState
    case "1"
      a.visible = false
      b.visible = true
      c.visible = false 
      d.visible = true
      e.visible = false
   case "2"
      a.visible = true
      b.visible = true
      c.visible = false 
      d.visible = true
      e.visible = false
  case ... 
 End Select

As you can imagine, this becomes a pain as every state needs a show/hide statement for each control. How can I refactor this so that adding controls and/or states becomes trivial?

My first instinct is to extend the control and add a collection of states that it should display for but this sounds like overkill.

Edit I was deliberately vague in my question in case this has other implications. In my current instance the "controls" in question are ASP Panels. Does that change anything?

+3  A: 

Use the State Pattern

Hooray Im Helping
Long live the state-machine!
Jason Down
Love the name. thanks for the response.
Rob Allen
+2  A: 

Use the state pattern. Have each state operate on your page (abstracted as an interface):

public interface IControlSituationPage // your page implements this
{
    void SetAvisibility(bool visibility);
    void SetBVisibility(bool visibility);
    ...
}

....

public interface PageState // each state implements this
{
    void ApplyState(IControlSituationPage page);
}

.. then, after figuring out which state you're in (factory pattern maybe), call the method:

// ... somewhere..
_state.ApplyState(this);

Of course, this is in lieu of actually using a more robust presentation pattern like MVC or MVP.

Matt Hinze
While I'd love to redo this as MVC, that is beyond the scope of my current engagement. I'll have to settle for refactoring.
Rob Allen
Correct me if I am wrong but in this situation, adding a control means updating the interface, then all of the implementations of that interface (each state) does it not?
Rob Allen
and the tests, yep. would take about 1 minute with resharper or another tool.
Matt Hinze
+1  A: 

[Note: I'm a java guy, so my implementation below is in Java - it should be very similar for C# or whatever other language you want to use]

Rob points out the trouble with the above solution -- it's really not maintainable.

It's also incredibly verbose (the GoF state pattern usually is...).

Sometimes the State pattern isn't really what you should use.

How about you turn the problem inside out and let data drive it?

Look at it as "for which states should X be shown?"

Try something like the following:

  1. create an enum that represents your states
  2. create an EnumSet for each control (they're very efficient)
  3. in each EnumSet, add the states for which that control should be visible
  4. when the state changes, check the visibility of each control

A sketchy implementation (not compiled/tested)

public enum MyStates {State1, State2, State3...}

public class VisibilityManager {
    private Map<Component, Set<MyStates>> managedComponents =
        new HashMap<Component, Set<MyStates>>();
    private Component component;
    public void setVisibility(Component component, MyStates... states) {
        Set<MyStates> visibleStates = EnumSet.of(states);
        managedComponents.put(component, states);
    }
    public void update(MyStates currentState) {
        for (Map.Entry<Component, Set<MyStates>> e : managedComponents.entrySet())
            e.getKey().setVisible(e.getValue().contains(currentState));
    }
}

// then in your GUI setup

VisibilityManager v = new VisibilityManager();
v.setVisibility(comp1, MyStates.State1, MyStates.State2);
v.setVisibility(comp1, MyStates.State3);
v.setVisibility(comp1, MyStates.State1, MyStates.State3, MyStates.State5);
...

// and when you change the state
v.update(newState);

Hope this helps!

Scott Stanchfield
A: 

Scott got the point - for such trivial cases (when you only need control over visibility) it makes sense to go with the reversed logic. State pattern becomes extremely useful if you need to perform state transitions and have your UI abstract from business logic.

Misha