Say I have a class like this for calculating the cost of travelling different distances with different modes of transportation:
public class TransportationCostCalculator
{
public double DistanceToDestination { get; set; }
public decimal CostOfTravel(string transportMethod)
{
switch (transportMethod)
{
case "Bicycle":
return (decimal)(DistanceToDestination * 1);
case "Bus":
return (decimal)(DistanceToDestination * 2);
case "Car":
return (decimal)(DistanceToDestination * 3);
default:
throw new ArgumentOutOfRangeException();
}
}
This is fine and all, but switch cases can be a nightmare to maintenance wise, and what if I want to use airplane or train later on? Then I have to change the above class. What alternative to a switch case could I use here and any hints to how?
I'm imagining using it in a console application like this which would be run from the command-line with arguments for what kind of transportation vehicle you want to use, and the distance you want to travel:
class Program
{
static void Main(string[] args)
{
if(args.Length < 2)
{
Console.WriteLine("Not enough arguments to run this program");
Console.ReadLine();
}
else
{
var transportMethod = args[0];
var distance = args[1];
var calculator = new TransportCostCalculator { DistanceToDestination = double.Parse(distance) };
var result = calculator.CostOfTravel(transportMethod);
Console.WriteLine(result);
Console.ReadLine();
}
}
}
Any hints greatly appreciated!
I prefer to use
Enum
for that like this:And use it like this method:
Note that above method is case-sensitive, So you can capitalize first char;
Related Answer
This is a case for the strategy design pattern. Create a base class, say
TravelCostCalculator
, then develop classes for each mode of travel you will consider, each overriding a common method,Calculate(double)
. You can then instantiate the specificTravelCostCalculator
as needed using the factory pattern.The trick is in how to construct the factory (without a switch statement). The way I do this is by having a static class constructor (
public static Classname()
- not an instance constructor) that registers each strategy class with the factory in aDictionary<string, Type>
.Since C# does not run class constructors deterministically (like C++ does in most cases) you have to explicitly run them to ensure they will run. This could be done in the main program or in the factory constructor. The downside is that if you add a strategy class, you must also add it to the list of constructors to be run. You can either create a static method that must be run (
Touch
orRegister
) or you can also useSystem.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor
.You could define an abstract class like this, and have each
TransportationMethod
extend the abstract class:So in your actual method call, it could be rewritten like this:
Sounds like a good candidate for dependency-injection:
Now you can easily create a new class
Plane
:Now create a constrcutor for your calculator that expects an instance of
ITransportation
. Within yourCostOfTravel
-method you can now callITransportation.CalcCosts(DistanceToDestination)
.This has the advantage that you can exchange your actual transportation-instance without any code-change to your
TransportationCostCalculator
-class.To complete this design you might also create a
TransportationFactory
as follows:Which you call like