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
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
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.
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.
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.
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.
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.
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:
Use polymorphism to get the correct run-time behavior you need without conditional statements.
Take all your conditional statements, and move them into some sort of "factory" which will hand you the appropriate type at run-time.
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.
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.
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.
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.