0

At the moment I have seven if statements that resemble the following code:

if(hit.collider.gameObject.tag == "Colour1" && start_time > look_at_time)
{
    new_colour1.ChangeObjectMaterialColour(hit.collider.gameObject.renderer.material.color);

   var colums = GameObject.FindGameObjectsWithTag("column");
   foreach( GameObject c in colums)
    c.GetComponent<MeshRenderer>().materials[1].color = new_colour1.orignalMaterial;
}


else if(hit.collider.gameObject.tag == "Colour2" && start_time > look_at_time)
{
    new_colour2.ChangeObjectMaterialColour(hit.collider.gameObject.renderer.material.color);
    var colums = GameObject.FindGameObjectsWithTag("column");
    foreach( GameObject c in colums)
       c.GetComponent<MeshRenderer>().materials[1].color = new_colour2.orignalMaterial;
}

Each statement is roughly 6 lines of code and takes up a lot of space and can be a little tricky to read. What I'm wanting to do is find a way to re factor this so that my code is little less clunky and doesn't take up too much space.

I had thought about changing my collection of if statements into a switch statement but I discovered that switch statements can't handle two arguments like I have above. If there any other way I can re factor my code but keep the same functionality or am I stuck with my collection of if statements?

edit

Updated to contain two of my 7 statements. Please note, I am trying to reduce the amount of IF statements I have, or find a more clever way to do them. I don't want to add any more extra lines of code or if statements.

N0xus
  • 2,674
  • 12
  • 65
  • 126
  • 1
    I think asking this question here: http://codereview.stackexchange.com/ will be better – Kamil Budziewski Oct 25 '13 at 08:17
  • 1
    Show us more... there's not a lot to work with here. – NPSF3000 Oct 25 '13 at 08:17
  • As I said, it is literally 7 of the above IF statements but I'll edit my post and also cross post to code review (thank you for that, didn't know there was one!) – N0xus Oct 25 '13 at 08:22
  • For future reference, after posting your second if statement it's obvious they are not identical, and therefor it was important to know the differences. – NPSF3000 Oct 25 '13 at 08:43
  • Yeah, you're correct. Should have put up more than a single omitted if statement. – N0xus Oct 25 '13 at 08:47

2 Answers2

2

I honestly think that the most readable and maintainable in your case is simply to move the logic in those if blocks into functions:

  if(hit.collider.gameObject.tag == "Colour1" && start_time > look_at_time)
    {
        DoTheThing(new_colour1);
    }
if(hit.collider.gameObject.tag == "Colour2" && start_time > look_at_time)
    {
        DoTheThing(new_colour2);
    }
//and so on



 void DoTheThing(MyType newColour)
  {
 newColour.ChangeObjectMaterialColour(hit.collider.gameObject.renderer.material.color);
      var colums = GameObject.FindGameObjectsWithTag("column");
       foreach( GameObject c in colums)
        c.GetComponent<MeshRenderer>().materials[1].color = newColour.orignalMaterial;
    }

As to using switch you can of course do something like this:

switch(hit.collider.gameObject.tag)
{
  case "Colour1":
      if (start_time>look_at_time)
      {
        //do something
      }
      else if (start_time<look_at_time)
      {
        //something else
      }
      else
      {
         //they are equal, etc, etc
      }
    break;
case "Colour2":
  //and so on
}

or if all of the if blocks make the same comparison between start_time and look_at_time then you could invert it:

enum TimeDifference
{
   LessThan,Equal,GreaterThan
}
//just one way to get a constant switchable value, not necessarily the best
var diff = TimeDifference.Equal;
if (start_time>look_at_time) {diff=TimeDifference.LessThan}
else if (start_time<look_at_time) {diff=TimeDifference.GreaterThan}

switch(diff)
{
  case TimeDifference.LessThan:
     switch(hit.collider.gameObject.tag)
     {
       case "Colour1":
         //do something for Colour1
        break;   
        case "Colour2":
         //do something for Colour2
        break;
      }//end switch Colour
     break;
  case TimeDifference.GreaterThan
    switch(hit.collider.gameObject.tag)
     {
       case "Colour1":
         //do something for Colour1
        break;   
        case "Colour2":
         //do something for Colour2
        break;
      }//end switch Colour
     break;  
   default:
    //nested switch for Colour in the == case, you get the idea.
}

although the readbility of the either of the above versus several nicely formatted and separate if blocks is very debatable.

Another approach is to use a Dictionary<string,Action<int,int>>: (assumingstart_time and look_at_time are both ints AND all of your logic involves them somehow)

var Actions = new Dictionary<string,Action<int,int>>();
Actions.Add("Colour1",(s,l)=>{
 if (start_time>look_at_time)
          {
            //do something
          }
          else if (start_time<look_at_time)
          {
            //something else
          }
          else
          {
             //they are equal, etc, etc
          }

});

and then your main code becomes 1 line:

Actions[hit.collider.gameObject.tag](start_time,look_at_time);

And I am sure that there are many many more ways you can rearrange that code.

Stephen Byrne
  • 7,400
  • 1
  • 31
  • 51
1

Thanks for updating your code, now we can start looking at what is duplicated. For example, the only difference between the two blocks of code is the string "Color1" and "Color2" in the if statement, and the variable new_colour1 which is replaced with new_colour2.

From here I'd suggest something like the following:

//This should be declared once - e.g. class level not method level.
var colorDict = new Dictionary<string, [new_color_type]> 
                    {{"Colour1", new_colour1}, {"Colour2", new_colour2}};

[new_color_type] newColor;
if(start_time > look_at_time && colorDict.TryGetValue(
        hit.collider.gameObject.tag,
        out newColor))
{
   newColor.ChangeObjectMaterialColour(
                            hit.collider.gameObject.renderer.material.color);

   var colums = GameObject.FindGameObjectsWithTag("column");
   foreach( GameObject c in colums)
      c.GetComponent<MeshRenderer>().materials[1].color =
                                                    newColor.orignalMaterial;
}
Jodrell
  • 34,946
  • 5
  • 87
  • 124
NPSF3000
  • 2,421
  • 15
  • 20
  • why use a `Dictionary` instead of a `switch`? – Jodrell Oct 25 '13 at 08:49
  • @Jodrell, write a version using a switch statement and compare the code :) My guess was that the Dictionary would be cleaner, shorter and simpler. – NPSF3000 Oct 25 '13 at 08:51
  • I did, http://stackoverflow.com/a/19585269/659190. Its my assertion that the `switch` shows obvious intent and will offer improved runtime performance. Whilst its possible to use the `Dictionary` save some typing this brevity doesn't translate to performance or readability. – Jodrell Oct 25 '13 at 08:54
  • @Jodrell, as noted on your answer you have introduced a bug, as well as wrote unclean code by deciding to use the switch. I will remove my -1 when you fix it, but I think I've made my point. – NPSF3000 Oct 25 '13 at 09:00
  • If your point was, there are bugs in my code then yes, you made it. I hope I've fixed those now. If your point was, `Dictionary` is an inherently better approach than a `switch` statement, then we disagree. I appreciate the utility of `TryGetValue` but if the tag values are known at compile time, it makes sense to build them into the byte code, this must have benefits. – Jodrell Oct 25 '13 at 09:34
  • @Jodrell my point was that switch isn't always the best choice - in this case because *developers stuff it up* - it took you half an hour and three edits to end up with a version of code that's *still* more complicated, longer, and more fragile. For example, why do you have to maintain a boolean flag telling you that no option was met? By using a dictionary I didn't: Forget to put in a break. Forget to take care of default. Accidently write non-functionally identical code. Duplicate logic with `new_colour1` and `new_colour2` when `newColour` would have worked just fine. – NPSF3000 Oct 25 '13 at 09:43
  • 1
    After reasearching here, http://stackoverflow.com/questions/1334087/net-switch-vs-dictionary-for-string-keys/1336112#1336112, I deleteing my answer, since this is a `string` lookup. – Jodrell Oct 25 '13 at 09:45
  • @Jodrell - thank you :) But I'd point out that performance was never an issue and yes I'm a game developer that uses this particular game engine (this is unity code) in commercially released titles - so it's something I'm keenly aware of. – NPSF3000 Oct 25 '13 at 09:47