20061022

Switch Statement = Ghetto... Enum = Ghetto

U like the title? Aww yeaa foo!

I like bustin' some OOP nyahh all the time, and some of the developers I have paired with notice two things: (1) I don't do switch statements, (2) I rarely (never) use enums...

Why you ask?

Switch / Case: It's gay.
Switch / Case: Too much code in the containing class.
Switch / Case: Can become complex.
Switch / Case: Containing class needs to be changed when a condition is added/changed.
Switch / Case: A 'case' is hard coded "case IsUgly:"

Enum: It's just a frigging int...
Enum: It's gay.

I will show you some cool tricks how to get rid of the nastyness...

Okay... here is a test.

[Test]
public void ShouldDJ()
{
    Person jonas = new Person("Jonas");
    jonas.Do(DoSomething.DJ);
 
    Assert.AreEqual("DJ", jonas.IsDoing);
}


Okay, nothing fancy... we are verifying that Person is DJing. But now here comes the ugly.

public enum DoSomething
{
    DJ = 1,
    BMX = 2,
    GetDrunk = 3
}


What the F**k is that!?!? That's the enum! we are just replacing numbers with something readable... So nasty!

Here's the Person object:

public class Person
{
    private readonly string name;
    private string isDoing;
 
    public Person(string name)
    {
        this.name = name;
    }
 
    public string Name
    {
        get { return name; }
    }
 
    public void Do(DoSomething doStuff)
    {
        switch(doStuff)
        {
            case DoSomething.DJ:
                isDoing = "DJ";
                break;
            case DoSomething.BMX:
                isDoing = "BMX";
                break;
            case DoSomething.GetDrunk:
                isDoing = "Aww yeaa foo!";
                break;
        }
    }
 
    public string IsDoing
    {
        get { return isDoing; }
    }
}


I know I coded a switch for this sample (almost made me throw up).

So what is the big deal with using enums or switch?


Three words: Object Oriented Programming. You basically make objects to build your application. Yes, they make take longer to develop, but they are waaaay more maintainable. And if you programmed it so it can be readable, you can read your code like a story.

How can we fix the code that you showed above?



First off, you will notice the Person() object has a method called "Do". What do you see very similar to what it's doing?... Yess they all set "IsDoing" property with a value which is a string.

What we need to implement is a strategy pattern:
public abstract class WhatToDoStrategy
{
    public static WhatToDoStrategy DJ = new DJStrategy();
    public static WhatToDoStrategy BMX = new BMXStrategy();
    public static WhatToDoStrategy GET_DRUNK = new GetDrunkStrategy();
    public abstract string Do { get; }
 
    private class DJStrategy : WhatToDoStrategy
    {
        public override string Do
        {
            get { return "DJ"; }
        }
    }
 
    private class BMXStrategy : WhatToDoStrategy
    {
        public override string Do
        {
            get { return "BMX"; }
        }
    }
 
    private class GetDrunkStrategy : WhatToDoStrategy
    {
        public override string Do
        {
            get { return "Aww yea foo!"; }
        }
    }


With the WhatToDoStrategy() strategy, our Person() object is now "naked" to all the logic when it comes to what it needs to do... it will simply just call an object.

Sometimes, you may not want to call your strategy directly. As for this example, I created an interface called IStuffToDo that is taken in by Person() object.

public interface IStuffToDo
{
    WhatToDoStrategy Doing();
}
 
public class DJing : IStuffToDo
{
    public WhatToDoStrategy Doing()
    {
        return WhatToDoStrategy.DJ;
    }
}
 
public class BMXing : IStuffToDo
{
    public WhatToDoStrategy Doing()
    {
        return WhatToDoStrategy.BMX;
    }
}
 
public class GettingDrunk : IStuffToDo
{
    public WhatToDoStrategy Doing()
    {
        return WhatToDoStrategy.GET_DRUNK;
    }
}


The code above replaces the enum we had. This is wayyyyyy better. now when you look at the code for DJing(), you know it is calling the DJ strategy, not a fu*king number 1 which doesnt make any sense.

Here is the modification to the Person() object:
    public class Person
    {
        //...
        public void Do(IStuffToDo stuffToDo)
        {
            isDoing = stuffToDo.Doing().Do;
        }
        //...
    }


Notice how it is taking in an IStuffToDo object, which in turn calls our strategy...

Lastly, how do we verify if this code works? Tests!!!!

[TestFixture]
public class PersonTest
{
    [Test]
    public void ShouldDJ()
    {
        Person jonas = new Person("Jonas");
        jonas.Do(new DJing());
 
        Assert.AreEqual("DJ", jonas.IsDoing);
    }
 
    [Test]
    public void ShouldBMX()
    {
        Person jonas = new Person("Jonas");
        jonas.Do(new BMXing());
 
        Assert.AreEqual("BMX", jonas.IsDoing);
    }
 
    [Test]
    public void ShouldGetDrunk()
    {
        Person jonas = new Person("Jonas");
        jonas.Do(new GettingDrunk());
 
        Assert.AreEqual("Aww yea foo!", jonas.IsDoing);
    }
}


Nyahhh!!!

2 comments:

Anonymous said...

I have so much enum centric code. I just tried refactoring my code as you have shown here and it works so much better. Thank you!

gutzoft said...

Try the following code. The only problem I see with you're code is that you are implicitly creating your strategy classes in your code. Tell, Don't Ask is my motto. Make those type codes work for a living.

Additional Classes is a cached repository for the strategies classes. and then a locater for the repository. I even included a composite strategy, ALL, cause sometimes we have too much time on our hands.

using System.Collections;
using NUnit.Framework;

namespace Ghetto
{
[TestFixture]
public class TestGhetto
{
private Person gutzer;

[SetUp]
public void SetUp()
{
gutzer = new Person("Gutzer");
}

[TearDown]
public void TearDown()
{
gutzer = null;
}

[Test]
public void ShouldDJ()
{
gutzer.WhatImDoing(Doing.DJ);
gutzer.DoingIt();
Assert.IsTrue(gutzer.Spent);
}

[Test]
public void ShouldBMX()
{
gutzer.WhatImDoing(Doing.BMX);
gutzer.DoingIt();
Assert.IsTrue(gutzer.Spent);
}

[Test]
public void ShouldGetDrunk()
{
gutzer.WhatImDoing(Doing.DRUNK);
gutzer.DoingIt();
Assert.IsTrue(gutzer.Spent);
}

[Test]
public void ShouldDoItAll()
{
gutzer.WhatImDoing(Doing.ALL);
gutzer.DoingIt();
Assert.IsTrue(gutzer.Spent);
}
}

public class Doing
{
public static Doing DJ = new Doing();
public static Doing BMX = new Doing();
public static Doing DRUNK = new Doing();
public static Doing ALL = new Doing();
}


public class Person
{
private readonly string mName;
private bool spent;
private Doing isDoing;

public Person(string name)
{
mName = name;
}

public string Name
{
get { return mName; }
}

public bool Spent
{
get { return spent; }
}

public void DoingIt()
{
DoingThings things = DoingThingsLocater.GetDoingThings();
spent = things.DoIt(isDoing);
}

public void WhatImDoing(Doing doing)
{
isDoing = doing;
}
}

public class DoingThingsLocater
{
public static DoingThings GetDoingThings()
{
return new DoingThings();
}
}

public class DoingThings
{
private readonly Hashtable things = new Hashtable();

public DoingThings()
{
things[Doing.BMX] = new BMXStrategy();
things[Doing.DJ] = new DJStrategy();
things[Doing.DRUNK] = new GetDrunkStrategy();
things[Doing.ALL] = new AllStrategy(new DJStrategy(), new BMXStrategy(), new GetDrunkStrategy());
}

public bool DoIt(Doing doing)
{
DoingStrategy doingIt = things[doing] as DoingStrategy;
if (doingIt != null)
return doingIt.GettingItDone();
return false;
}
}

public abstract class DoingStrategy
{
public abstract bool GettingItDone();
}

public class DJStrategy : DoingStrategy
{
public override bool GettingItDone()
{
return true;
}
}

public class BMXStrategy : DoingStrategy
{
public override bool GettingItDone()
{
return true;
}
}

public class GetDrunkStrategy : DoingStrategy
{
public override bool GettingItDone()
{
return true;
}
}

public class AllStrategy : DoingStrategy
{
private readonly IList mStrategyCompositeList = new ArrayList();

public AllStrategy(DoingStrategy dj, DoingStrategy bmx, DoingStrategy drunk)
{
mStrategyCompositeList.Add(dj);
mStrategyCompositeList.Add(bmx);
mStrategyCompositeList.Add(drunk);
}

public override bool GettingItDone()
{
bool result = true;

foreach (DoingStrategy strategy in mStrategyCompositeList)
{
result &= strategy.GettingItDone();
}

return result;
}
}
}