views:

442

answers:

8

I have an if else tree that is going to grow as I add additional items for it to maintain and I'm looking at the best way to write it for maintainability I'm starting with this code

private void ControlSelect()
{

    if (PostingType == PostingTypes.Loads && !IsMultiPost)
    {
     singleLoadControl.Visible = true;
          singleTruckControl.Visible = false;
          multiTruckControl.Visible = false;
          multiLoadControl.Visible = false;
    }
    else if (PostingType == PostingTypes.Trucks && !IsMultiPost)
    {
     singleLoadControl.Visible = false;
          singleTruckControl.Visible = true;
          multiTruckControl.Visible = false;
          multiLoadControl.Visible = false;
    }
    else if (PostingType == PostingTypes.Loads && IsMultiPost)
    {
     singleLoadControl.Visible = false;
          singleTruckControl.Visible = false;
          multiTruckControl.Visible = false;
          multiLoadControl.Visible = true;
    }
    else if (PostingType == PostingTypes.Trucks && IsMultiPost)
    {
     singleLoadControl.Visible = false;
        singleTruckControl.Visible = false;
          multiTruckControl.Visible = true;
        multiLoadControl.Visible = false;
    }
}

and thinking of re-factoring it to something like this

private void ControlSelect()
{
    List<UserControl> controlList = GetControlList();

      string visableControl = singleLoadControl.ID;
      if (PostingType == PostingTypes.Loads && !IsMultiPost)
      {
        visableControl = singleLoadControl.ID;
      }
      else if (PostingType == PostingTypes.Trucks && !IsMultiPost)
      {
        visableControl = singleTruckControl.ID;
      }
      else if (PostingType == PostingTypes.Loads && IsMultiPost)
      {
        visableControl = multiLoadControl.ID;
      }
      else if (PostingType == PostingTypes.Trucks && IsMultiPost)
      {
        visableControl = multiTruckControl.ID;
      }

      foreach (UserControl userControl in controlList)
      {
        userControl.Visible = (userControl.ID == visableControl);
      }
}

private List<UserControl> GetControlList()
{
    List<UserControl> controlList = new List<UserControl>
      {
        singleLoadControl,
            multiTruckControl,
            singleTruckControl,
            multiLoadControl
      };
      return controlList;
}

I take a performance hit but I can manage all of my controls is a single place

my other thought was to make each selected control block it own method, something like this

private void SetSingleLoadControlAsSelected()
{
      singleLoadControl.Visible = true;
      singleTruckControl.Visible = false;
      multiTruckControl.Visible = false;
      multiLoadControl.Visible = false;
}

I don't take a performance hit but I'm maintaining the controls in multiple location

I'm leaning for option one just because I like maintainability aspect of it.

+23  A: 

what about

singleLoadControl.Visible  = 
      PostingType == PostingTypes.Loads  && !IsMultiPost;      
singleTruckControl.Visible = 
      PostingType == PostingTypes.Trucks && !IsMultiPost;      
multiTruckControl.Visible  = 
      PostingType == PostingTypes.Loads  && IsMultiPost;      
multiLoadControl.Visible   =  
      PostingType == PostingTypes.Trucks && IsMultiPost;

if you want capability to make multiple controls visible (or add more enumerated values) decorate the enum with [Flags] attribute as follows:

[Flags]   
public enum PostTyp { None=0, IsMultiPost = 1, Loads = 2, Trucks = 4 }

and modify Code as follows:

singleLoadControl.Visible  = 
      ((PostingType &  (PostTyp.Loads | ~PostTyp.MultiCast)) 
         == PostingType );      
singleTruckControl.Visible = 
      ((PostingType & (PostTyp.Trucks | ~PostTyp.MultiCast)) 
         == PostingType );          
multiTruckControl.Visible  = 
      ((PostingType & (PostTyp.Loads  |  PostTyp.MultiCast)) 
         == PostingType );        
multiLoadControl.Visible   =  
      ((PostingType & (PostTyp.Trucks |  PostTyp.MultiCast)) 
         == PostingType );
Charles Bretana
This will still work. All of the values will be false except for one with the algorithm above.
JSBangs
@JS is right. Only one will get set to visible as all the boolean expressions are exclusive
Charles Bretana
It works for the scenario posted, but I feel this approach could prove hard to maintain should more enumeration values and controls be added, or combinations be required.If it never changes, however, I think this is a good, tidy refactoring.
Jeff Yates
@Jeff, using [Flags] decorated enumeration allows for unlimited expansion with minimum maintenance headaches... but requires you understand bitwise logic ...
Charles Bretana
@Charles: Yup, that's a reasonable idea and you could tidy that up further with an extension method for checking the values. Nice.
Jeff Yates
@Jeff, I'm only just this month switching from 2.x to 3.x, so extension methods are new to me... Thx for the idea!
Charles Bretana
I like the flags Idea
Bob The Janitor
@Charles: I use them often for flags checking. I usually create Contains and ContainsAny; the first matches a given set of flags exactly against a value, the second checks if any of the bits in the given flags are set.
Jeff Yates
+5  A: 

What about this:

     singleLoadControl.Visible = false;
     singleTruckControl.Visible = false;
     multiTruckControl.Visible = false;
     multiLoadControl.Visible = false;

    if (PostingType == PostingTypes.Loads && !IsMultiPost)
    {
            singleLoadControl.Visible = true;
    }
    else if (PostingType == PostingTypes.Trucks && !IsMultiPost)
    {
          singleTruckControl.Visible = true;
    }
    else if (PostingType == PostingTypes.Loads && IsMultiPost)
    {
        multiLoadControl.Visible = true;
    }
    else if (PostingType == PostingTypes.Trucks && IsMultiPost)
    {
          multiTruckControl.Visible = true;
}
Sergio
This is certainly better than the OPs code, but I feel it is on the path to an even tighter refactoring as suggested by Charles Bretana, Chris Brandsma, or myself.
Jeff Yates
+7  A: 

As you appear to be using an enumeration, I would recommend a switch with a default case to cope with unknown values. I believe this approach makes intentions clearer than doing all the checking in the assignment.

switch (PostingType)
{
case PostingTypes.Loads:
   singleLoadControl.Visible = !IsMultiPost;
   multiTruckControl.Visible = IsMultiPost;
   singleTruckControl.Visible = false;
   multiTruckLoadControl.Visible = false;
   break;

case PostingTypes.Trucks:
   singleLoadControl.Visible = false;
   multiTruckControl.Visible = false;
   singleTruckControl.Visible = !IsMultiPost;
   multiLoadControl.Visible = IsMultiPost;
   break;

default:
   throw InvalidOperationException("Unknown enumeration value.");
}
Jeff Yates
+1 for code readability/clarity vs. clever assignments and bitwiseness (is that even a word!?).
Metro Smurf
+1  A: 

If you are expecting to be adding a lot of different parameters, you could create a multi-dimensional array of objects

Arr[0][0] = singleLoadControl
Arr[0][1] = singleTruckControl
Arr[1][0] = multiLoadControl
Arr[1][1] = multiTruckControl

This is pretty scary, but makes for simpler if statements. If you're going to have loads of references to the controls anyhow, I'd rather use a code-based representation of what those loads are. Such an array can be wrapped in a class to let you access the elements using something more like:

ControlClassInstance.single.truck

You'd have code like this:

p1 = IsMultiPost ? ControlClassInstance.multi : ControlClassInstance.single
p2 = p1[PostingType] //(this call would mean adding an indexer)

This kind of solution is way too sophisticated unless you expect things to get complicated...and might be poor then, too.

Brian
I'm not sure sophisticated is the right word for this. Obfuscated, unmaintainable, confusing, overkill; these words seem a better fit. While the use becomes clearer, the implementation is convoluted to the point that others may struggle to understand and maintain it, which could lead to easy bugs.
Jeff Yates
@Jeff: Yeah, it does add way too much overhead. It would really only be justified if a lot of properties were being added and if the usage changes it fosters would bleed through into other places. But in that case, there's probably a better way. :::Shrug:::
Brian
+1  A: 
  singleLoadControl.Visible = false;
  singleTruckControl.Visible = false;
  multiTruckControl.Visible = false;
  multiLoadControl.Visible = false;


  singleLoadControl.Visible = (PostingType == PostingTypes.Loads && !IsMultiPost);
  singleTruckControl.Visible = (PostingType == PostingTypes.Trucks && !IsMultiPost);
  multiLoadControl.Visible = (PostingType == PostingTypes.Loads && IsMultiPost);
  multiTruckControl.Visible = (PostingType == PostingTypes.Trucks && IsMultiPost);
Chris Brandsma
There is no reason to initialize all the values to false as this will happen by the virtue of your later declarations.
Jeff Yates
I was thinking of something like this, but then just using a switch statement to select the specific instance to enable, but Jeff Yate's answer works pretty much the same as that anyway (and is probably a little more elegant with that boolean in there)
Grant Peters
Absolutely yes. But something in the post'ers answers made me think this was part of a lot more code. So I left them in.
Chris Brandsma
+2  A: 

If it would be otherwise sensible (for example, if these are already domain-specific custom controls), you could encapsulate the logic inside the controls themselves (Replace Conditional with Polymorphism). Perhaps create an interface like this:

public interface IPostingControl {
    void SetVisibility(PostingType postingType, bool isMultiPost);
}

Then each control would be responsible for its own visibility rules:

public class SingleLoadControl: UserControl, IPostingControl {

    // ... rest of the implementation
    public void SetVisibility(PostingType postingType, bool isMultiPost) {
        this.Visible = postingType == PostingType.Load && !isMultiPost);
    }
}

Finally, in your page, just iterate over your IPostingControls and call SetVisibility(postingType, isMultiPost).

Jeff Sternal
An interesting idea, but if the only reason to specialise is to add handling for this visibility logic, I don't think it's a good refactoring. I don't want to specialise TextBox's just to add specific code for handling when they should be visible - I don't even think every instance should need to know what state it's owner wants things.
Jeff Yates
Agreed, this is overkill if the controls in question are just TextBoxes (or any other out-of-the-box UserControl), the domain isn't likely to change, and the logic is only in a single place. It might not even be worth it if only two of those are true. But if one or none of those are true, it definitely would be worth it!
Jeff Sternal
this would tightly couple the control to this specific use
Bob The Janitor
Not necessarily this specific use, but it would certainly tightly couple the control to the domain (where the concepts of `PostingType` and `IsMultiPost` are coherent), which is sometimes desirable.
Jeff Sternal
when is tightly coupling something desirable? Necessary at time yes, but never desirable
Bob The Janitor
I shouldn't have said 'coupling' since I really just meant expressing the domain. I'm talking about things like creating a WebControl that presents a drop-down list of all possible statuses for deliveries, which you could use throughout your application in several different contexts. Basically, creating a domain representation that helps you modularize the UI.
Jeff Sternal
A: 
private void ControlSelect()
{
    if (PostingType == PostingTypes.Loads && !IsMultiPost)
    {
        singleLoadControl.Visible = true;
        singleTruckControl.Visible = false;
        multiTruckControl.Visible = false;
        multiLoadControl.Visible = false;
        return;
    }
    if (PostingType == PostingTypes.Trucks && !IsMultiPost)
    {
        singleLoadControl.Visible = false;
        singleTruckControl.Visible = true;
        multiTruckControl.Visible = false;
        multiLoadControl.Visible = false;
        return;
    }
    if (PostingType == PostingTypes.Loads && IsMultiPost)
    {
        singleLoadControl.Visible = false;
        singleTruckControl.Visible = false;
        multiTruckControl.Visible = false;
        multiLoadControl.Visible = true;
        return;
    }
    if (PostingType == PostingTypes.Trucks && IsMultiPost)
    {
        singleLoadControl.Visible = false;
        singleTruckControl.Visible = false;
        multiTruckControl.Visible = true;
        multiLoadControl.Visible = false;
        return;
    }
}
yigal
this is exactly what I'm NOT trying to do
Bob The Janitor
-1 this is a horrible suggestion, IMHO. It makes the code worse rather than better, introducing multiple opportunities for maintenance bugs.
Jeff Yates
+1  A: 

Have you considered the State Pattern?

krdluzni