Railway Ticket Fare Calculation With Strategy Pattern

Introduction

Design Pattern is a very wonderful part of object-oriented programming. It provides flexibility, extensibility, and clean code features. Railway Ticket Price can be calculated and implemented with the help of design patterns.

Problem Statement

A passenger travels from one station to another station by train and he/she needs a ticket. He pays the fare for the traveled stations and, Railways has some particular rule related to the fare. It is based on stations, senior citizens, festivals, etc.

Railway rule says "If a passenger travels up to 5 stations, then fare is only Rs. 10. But, after the five stations, for every five or less than five stations, an extra fare of Rs. 5 will be charged. If the passenger is a senior citizen, then Railway will offer them some discount. And, some additional fare will be added on the occasion of some particular festivals." 

Example
 
Let us consider there are 15 stations named, one, two, three, four...... fifteen and the passengers are traveling with different cases.
  1. If passenger A travels from one to five stations, then he will be charged only Rs. 10.
  2. If Passenger B travels from one to six stations, then he will be charged Rs. 15.
  3. If Passenger C travels from six to ten station, then he will be charged Rs 15.
  4. If Passenger D travels from one to fifteen station, then he will be charged Rs 20. 
Solution

This problem statement can be solved by using "Strategy Desing Pattern." Because different types of fare strategy can be applied based on the context. Below are the different types of fare strategies,
  1. Station Fare Rule Strategy
  2. Age Fare Rule Strategy
  3. Festival Fare Rule Strategy
  4. Distance Fare Rule Strategy
  5. Class Type like Sleeper, General, First Ac, Second AC, etc. Fare Rule Strategy
  6. Basic Fare Rule Strategy
  7. Train Type Fare Rule Strategy 
So, each fare calculation algorithm will be written separately for each Fare Rule Strategy and these will not interfere with each other. New fare rule strategy can be added and written in case of new fare rule. So, it will also follow the concept of "Open for Extension and Close for Modification".
 
Diagrams - Depenency Diagram
Class Diagram
 
 
Code Explanation
 
IFareStrategy Interface

This interface defines the common strategy for the calculation of fare so that implementing a class can implement fare algorithm based on the context.
  1. using TrainFair.Models;  
  2.   
  3. namespace TrainFair.FareCalculator  
  4. {  
  5.     public interface IFareStrategy {  
  6.         float GetFare(IFareRule ruleValues, float basicFare);  
  7.     }  
  8. }  
StationRuleFareCalculator class

This class calculates the fare based on the number of stations traveled and some ruleset which has been defined in the section of the problem statement. 
  1. using System;  
  2. using TrainFair.Models;  
  3.   
  4. namespace TrainFair.FareCalculator  
  5. {  
  6.     public class StationRuleFareCalculator : IFareStrategy  
  7.     {  
  8.         public float GetFare(IFareRule ruleValues, float basicFare) {  
  9.             var stationFareRuleModel = ruleValues as StationFareRuleModel;  
  10.             if (stationFareRuleModel == null || stationFareRuleModel.StationsCounts <= 0)  
  11.                 return 0;  
  12.   
  13.             int chargingStations = (int)Math.Ceiling(stationFareRuleModel.StationsCounts / 5.0);  
  14.             if (chargingStations <= 1)  
  15.                 return basicFare;  
  16.   
  17.             int restChargingStations = chargingStations - 1;  
  18.             var totalFare = basicFare + restChargingStations * stationFareRuleModel.IncrementalPrice;  
  19.   
  20.             return totalFare;  
  21.         }  
  22.     }  
  23. }  
AgeRuleFareCalculator class
 
This class implements the Fare algorithm, especially for the senior citizen. If the traveler is a senior citizen, then a particular dicount will be given to him/her. So, this class implements that algorithm.
  1. using TrainFair.Models;  
  2.   
  3. namespace TrainFair.FareCalculator  
  4. {  
  5.     public class AgeRuleFareCalculator : IFareStrategy  
  6.     {  
  7.         public float GetFare(IFareRule ruleValues, float basicFare) {  
  8.             var ageFareRuleModel = ruleValues as AgeFareRuleModel;  
  9.             if (ageFareRuleModel == null)  
  10.                 return 0;  
  11.   
  12.             if (ageFareRuleModel.Age < 60) return basicFare;  
  13.             var totalFare = basicFare - (basicFare * ageFareRuleModel.Discount);  
  14.             return totalFare;  
  15.         }  
  16.     }  
  17. }   
FestivalRuleFareCalculator class

This class implements the fare price for the festival season. Some additional price will be added or some particular discount will be offered on the ocassion a particular festival. 
  1. using TrainFair.Models;  
  2.   
  3. namespace TrainFair.FareCalculator  
  4. {  
  5.     public class FestivalRuleFareCalculator : IFareStrategy  
  6.     {  
  7.         public float GetFare(IFareRule ruleValues, float basicFare) {  
  8.             var festivalFareRuleModel = ruleValues as FestivalFareRuleModel;  
  9.             if (festivalFareRuleModel == null)  
  10.                 return basicFare;  
  11.   
  12.             float totalFare = basicFare + festivalFareRuleModel.FestivalAdditionalFare;  
  13.             return totalFare;  
  14.         }  
  15.     }  
  16. }  
FareRuleCalculatorContext class
  1. using TrainFair.Models;  
  2.   
  3. namespace TrainFair.FareCalculator  
  4. {  
  5.     public class FareCalculatorContext {  
  6.   
  7.         private readonly IFareStrategy _fareStrategy;  
  8.         public FareCalculatorContext(IFareStrategy fareStrategy) {  
  9.             this._fareStrategy = fareStrategy;  
  10.         }  
  11.   
  12.         public float GetFareDetails(IFareRule fareRules, float basicFare)  
  13.         {  
  14.             return _fareStrategy.GetFare(fareRules, basicFare);  
  15.         }  
  16.     }  
  17. }  
And, here are some model classes based on the contexts like station fare, age fare, festival fare, etc.
 
IFareRule interface

This is the basic fare rule model class and every model class implements it. 
  1. namespace TrainFair.Models  
  2. {  
  3.     public interface IFareRule  
  4.     {  
  5.         int FareRuleId { getset; }  
  6.     }  
  7. }  
StationFareRuleModel class
 
This class defines the properties for the Station Fare Rule.
  1. namespace TrainFair.Models  
  2. {  
  3.     public class StationFareRuleModel : IFareRule  
  4.     {  
  5.         public int FareRuleId { getset; }  
  6.   
  7.         public int  StationsCounts { getset; }  
  8.   
  9.         public float IncrementalPrice { getset; }  
  10.     }  
  11. }  
AgeFareRuleModel class
 
This class defines the properties for the Age in case of a senior citizen. 
  1. namespace TrainFair.Models  
  2. {  
  3.     public class AgeFareRuleModel : IFareRule  
  4.     {  
  5.         public int FareRuleId { getset; }  
  6.   
  7.         public int Age { getset;  }  
  8.   
  9.         public float Discount { getset; }  
  10.     }  
  11. }  
FestivalFareRuleModel class
 
This class defines the properties of festival and type of additional price or dicount on festival. 
  1. namespace TrainFair.Models  
  2. {  
  3.     public class FestivalFareRuleModel : IFareRule  
  4.     {  
  5.         public int FareRuleId { getset; }  
  6.   
  7.         public string FestivalName { getset; }  
  8.   
  9.         public float FestivalAdditionalFare { getset; }  
  10.     }  
  11. }  
Model properties can be enhanced based on the future requirement and that can be used inside of algorithm classes.
 
Output

Below is the basic console output of the written code.
 
 
 
I have attached the zipped file of code that is in C# language.
 
Conclusion

Here, different types of fare calculation concepts like Station Fare, Age Fare, Festival Fare, Distance Fare, etc. are different in their behavior as their algorithms are different. So, all the algorithms have been isolated into separate classes in order to have the ability to select different algorithm at runtime. And, strategy design pattern is best for this type of case.