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!
Apply the Replace Type Code with Subclasses refactoring: Add subclasses for each type represented by the type code. Use a factory method to create the subclass objects based on the type. Apply Push Down Method by moving the switch-statement-abusing methods to the subclasses.
The first and second options to avoid switch cases keep the original string array cardTypes . All other examples base on the conversion to a List<string> . This is only a short comparison of meaningful substitutions of a switch case.
No its not neccesary. the default case in switch statement is not necessary,but it is useful when no case in switch is satisified or not matched then automatically it executes the default statement,if it is not there ,the switch statement is terminated.
You could do something like this:
public class TransportationCostCalculator { Dictionary<string,double> _travelModifier; TransportationCostCalculator() { _travelModifier = new Dictionary<string,double> (); _travelModifier.Add("bicycle", 1); _travelModifier.Add("bus", 2); _travelModifier.Add("car", 3); } public decimal CostOfTravel(string transportationMethod) => (decimal) _travelModifier[transportationMethod] * DistanceToDestination; }
You could then load the transportation type and it's modifier in a configuration file instead of using a switch statement. I put it in the constructor to show the example, but it could be loaded from anywhere. I would also probably make the Dictionary static and only load it once. There is no need to keep populating it each time you create a new TransportationCostCalculator
especially if it isn't going to change during runtime.
As noted above, here is how you could load it by a configuration file:
void Main() { // By Hard coding. /* TransportationCostCalculator.AddTravelModifier("bicycle", 1); TransportationCostCalculator.AddTravelModifier("bus", 2); TransportationCostCalculator.AddTravelModifier("car", 3); */ //By File //assuming file is: name,value System.IO.File.ReadAllLines("C:\\temp\\modifiers.txt") .ToList().ForEach(line => { var parts = line.Split(','); TransportationCostCalculator.AddTravelModifier (parts[0], Double.Parse(parts[1])); } ); } public class TransportationCostCalculator { static Dictionary<string,double> _travelModifier = new Dictionary<string,double> (); public static void AddTravelModifier(string name, double modifier) { if (_travelModifier.ContainsKey(name)) { throw new Exception($"{name} already exists in dictionary."); } _travelModifier.Add(name, modifier); } public double DistanceToDestination { get; set; } TransportationCostCalculator() { _travelModifier = new Dictionary<string,double> (); } public decimal CostOfTravel(string transportationMethod) => (decimal)( _travelModifier[transportationMethod] * DistanceToDestination); }
Edit: It was mentioned in the comments that this wouldn't allow the equation to be modified if it ever needed to change without updating the code, so I wrote up a post about how to do it here: http://structuredsight.com/2016/03/07/configuring-logic.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With