If Statements Vs Object Oreiented practices
When i was browsing, i found that there is a campaign called "The Anti If Campaign" https://www.antiifcampaign.com/code-recruitment.html. They are very much related to the Agile team. I read saw a claim that says the "IF Statements" are difficult to maintain and to add/delete codes inside them. They claim to use inheritance and Object Oriented best practices to enhance the code.
So i said to myself how can that impact performance? Then i decided to create this example:
Let’s take it step by step
if i have two products A & B
A comes in three flavors C, D & E.
C comes in three flavors too F, G & H.
so when you come to the bottom of it, you see that there are actually 6 products. C, D, E, F, G & H.
The If-Statement-Way
If i have a code that will simulate that scenario, it would be like this:
if( a == 1)
{
if (b == 2)
{
if (c == 3) doSomething(a, b, c);
else if (c == 4) doSomething(a, b, c);
else doSomething(a, b, c);
}
else if (b == 3) doSomething(a, b, c);
else doSomething(a, b, c);
}
else doSomething(a, b, c);
The Anti-If-Way
If you follow the people who are "Anti IF Statement", i would write the code in a different way:
public abstract class MyBaseClass
{
public int a, b, c;
public MyBaseClass() { }
public abstract void doSomething();
}
public abstract class AEqual1Class : MyBaseClass
{
public AEqual1Class() {a = 1;}
}
public abstract class BEqual2Class : AEqual1Class
{
public BEqual2Class(){b = 2;}
}
public class CEqual3Class : BEqual2Class
{
public CEqual3Class(){c = 3;}
public override void doSomething(){…}
}
public class CEqual4Class : BEqual2Class
{
public CEqual4Class(){c = 4;}
public override void doSomething(){…}
}
public class CEqualOtherClass : BEqual2Class
{
public CEqualOtherClass() {c = 100;}
public override void doSomething(){…}
}
public class BEqual3Class : AEqual1Class
{
public BEqual3Class(){b = 3;c = 100;}
public override void doSomething(){…}
}
public class BEqualOtherClass : AEqual1Class
{
public BEqualOtherClass(){b = 100;c = 100;}
public override void doSomething(){…}
}
public class AEqualOtherClass : MyBaseClass
{
public AEqualOtherClass(){a = 100;b = 100;c = 100;}
public override void doSomething(){…}
}
The Results
So i decided to compare between the two methods in all cases by calculating the execution time of each one and below are the results:
Test |
in case of IF statements |
in case of *NO* IF statements |
a=100, b=100, c=100 |
1.00619492645912 seconds |
0.499456347674696 seconds |
a=1, b=100, c=100 |
0.333323067994865 seconds |
0.249895601507773 seconds |
a=1, b=3, c=100 |
0.200379324787097 seconds |
0.166854522360645 seconds |
a=1, b=3, c=100 |
0.142798249143871 seconds |
0.125212235872841 seconds |
a=1, b=3, c=4 |
0.111325223259706 seconds |
0.10132541944173 seconds |
a=1, b=2, c=3 |
0.0910784876580769 seconds |
0.0835666302589329 seconds |
To be honest the results came us a surprise to me J. But I’ve learned something today.
Updated --- some people wanted to get the test project
Test Project
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace AntiIfStatements
{
class Program
{
static void Main(string[] args)
{
QueryPerfCounter counter = new QueryPerfCounter();
// Calculate time per iteration in nanoseconds
double result;
counter.Start();
counter.Stop();
Console.WriteLine("in case of IF statements a=100, b=100, c=100");
counter.Start();
IIfStatements(100,100,100);
counter.Stop();
result = counter.Duration(1);
Console.WriteLine(result / 1000000000 + " seconds");
Console.WriteLine("in case of *NO* IF statements a=100, b=100, c=100");
counter.Start();
NoIfStatement(new AEqualOtherClass());
counter.Stop();
result = counter.Duration(2);
Console.WriteLine(result / 1000000000 + " seconds");
Console.WriteLine("in case of IF statements a=1, b=100, c=100");
counter.Start();
IIfStatements(1, 100, 100);
counter.Stop();
result = counter.Duration(3);
Console.WriteLine(result / 1000000000 + " seconds");
Console.WriteLine("in case of *NO* IF statements a=1, b=100, c=100");
counter.Start();
NoIfStatement(new BEqualOtherClass());
counter.Stop();
result = counter.Duration(4);
Console.WriteLine(result / 1000000000 + " seconds");
Console.WriteLine("in case of IF statements a=1, b=3, c=100");
counter.Start();
IIfStatements(1, 3, 100);
counter.Stop();
result = counter.Duration(5);
Console.WriteLine(result / 1000000000 + " seconds");
Console.WriteLine("in case of *NO* IF statements a=1, b=3, c=100");
counter.Start();
NoIfStatement(new BEqual3Class());
counter.Stop();
result = counter.Duration(6);
Console.WriteLine(result / 1000000000 + " seconds");
Console.WriteLine("in case of IF statements a=1, b=2, c=100");
counter.Start();
IIfStatements(1, 3, 100);
counter.Stop();
result = counter.Duration(7);
Console.WriteLine(result / 1000000000 + " seconds");
Console.WriteLine("in case of *NO* IF statements a=1, b=2, c=100");
counter.Start();
NoIfStatement(new CEqualOtherClass());
counter.Stop();
result = counter.Duration(8);
Console.WriteLine(result / 1000000000 + " seconds");
Console.WriteLine("in case of IF statements a=1, b=2, c=4");
counter.Start();
IIfStatements(1, 3, 4);
counter.Stop();
result = counter.Duration(9);
Console.WriteLine(result / 1000000000 + " seconds");
Console.WriteLine("in case of *NO* IF statements a=1, b=2, c=4");
counter.Start();
NoIfStatement(new CEqual4Class());
counter.Stop();
result = counter.Duration(10);
Console.WriteLine(result / 1000000000 + " seconds");
Console.WriteLine("in case of IF statements a=1, b=2, c=3");
counter.Start();
IIfStatements(1, 3, 3);
counter.Stop();
result = counter.Duration(11);
Console.WriteLine(result / 1000000000 + " seconds");
Console.WriteLine("in case of *NO* IF statements a=1, b=2, c=3");
counter.Start();
NoIfStatement(new CEqual3Class());
counter.Stop();
result = counter.Duration(12);
Console.WriteLine(result / 1000000000 + " seconds");
Console.ReadLine();
}
static void IIfStatements(int a, int b, int c)
{
if( a == 1)
{
if (b == 2)
{
if (c == 3) doSomething(a, b, c);
else if (c == 4) doSomething(a, b, c);
else doSomething(a, b, c);
}
else if (b == 3) doSomething(a, b, c);
else doSomething(a, b, c);
}
else doSomething(a, b, c);
}
static void doSomething(int a,int b,int c)
{
System.Threading.Thread.Sleep(1000);
System.Console.WriteLine("\tvalues a={0}, b={1}, c={2}", a, b, c);
}
static void NoIfStatement(MyBaseClass MyClass)
{
MyClass.doSomething();
}
}
public abstract class MyBaseClass
{
public int a, b, c;
public MyBaseClass() { }
public abstract void doSomething();
}
public abstract class AEqual1Class : MyBaseClass
{
public AEqual1Class() {a = 1;}
}
public abstract class BEqual2Class : AEqual1Class
{
public BEqual2Class(){b = 2;}
}
public class CEqual3Class : BEqual2Class
{
public CEqual3Class(){c = 3;}
public override void doSomething()
{
System.Threading.Thread.Sleep(1000);
System.Console.WriteLine("\tvalues a={0}, b={1}, c={2}", a, b, c);
}
}
public class CEqual4Class : BEqual2Class
{
public CEqual4Class(){c = 4;}
public override void doSomething()
{
System.Threading.Thread.Sleep(1000);
System.Console.WriteLine("\tvalues a={0}, b={1}, c={2}", a, b, c);
}
}
public class CEqualOtherClass : BEqual2Class
{
public CEqualOtherClass() {c = 100;}
public override void doSomething()
{
System.Threading.Thread.Sleep(1000);
System.Console.WriteLine("\tvalues a={0}, b={1}, c={2}", a, b, c);
}
}
public class BEqual3Class : AEqual1Class
{
public BEqual3Class(){b = 3;c = 100;}
public override void doSomething()
{
System.Threading.Thread.Sleep(1000);
System.Console.WriteLine("\tvalues a={0}, b={1}, c={2}", a, b, c);
}
}
public class BEqualOtherClass : AEqual1Class
{
public BEqualOtherClass(){b = 100;c = 100;}
public override void doSomething()
{
System.Threading.Thread.Sleep(1000);
System.Console.WriteLine("\tvalues a={0}, b={1}, c={2}", a, b, c);
}
}
public class AEqualOtherClass : MyBaseClass
{
public AEqualOtherClass(){a = 100;b = 100;c = 100;}
public override void doSomething()
{
System.Threading.Thread.Sleep(1000);
System.Console.WriteLine("\tvalues a={0}, b={1}, c={2}",a,b,c);
}
}
}
Comments
Anonymous
May 12, 2010
It's very interesting. Would you please attach your test project into this post?Anonymous
May 12, 2010
you got it Kevin, i've updated the articleAnonymous
May 12, 2010
The results are interesting, but really make perfect sense. For a=b=c=100, the "if" version would have to evaluate every single one of the various "if" statements before finally arriving at a doSomething. The "NoIf" version can go straight to the correct doSomething. Even for a=1, b=2, c=3, the number of statements being executed for both is very small, hence why both times are low. That said, what are the memory footprints of each version?Anonymous
May 12, 2010
Not sure I'd want to write a class for each case though.Anonymous
May 13, 2010
The comment has been removedAnonymous
May 15, 2010
The comment has been removedAnonymous
June 01, 2010
Quoting you: "Let’s take it step by step if i have two products A & B A comes in three flavors C, D & E. C comes in three flavors too F, G & H. so when you come to the bottom of it, you see that there are actually 6 products. C, D, E, F, G & H." Did you mean "B comes in three flavors too"?