views:

3211

answers:

8

Are there any design patterns/methods/ways to remove nested if then else conditions/switch statements?

I remember coming across some methods used by the Google folks listed in a Google code blog post. Can’t seem to find it now though

+5  A: 

You want to use a refactoring that replaces a conditional using a polymorphic class. For example.

Or here's another example

Essentially the ideal is very simple you create an object heirarchy, and move the various behaviors into an overriden method. You will still need a method to create the right class but this can be done using a factory pattern.

Edit

Let me add that this is not a perfect solution in every case. As (I forgot your name sorry) pointed in my comments, some times this can be a pain especially if you have to create an object model just to do this. This refactoring excells if you have this:

function doWork(object x)
{

   if (x is a type of Apple)
   {
      x.Eat();

   } else if (x is a type of Orange)
   {
      x.Peel();
      x.Eat();
   }

}

Here you can refactor the switch into some new method that each fruit will handle.

Edit

As someone pointed out how do you create the right type to go into doWork, there are more ways to solve this problem then I could probally list so some basic ways. The first and most straight forward (and yes goes against the grain of this question) is a switch:

class FruitFactory
{
   Fruit GetMeMoreFruit(typeOfFruit)
   {
         switch (typeOfFruit)
         ...
         ...
   }
}

The nice thing about this approach is it's easy to write, and is usually the first method I use. While you still have a switch statement its isolated to one area of code and is very basic all it returns is a n object. If you only have a couple of objects and they;re not changing this works very well.

Other more compelx patterns you can look into is an Abstract Factory. You could also dynamically create the Fruit if your platform supports it. You could also use something like the Provider Pattern. Which essentially to me means you configure your object and then you have a factory which based on the configuration and a key you give the factory creates the right class dynamically.

JoshBerke
Works if you have objects and polymorphism handy. Pain if you don't.
Adam Hawes
Yes quite true also if the logic is fairly simple well then it might not be worth the pain...but if there is a lot going on within in each body of the conditional then simply breaking it out can reduce the complexity you have to worry about at any time.
JoshBerke
How is doWork() handed the correct type?
Esteban Araya
A: 

You don't say what language you're using, but if you are using an OO language such as C++, C# or Java, you can often use virtual functions to solve the same problem as you are currently solving with a switch statement, and in a more extensible way. In the case of C++, compare:

class X {
public:
 int get_type();  /* Or an enum return type or similar */
 ...
};

void eat(X& x) {
 switch (x.get_type()) {
 TYPE_A: eat_A(x); break;
 TYPE_B: eat_B(x); break;
 TYPE_C: eat_C(x); break;
 }
}

void drink(X& x) {
 switch (x.get_type()) {
 TYPE_A: drink_A(x); break;
 TYPE_B: drink_B(x); break;
 TYPE_C: drink_C(x); break;
 }
}

void be_merry(X& x) {
 switch (x.get_type()) {
 TYPE_A: be_merry_A(x); break;
 TYPE_B: be_merry_B(x); break;
 TYPE_C: be_merry_C(x); break;
 }
}

with

class Base {
 virtual void eat() = 0;
 virtual void drink() = 0;
 virtual void be_merry() = 0;
 ...
};

class A : public Base {
public:
 virtual void eat() { /* Eat A-specific stuff */ }
 virtual void drink() { /* Drink A-specific stuff */ }
 virtual void be_merry() { /* Be merry in an A-specific way */ }
};

class B : public Base {
public:
 virtual void eat() { /* Eat B-specific stuff */ }
 virtual void drink() { /* Drink B-specific stuff */ }
 virtual void be_merry() { /* Be merry in an B-specific way */ }
};

class C : public Base {
public:
 virtual void eat() { /* Eat C-specific stuff */ }
 virtual void drink() { /* Drink C-specific stuff */ }
 virtual void be_merry() { /* Be merry in a C-specific way */ }
};

The advantage is that you can add new Base-derived classes D, E, F and so on without having to touch any code that only deals with pointers or references to Base, so there is nothing that can get out of date the way that a switch statement can in the original solution. (The transformation looks very similar in Java, where methods are virtual by default, and I'm sure it looks similar in C# too.) On a large project, this is a huge maintainability win.

j_random_hacker
Why the downvotes people?
j_random_hacker
probably because virtual functions are how C++ implements polymorphism; +1 to counter cowardly drive-by downvote
Steven A. Lowe
@Steven: I guess... I would've hoped it would be obvious that this applies in any OO language (I know that the Java version is almost identical, but for the fact that methods are "virtual" by default, and it's probably the same with C#), but in any case I've edited to make this more explicit.
j_random_hacker
+4  A: 

Have you read this on flattening arrow code from Coding Horror?

You can replace throw with return or goto if you're using a language without exceptions.

Mark
+1 guard clauses are a good way to reduce nested ifs and exit a function early. A lot cleaner and easier to read IMHO.
JoshBerke
+1  A: 

I actually wrote about how to solve this issues in my blog in April of 2008. Take a look here and let me know what you think.

I recommend you:

  1. Use polymorphism to get the correct run-time behavior you need without conditional statements.

  2. Take all your conditional statements, and move them into some sort of "factory" which will hand you the appropriate type at run-time.

  3. You're done. Wasn't that easy? :)

If you want to see some actual code sample on how transform your code, head over to my blog.

P.S. This is not a cheap attempt at self promotion; I've been a SO user for a long time now, and this is the first time I've linked to my blog - and I've only done so because I think it's relevant.

Esteban Araya
If you excerpt the article, or at least hit on the main points in this post (rather than forcing the user to click the link just to find out what it's about) then you'll probably get more upvotes...
Adam Davis
@Adam: Yeah, I was thinking I should have done that on my first edit of this answer, but it's late and I'm tired. I went ahead and followed your advice, however. Thanks!
Esteban Araya
+2  A: 

Were you thinking of Google's "The Clean Code Talks -- Inheritance, Polymorphism, & Testing" video? It discusses approaches to using Object Oriented techniques to remove if/switch conditionals.

Zach Scrivena
A: 

You might want to look at the Strategy Pattern, wherein instead of putting a long chain of ifs with linked conditions, you abstract each condition to a different object, each one defining what its specific behavior.

The class that defines those objects will implement an Interface which will be called by the parent object.

Jon Limjap
another variation is a Template Pattern....very simillar
JoshBerke
A: 

How about:

/* Code Block 1... */

if(/* result of some condition or function call */)
{
   /* Code Block 2... */

   if(/* result of some condition or function call */)
   {
      /* Code Block 3... */

      if(/* result of some condition or function call */)
      {
         /* Code Block 4... */
      }
   }
}

Becomes this:

/* Code Block 1... */
IsOk = /* result of some condition or function call */

if(IsOK)
{
   /* Code Block 2... */
   IsOk = /* result of some condition or function call */
}

if(IsOK)
{
   /* Code Block 3...*/
   IsOk = /* result of some condition or function call */
}

if(IsOK)
{
   /* Code Block 4...*/
   IsOk = /* result of some condition or function call */
}

/* And so on... */

You can of course return if ever IsOk becomes false, if appropriate.

Steve Melnikoff
A: 
  1. Write a program to print the following triangle of numbers
    1 1 2 1 2 3 1 2 3 4 1 2 3 4 5
MOON