After Thought

Archive for July 2010

Eliminate if else using Smart Enums

with 4 comments

Open/Close principle (SOLID) states that class should be Open for extension and closed for modification. One way to achieve this is to eliminate conditional statements (if…else) in our code. Continuing our example of PlasticType from the Smart Enums post, let us say we have to write a method that has to decide the recycling program based on the plastic type.

public class Controller
{
	private readonly IRecycleManager _recycleManager;

	public Controller(IRecycleManager recycleManager)
	{
		_recycleManager = recycleManager;
	}

	public void Recycle(PlasticData plasticData)
	{
		if (plasticData.PlasticType == "pet")
		{
			_recycleManager.RecycleType1(plasticData);
		}
		if (plasticData.PlasticType == "hdpe")
		{
			_recycleManager.RecycleType2(plasticData);
		}
		if (plasticData.PlasticType == "pvc")
		{
			_recycleManager.RecycleType3(plasticData);
		}
	}
}

In the above example, we inject RecycleManager in to our Controller class that is responsible for recycling the plastic. The Recycle method calls the appropriate recycling program based on the plastic type supplied in the PlasticData model.

public class PlasticData
{
	public string PlasticType { get; set; }
	public decimal Weight { get; set; }
	public string Color { get; set; }
}

 

 

public interface IRecycleManager
{
	void RecycleType1(PlasticData plasticData);
	void RecycleType2(PlasticData plasticData);
	void RecycleType3(PlasticData plasticData);
}

 

 

public class RecycleManager : IRecycleManager
{
	public void RecycleType1(PlasticData plasticData)
	{
		// code here
	}

	public void RecycleType2(PlasticData plasticData)
	{
		// code here
	}

	public void RecycleType3(PlasticData plasticData)
	{
		// code here
	}
}

 

We see that Recycle method is filled with multiple conditional statements making it open to modification violating the Open/Close principle. The reason I say it is open to modification is because if we want to add recycling program to another type of plastic in future, we have to add another conditional statement and add another method to RecycleManager class. It also makes our tests less maintainable and brittle. If we can eliminate the conditional statements in the Recycle method, we have a good chance of making this code adhere to Open/Close principle. One effective way of doing this is to use Smart Enums. Read more about about Smart Enums here. Let us go ahead and add a behavior to PlasticType from the previous post as shown below.

public class PlasticType
{
	public static PlasticType PET = new PlasticType("pet", "Polyethylene Terephthalate",
													(recycleManager, plasticData) =>
													recycleManager.RecycleType1(plasticData));

	public static PlasticType HDPE = new PlasticType("hdpe", "High-density Polyethylene",
													 (recycleManager, plasticData) =>
													 recycleManager.RecycleType2(plasticData));

	public static PlasticType PVC = new PlasticType("pvc", "Polyvinyl Chloride", 
													(recycleManager, plasticData) =>
													recycleManager.RecycleType3(plasticData));

	private PlasticType(string key, string description, Action<IRecycleManager, PlasticData> recycle)
	{
		Key = key;
		Description = description;
		Recycle = recycle;
		NamedConstants.Add(key, this);
	}


	public static PlasticType GetFor(string key)
	{
		if (key == null)
		{
			return null;
		}

		PlasticType plasticType;
		NamedConstants.TryGetValue(key, out plasticType);
		return plasticType;
	}

	private static readonly Dictionary<string, PlasticType> NamedConstants = new Dictionary<string, PlasticType>();

	public string Key { get; set; }
	public string Description { get; set; }
	public Action<IRecycleManager, PlasticData> Recycle;
}

Now the plastic type also defines the recycling program that it is associated with. Now let us go ahead an modify our Recycle method in the Controller class.

public class Controller
{
	private readonly IRecycleManager _recycleManager;

	public Controller(IRecycleManager recycleManager)
	{
		_recycleManager = recycleManager;
	}

	public void Recycle(PlasticData plasticData)
	{
		PlasticType.GetFor(plasticData.PlasticType).Recycle(_recycleManager, plasticData);
	}
}

We see that the Recycle method is reduced to just one line and we got rid of all the conditional statements in the method. Now our Controller is open to extension and closed to modification. If were to add a new recycling program for another type of plastic in the future, we just have to add another method to RecycleManager class.

Written by shashankshetty

July 18, 2010 at 10:39 pm

Posted in C#, Uncategorized

Tagged with

Smart Enums

with 2 comments

Our team was introduced to Smart Enums by J.P. Boodhoo in one of his Nothin’ but .NET classes in Austin. An example of a simple Smart Enum is shown below.

public class PlasticType
{
	public static PlasticType PET = new PlasticType("pet");
	public static PlasticType HDPE = new PlasticType("hdpe");
	public static PlasticType PVC = new PlasticType("pvc");
	public static PlasticType LDPE = new PlasticType("ldpe");
	public static PlasticType PP = new PlasticType("pp");
	public static PlasticType PS = new PlasticType("ps");
	public static PlasticType Other = new PlasticType("other");

	private PlasticType(string key)
	{
		Key = key;
	}
	public string Key { get; set; }
}

PlasticType is a Smart Enum that is just a collection of static classes that can be used instead of a regular Enum in our code.

The example above describes different types of plastic  (source: Wikipedia) that are in use today. The numbers were devised by Plastic Bottle Institute of the Society of the Plastics Industry to make recycling easier (On a separate note, please recycle plastic after use. Let us make the world a better place to live for the next generation). You may ask what are PET, HDPE or PVC. To make the output more readable if the PlasticTypes were to be printed, let us add Description property as shown below.

public class PlasticType
{
	public static PlasticType PET = new PlasticType("pet", "Polyethylene Terephthalate");
	public static PlasticType HDPE = new PlasticType("hdpe", "High-density Polyethylene");
	public static PlasticType PVC = new PlasticType("pvc", "Polyvinyl Chloride");
	public static PlasticType LDPE = new PlasticType("ldpe", "Low-density Polyethylene");
	public static PlasticType PP = new PlasticType("pp", "Polypropylene");
	public static PlasticType PS = new PlasticType("ps", "Polystyrene");
	public static PlasticType Other = new PlasticType("other", "Others");

	private PlasticType(string key, string description)
	{
		Key = key;
		Description = description;
	}

	public string Key { get; set; }
	public string Description { get; set; }	
}

Smart Enum is already looking better than our regular Enum with the addition of Description property. We (myself and Clinton Sheppard) decided to take it a step further to make the usage of PlasticType easier in our code. So we added a GetFor(key) method as shown below.

public class PlasticType
{
	public static PlasticType PET = new PlasticType("pet", "Polyethylene Terephthalate");
	public static PlasticType HDPE = new PlasticType("hdpe", "High-density Polyethylene");
	public static PlasticType PVC = new PlasticType("pvc", "Polyvinyl Chloride");
	public static PlasticType LDPE = new PlasticType("ldpe", "Low-density Polyethylene");
	public static PlasticType PP = new PlasticType("pp", "Polypropylene");
	public static PlasticType PS = new PlasticType("ps", "Polystyrene");
	public static PlasticType Other = new PlasticType("other", "Others");

	private PlasticType(string key, string description)
	{
		Key = key;
		Description = description;
        NamedConstants.Add(key, this);
	}

	public static PlasticType GetFor(string key)
	{
		if (key == null)
		{
			return null;
		}

		PlasticType plasticType;
		NamedConstants.TryGetValue(key, out plasticType);
		return plasticType;
	}

	private static readonly Dictionary<string, PlasticType> NamedConstants = new Dictionary<string, PlasticType>();

	public string Key { get; set; }
	public string Description { get; set; }
}

GetFor(key) method converts a given key to the corresponding Smart Enum that can be used within our code. This limits the usage of strings only within the Smart Enum making renaming easier across the project. We have created a NamedConstant class (our name for Smart Enum) that is part of MvbaCore project that provides other helper methods like Equals, Values (enumerates over all the available PlasticTypes). Inheriting PlasticType from NamedConstant class would add all the helper methods automatically as shown below.

public class PlasticType : NamedConstant&lt;PlasticType&gt;
{
	public static PlasticType PET = new PlasticType("pet", "Polyethylene Terephthalate");
	public static PlasticType HDPE = new PlasticType("hdpe", "High-density Polyethylene");
	public static PlasticType PVC = new PlasticType("pvc", "Polyvinyl Chloride");
	public static PlasticType LDPE = new PlasticType("ldpe", "Low-density Polyethylene");
	public static PlasticType PP = new PlasticType("pp", "Polypropylene");
	public static PlasticType PS = new PlasticType("ps", "Polystyrene");
	public static PlasticType Other = new PlasticType("other", "Others");

	private PlasticType(string key, string description)
	{
		Key = key;
		Description = description;
		NamedConstants.Add(key, this);
	}

	public string Description { get; set; }
}

NamedConstant<T> makes creating Smart Enums easier, so we can focus on solving a business problem rather than building helper methods around it.

We can use Smart Enums not only to compare types like any other regular Enum but also add behavior to them making it smarter as described in this post.

You can create a Smart Enum as described above without a key or description. It depends on the problem you are trying to solve.

Written by shashankshetty

July 18, 2010 at 9:51 pm

Posted in C#, Uncategorized

Tagged with