Abstract:
As the title specifies we will be Replacing the conditional if and Switch statement with the help of Polymorphism using DI container and talk about the benefits of doing the same in this article. Oops gives us pretty much great features which we are aware of theoretically but never implements practically. Polymorphism is one of the main features provided by Object-Oriented Languages which in turn implies that a Parent class can have more than one behavior and can point towards its child classes at runtime. If you want to learn more about OOPS features, you can go through below blog which talks about oops and other features.
Uncle Bob Oop design principle
There are tons and tons of article you can read from.
Problem statement:
I have been using dynamic Polymorphism where parent class can point towards child class and call the child method at the run time and solving the problem that way. But I found polymorphism is not only here where I can use it another side along which we are going to talk about i.e. replacing our Switch and if statement with the help of Polymorphism. I was aware of this concept when more technically sound people talk about this topic but the implementation is not so concrete and practical easily available on the internet showing the practical demo. So let’s try to solve this problem with practical use case and how we can avoid using Switch and if statement via Polymorphism.
“Let’s get started”
The concept I wish I was aware before or Someone would have told me to do this way. Fellas, it’s still in Production but I have not yet replaced it as Client requirements have been a freeze. So in one of our User Story, there was a requirement of saving FTP details of the customer and use this FTP details for File Processing etc. I am talking about this requirement on the overall focus on the FTP details part because there is where actual if else problem exists. So requirement says Client wants support for FTP and SFTP which logs in to the application and will save the FTP details and if there is no permission of reading and writing we should tell the user at that time only.
As a passionate developer, I started working on this story and created a simple use case and short diagram without using polymorphism allow him to save the FTP or SFTP details.
So I created an ENUM to handle the problem as shown below:
public enum EFtpTypes { FTP=1, SFTP=2 }
Where I have both the two supported FTP protocols.
I have my services function ready for these two protocols separately with the two separate interface.
So my IFTP interface contains the following method and other methods which I am assuming are not required for this article.
public interface IFTP { bool IsSFTPLoginFileCreationSuccessful(string host, string ftpPort, string ftpUsername, string ftpPassword, string folder); }
Now the implementation Class FTPManager to manage FTP stuff
public class FtpManager : IFTP { public bool IsFtpLoginFileCreationSuccessful(string host, string ftpUsername, string ftpPassword, string folder) { var folders = string.IsNullOrEmpty(folder) ? new string[] { "" } : folder.Split('/'); StringBuilder directoryMaker = new StringBuilder("/"); for (int i = 0; i = 1) directoryMaker.Append(folders[i - 1] + "/"); host = host + directoryMaker.ToString(); FtpWebRequest request; if (host.StartsWith("ftp") && !host.StartsWith("ftps")) { request = (FtpWebRequest)WebRequest.Create(new Uri(host)); } else { host = "ftp://" + host; request = (FtpWebRequest)WebRequest.Create(new Uri(host)); } request.Method = WebRequestMethods.Ftp.ListDirectory; request.Credentials = new NetworkCredential(ftpUsername, ftpPassword); request.KeepAlive = false; var response = (FtpWebResponse)request.GetResponse(); if (!(response.StatusCode == FtpStatusCode.CommandOK || response.StatusCode == FtpStatusCode.FileActionOK || response.StatusCode == FtpStatusCode.DataAlreadyOpen || response.StatusCode == FtpStatusCode.OpeningData)) return false; Stream responseStream = response.GetResponseStream(); StreamReader reader = new StreamReader(responseStream); string names = reader.ReadToEnd(); var Directories = names.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries).ToList(); if (Directories.Where(x => x.ToString().ToLower() == folders[i].Trim().ToLower()).Any()) { if (i == folders.Length - 1) { CreateFile(request, folders[i], ftpUsername, ftpPassword); directoryMaker.Append(folders[i]); host = host + folders[i]; Delete(request, folder, host, ftpUsername, ftpPassword); } } else { if (!string.IsNullOrEmpty(folder)) CreateDirectory(request, folders[i], host, ftpUsername, ftpPassword); if (i == folders.Length - 1) { CreateFile(request, folders[i], ftpUsername, ftpPassword); if (!string.IsNullOrEmpty(folder)) directoryMaker.Append(folders[i]); if (!string.IsNullOrEmpty(folder)) host = host + folders[i]; Delete(request, folder, host, ftpUsername, ftpPassword); } } } return true; } public bool CreateFile(WebRequest request, string folder, string userName, string password) { try { int buffLength = 2048; byte[] buff = new byte[buffLength]; byte[] bytes = Encoding.UTF8.GetBytes("Test Export"); request = (FtpWebRequest)FtpWebRequest.Create(new Uri(request.RequestUri + "/" + folder + "/" + "Sample.txt")); request.Credentials = new NetworkCredential(userName, password); request.Method = WebRequestMethods.Ftp.UploadFile; request.ContentLength = bytes.Length; Stream requestStream = request.GetRequestStream(); requestStream.Write(bytes, 0, bytes.Length); requestStream.Close(); FtpWebResponse response = (FtpWebResponse)request.GetResponse(); return true; } catch (Exception) { throw; } } private bool Delete(WebRequest request, string folder, string host, string userName, string password) { try { if (host.StartsWith("ftp") && !host.StartsWith("ftps")) { request = (FtpWebRequest)WebRequest.Create(new Uri(host + "//" + "Sample.txt")); } else { host = "ftp://" + host; request = (FtpWebRequest)WebRequest.Create(new Uri(host + "//" + "Sample.txt")); } request.Method = WebRequestMethods.Ftp.DeleteFile; request.Credentials = new NetworkCredential(userName, password); FtpWebResponse response = (FtpWebResponse)request.GetResponse(); return true; } catch (Exception ex) { throw; } } private bool CreateDirectory(WebRequest request, string folder, string Host, string userName, string password) { try { var getFolders = folder.Split('/'); StringBuilder directoriesToBeCreated = new StringBuilder("/"); for (int i = 0; i < getFolders.Length; i++) { directoriesToBeCreated.Append(getFolders[i] + "/"); if (Host.StartsWith("ftp") && !Host.StartsWith("ftps")) { request = (FtpWebRequest)WebRequest.Create(new Uri(Host + directoriesToBeCreated.ToString())); } else { Host = "ftp://" + Host; request = (FtpWebRequest)WebRequest.Create(new Uri(Host + directoriesToBeCreated.ToString())); } // request = WebRequest.Create(new Uri("ftp://" + Host + directoriesToBeCreated.ToString())); request.Credentials = new NetworkCredential(userName, password); request.Method = WebRequestMethods.Ftp.MakeDirectory; FtpWebResponse responses = (FtpWebResponse)request.GetResponse(); } return true; } catch (Exception ex) { throw ex; } }
So this API is totally responsible for Creating and Checking whether the user has rights to the FTP server or not.
Now my FTP service comes into the picture.
public class SftpManager: ISFTP { public bool IsSFTPLoginFileCreationSuccessful(string host, string ftpPort, string ftpUsername, string ftpPassword, string folder) { try { var folders = string.IsNullOrEmpty(folder) ? new string[] { "" } : folder.Split('/'); StringBuilder directoryMaker = new StringBuilder(""); int defaultSFtpPort = 115; int port = (Convert.ToInt32(ftpPort) == 0) ? defaultSFtpPort : Convert.ToInt32(ftpPort); using (var ftpClient = new SftpClient(host, port, ftpUsername, ftpPassword)) { ftpClient.Connect(); for (int i = 0; i x.ToString().ToLower() == folders[i].ToLower()).Any()) { directoryMaker.Append(folders[i] + "/"); if (i == folders.Length - 1) { UploadFileAndDeleteFromSFTPServer(directoryMaker, ftpClient); } } else { if (!string.IsNullOrEmpty(folder)) directoryMaker.Append(folders[i] + "/"); if (!string.IsNullOrEmpty(folder)) ftpClient.CreateDirectory(directoryMaker.ToString()); if (i == folders.Length - 1) { UploadFileAndDeleteFromSFTPServer(directoryMaker, ftpClient); } } } ftpClient.Disconnect(); } return true; } catch (Exception EX) { throw; } } private void UploadFileAndDeleteFromSFTPServer(StringBuilder directoryMaker, SftpClient ftpClient) { if (!string.IsNullOrEmpty(directoryMaker.ToString())) ftpClient.ChangeDirectory(directoryMaker.ToString()); using (Stream s = GenerateStreamFromString("TestFile")) { ftpClient.BufferSize = 4 * 1024; ftpClient.UploadFile(s, "Sample.txt"); } ftpClient.Delete("Sample.txt"); } private static Stream GenerateStreamFromString(string s) { MemoryStream stream = new MemoryStream(); StreamWriter writer = new StreamWriter(stream); writer.Write(s); writer.Flush(); stream.Position = 0; return stream; } }
I have below dummy UI which shows User Interface to enter FTP details:
It’s a simple UI which I have created for demonstration purpose. FTP Type here 1 means normal FTP and 2 stands for SFTP. Just for demonstration purpose, I have left this like 1,2 this should be in radio button in UI.
Controller code to save the FTP Details entered by User.
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using WebApplication1.Models; using WebInterfaces; namespace WebApplication1.Controllers { public class FileProcessingController : Controller { private readonly IFTP _ftpservice; private readonly ISFTP _sftpservice; public FileProcessingController(IFTP ftpservice, ISFTP sftpservice) { _ftpservice = ftpservice; _sftpservice = sftpservice; } // GET: FileProcessing public ActionResult Index() { return View("FTPDetails",new FtpDetailsViewModels()); } [HttpPost] public ActionResult UpdateSetting(FtpDetailsViewModels ftpDetails) { if (ModelState.IsValid) { try { if (ftpDetails.FtpType.Equals((int)EFtpTypes.FTP)) { if (!_ftpservice.IsFtpLoginFileCreationSuccessful(ftpDetails.FtpHost.Trim(), ftpDetails.FtpUserName, ftpDetails.FtpPassword.Trim(), ftpDetails.Directory)) ModelState.AddModelError("FTPDetail.FtpHost", "Invalid FTP Details"); } else if (ftpDetails.FtpType.Equals((int)EFtpTypes.SFTP)) { _sftpservice.IsSFTPLoginFileCreationSuccessful(ftpDetails.FtpHost.Trim(), ftpDetails.FtpPort.Trim(), ftpDetails.FtpUserName.Trim(), ftpDetails.FtpPassword.Trim(), ftpDetails.Directory.Trim()); } } catch (Exception ex) { ModelState.AddModelError("FTPDetail.FtpHost", ex.Message); } return View("FTPDetails", ftpDetails); } else { return View("FTPDetails", ftpDetails); } //save ftp details in db with the help of Repository } } }
I have used Constructor Injection DI to get all the services instance like FTPManager, SFTPManager which otherwise I would have to create based on selected ftp type by User.
So this design when I started was working fine and I was happy that I was done with this user story. But as we all know that
“The Only Thing That Is Constant Is Change -“
So client tested the User Story it worked great and does all the functionality and then they came up and said we want support for FTPS as FTP over SSL. One our client uses the FTPS. So I started it again and created one more enum with 3 which stands for FTPS and then there was one more else if condition in Controller for FTPS.
Problems:
- The Old code has to be changed again in order to handle new ftp protocol.
if (ftpDetails.FtpType.Equals((int)EFtpTypes.FTP)) { if (!_ftpservice.IsFtpLoginFileCreationSuccessful(ftpDetails.FtpHost.Trim(), ftpDetails.FtpUserName, ftpDetails.FtpPassword.Trim(), ftpDetails.Directory)) ModelState.AddModelError("FTPDetail.FtpHost", "Invalid FTP Details"); } else if (ftpDetails.FtpType.Equals((int)EFtpTypes.SFTP)) { _sftpservice.IsSFTPLoginFileCreationSuccessful(ftpDetails.FtpHost.Trim(), ftpDetails.FtpPort.Trim(), ftpDetails.FtpUserName.Trim(), ftpDetails.FtpPassword.Trim(), ftpDetails.Directory.Trim()); }
If a new transfer protocol is added, we have to add another else if statement to satisfy the requirement.
- The services and web project has to be deployed again for the new change.
- Prone to error because this change will force me to test FTP and SFTP which are totally independent of FTPS.
Solution: Replace If with Polymorphism Refactoring
So when I think of Polymorphism Dynamic Polymorphism comes into my mind i.e. parent class can call its child classes at run time i.e. parent class has more than one form. So with respect to our User story the Dynamic Polymorphism will look like as shown below:
So now I have an Abstract Class named FileTransferProtocol which has abstract method IsFtpLoginFileCreationSuccessful or other methods you want to other protocol to implement. Now let’s get started with the new design and learn how it helps us removing unnecessary if else conditions.
Abstract class:
public abstract class FileTransferProtocol:IProtocol { public abstract bool IsFtpLoginFileCreationSuccessful(string host, string ftpPort, string ftpUsername, string ftpPassword, string folder); }
Child Class which implement Network Protocol
New class FTPS that is to be supported now as per new change.
public class FTPS : FileTransferProtocol { private FtpClient _client; private int _ftpReadTimeout = 0; public object ConfigurationManager { get; private set; } private void Connect(string Username, string Password, string HostName) { if (string.IsNullOrEmpty(Username)) throw new ArgumentNullException(); if (string.IsNullOrEmpty(Password)) throw new ArgumentNullException(); if (string.IsNullOrEmpty(HostName)) throw new ArgumentNullException(); _client = new FtpClient(HostName); _client.Credentials = new NetworkCredential(Username, Password); _client.Connect(); } private void CreateSetDirectory(string DirectoryName) { if (string.IsNullOrEmpty(DirectoryName)) throw new ArgumentNullException(); _client.CreateDirectory(DirectoryName); _client.SetWorkingDirectory(DirectoryName); } private void Upload(byte[] data, string FileName) { _client.Upload(data, FileName); } public void CreateFile(string UserName, string Password, string HostName, string FileName, byte[] data, string Directory) { Connect(UserName, Password, HostName); if (!string.IsNullOrEmpty(Directory)) CreateSetDirectory(Directory); Upload(data, FileName); Disconnect(); } public void DeleteFile(string FileName) { _client.DeleteFile(FileName); } public string ReadFile(string UserName, string Password, string HostName, string FileName, string Directory) { Connect(UserName, Password, HostName); string filePath = Directory + "/" + FileName; var _ftpStream = _client.OpenRead(filePath); using (var reader = new StreamReader(_ftpStream)) { var Output = reader.ReadToEnd(); Disconnect(); return Output; } } private void Disconnect() { _client.Disconnect(); _client.Dispose(); } public override bool IsFtpLoginFileCreationSuccessful(string host, string ftpPort, string ftpUsername, string ftpPassword, string folder) { this.Connect(ftpUsername, ftpPassword, host); if (!string.IsNullOrEmpty(folder)) this.CreateSetDirectory(folder); byte[] bytes = Encoding.UTF8.GetBytes("Test"); this.Upload(bytes, "sample.txt"); this.DeleteFile("sample.txt"); this.Disconnect(); return true; } }
public class SFTP : FileTransferProtocol { public override bool IsFtpLoginFileCreationSuccessful( string host, string ftpPort, string ftpUsername, string ftpPassword, string folder) { try { var folders = string.IsNullOrEmpty(folder) ? new string[] { "" } : folder.Split('/'); StringBuilder directoryMaker = new StringBuilder(""); int defaultSFtpPort = 115; int port = (Convert.ToInt32(ftpPort) == 0) ? defaultSFtpPort : Convert.ToInt32(ftpPort); using (var ftpClient = new SftpClient(host, port, ftpUsername, ftpPassword)) { ftpClient.Connect(); for (int i = 0; i x.ToString().ToLower() == folders[i].ToLower()).Any()) { directoryMaker.Append(folders[i] + "/"); if (i == folders.Length - 1) { UploadFileAndDeleteFromSFTPServer(directoryMaker, ftpClient); } } else { if (!string.IsNullOrEmpty(folder)) directoryMaker.Append(folders[i] + "/"); if (!string.IsNullOrEmpty(folder)) ftpClient.CreateDirectory(directoryMaker.ToString()); if (i == folders.Length - 1) { UploadFileAndDeleteFromSFTPServer(directoryMaker, ftpClient); } } } ftpClient.Disconnect(); } return true; } catch (Exception EX) { throw; } } private void UploadFileAndDeleteFromSFTPServer(StringBuilder directoryMaker, SftpClient ftpClient) { if (!string.IsNullOrEmpty(directoryMaker.ToString())) ftpClient.ChangeDirectory(directoryMaker.ToString()); using (Stream s = GenerateStreamFromString("TestFile")) { ftpClient.BufferSize = 4 * 1024; ftpClient.UploadFile(s, "Sample.txt"); } ftpClient.Delete("Sample.txt"); } private static Stream GenerateStreamFromString(string s) { MemoryStream stream = new MemoryStream(); StreamWriter writer = new StreamWriter(stream); writer.Write(s); writer.Flush(); stream.Position = 0; return stream; } }
public class FTP : FileTransferProtocol { public override bool IsFtpLoginFileCreationSuccessful(string host,string ftpPort, string ftpUsername, string ftpPassword, string folder) { var folders = string.IsNullOrEmpty(folder) ? new string[] { "" } : folder.Split('/'); StringBuilder directoryMaker = new StringBuilder("/"); for (int i = 0; i = 1) directoryMaker.Append(folders[i - 1] + "/"); host = host + directoryMaker.ToString(); FtpWebRequest request; if (host.StartsWith("ftp") && !host.StartsWith("ftps")) { request = (FtpWebRequest)WebRequest.Create(new Uri(host)); } else { host = "ftp://" + host; request = (FtpWebRequest)WebRequest.Create(new Uri(host)); } request.Method = WebRequestMethods.Ftp.ListDirectory; request.Credentials = new NetworkCredential(ftpUsername, ftpPassword); request.KeepAlive = false; var response = (FtpWebResponse)request.GetResponse(); if (!(response.StatusCode == FtpStatusCode.CommandOK || response.StatusCode == FtpStatusCode.FileActionOK || response.StatusCode == FtpStatusCode.DataAlreadyOpen || response.StatusCode == FtpStatusCode.OpeningData)) return false; Stream responseStream = response.GetResponseStream(); StreamReader reader = new StreamReader(responseStream); string names = reader.ReadToEnd(); var Directories = names.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries).ToList(); if (Directories.Where(x => x.ToString().ToLower() == folders[i].Trim().ToLower()).Any()) { if (i == folders.Length - 1) { CreateFile(request, folders[i], ftpUsername, ftpPassword); directoryMaker.Append(folders[i]); host = host + folders[i]; Delete(request, folder, host, ftpUsername, ftpPassword); } } else { if (!string.IsNullOrEmpty(folder)) CreateDirectory(request, folders[i], host, ftpUsername, ftpPassword); if (i == folders.Length - 1) { CreateFile(request, folders[i], ftpUsername, ftpPassword); if (!string.IsNullOrEmpty(folder)) directoryMaker.Append(folders[i]); if (!string.IsNullOrEmpty(folder)) host = host + folders[i]; Delete(request, folder, host, ftpUsername, ftpPassword); } } } return true; } private bool CreateFile(WebRequest request, string folder, string userName, string password) { try { int buffLength = 2048; byte[] buff = new byte[buffLength]; byte[] bytes = Encoding.UTF8.GetBytes("Test Export"); request = (FtpWebRequest)FtpWebRequest.Create(new Uri(request.RequestUri + "/" + folder + "/" + "Sample.txt")); request.Credentials = new NetworkCredential(userName, password); request.Method = WebRequestMethods.Ftp.UploadFile; request.ContentLength = bytes.Length; Stream requestStream = request.GetRequestStream(); requestStream.Write(bytes, 0, bytes.Length); requestStream.Close(); FtpWebResponse response = (FtpWebResponse)request.GetResponse(); return true; } catch (Exception) { throw; } } private bool CreateDirectory(WebRequest request, string folder, string Host, string userName, string password) { try { var getFolders = folder.Split('/'); StringBuilder directoriesToBeCreated = new StringBuilder("/"); for (int i = 0; i < getFolders.Length; i++) { directoriesToBeCreated.Append(getFolders[i] + "/"); if (Host.StartsWith("ftp") && !Host.StartsWith("ftps")) { request = (FtpWebRequest)WebRequest.Create(new Uri(Host + directoriesToBeCreated.ToString())); } else { Host = "ftp://" + Host; request = (FtpWebRequest)WebRequest.Create(new Uri(Host + directoriesToBeCreated.ToString())); } // request = WebRequest.Create(new Uri("ftp://" + Host + directoriesToBeCreated.ToString())); request.Credentials = new NetworkCredential(userName, password); request.Method = WebRequestMethods.Ftp.MakeDirectory; FtpWebResponse responses = (FtpWebResponse)request.GetResponse(); } return true; } catch (Exception ex) { throw ex; } } private bool Delete(WebRequest request, string folder, string host, string userName, string password) { try { if (host.StartsWith("ftp") && !host.StartsWith("ftps")) { request = (FtpWebRequest)WebRequest.Create(new Uri(host + "//" + "Sample.txt")); } else { host = "ftp://" + host; request = (FtpWebRequest)WebRequest.Create(new Uri(host + "//" + "Sample.txt")); } request.Method = WebRequestMethods.Ftp.DeleteFile; request.Credentials = new NetworkCredential(userName, password); FtpWebResponse response = (FtpWebResponse)request.GetResponse(); return true; } catch (Exception ex) { throw; } } }
So now in future client comes and say we want another ftp to be supported we will create a new class and inherit our base class NetworkProtocol and implement the same to use in our application.
This is just the first phase of design. Now the important thing is pending that is getting the instance of a class based on selected Network protocol in UI by which we will be able to call correct Protocol and check if username and password are correct or not?
As I mentioned I will be using Dependency injection container to get the appropriate instance based on the type of Protocol. i.e. in DB, we have set protocol details as shown below:
ID | Protocol |
1 | FTP |
2 | SFTP |
3 | FTPS |
I will not be explaining Dependency injection thoroughly over here. But for Abstract purpose whenever we create an instance of class in our controller. Our controller is dependent on that class if anything on that class changes compiler has to go through all desired change and also have to recompile the controller due to change in Class itself. So in order to remove the use of new in our classes we inject the required instance via Interface constructor.
For IOC(inversion of control) container which responsible for giving me the instance I am using AutoFac. I will configure the above dependency as shown below in di configuration.
public static IContainer Build() { var builder = new ContainerBuilder(); builder.RegisterControllers(Assembly.GetExecutingAssembly()); //builder.RegisterType().As(); //builder.RegisterType().As(); builder.RegisterType().As().InstancePerLifetimeScope(); builder.RegisterType().Named("1").InstancePerLifetimeScope(); builder.RegisterType().Named("2").InstancePerLifetimeScope(); builder.RegisterType().Named("3").InstancePerLifetimeScope(); Container = builder.Build(); DependencyResolver.SetResolver(new AutofacDependencyResolver(Container)); Registry.Container = Container; return Container; }
So I have created named dependency with 1,2,3 which I will get in controller action method while saving this details and based on received ftp type I will get the required instance of Protocol by resolving the same with the help of DI Container. Let’s get started.
Now in my action method I will just resolve the instance by FTP type as simple as that.
[HttpPost] public ActionResult UpdateSetting(FtpDetailsViewModels ftpDetails) { if (ModelState.IsValid) { try { var reportSubject = _container.ResolveOptionalNamed(ftpDetails.FtpType.ToString()); var status= reportSubject.IsFtpLoginFileCreationSuccessful(ftpDetails.FtpHost.Trim(),ftpDetails.FtpPort, ftpDetails.FtpUserName, ftpDetails.FtpPassword.Trim(), ftpDetails.Directory); if(status) { saveDetails(); }else { ModelState.AddModelError("FTPDetail.FtpHost", "Invalid FTP Details"); ; } } catch (Exception ex) { ModelState.AddModelError("FTPDetail.FtpHost", ex.Message); } return View("FTPDetails", ftpDetails); } else { return View("FTPDetails", ftpDetails); } //save ftp details in db with the help of Repository }
Now when I save the detail in UI and save.
Now in my controller I can see the user entered ftp details in my Model.
Now we can see the FTPtype is 1 i.e. FTP which means user has selected FTP protocol. Now when we resolve the dependency let’s which instance we get?
We can clearly see the instance is pointing to FTP service which will call FTP service IsFtpLoginFileCreationSuccessful method to check ftp detail.
We are able to successfully check the rights and username and password for ftp protocol and same can be done for other protocol.
Now you can clearly see there is no if else statement it’s purely one liner to resolve the dependency. We have seen the power of Polymorphism to “Remove if with Polymorphism”
Conclusion:
Here we learned how easily we can get rid of our common if else condition and make it cleaner with the help of Polymorphism. With the help of DI, we are able decouple the controller from service layer.
In future if any more protocol we need to support we have to just create a new respective protocol service and add this in DI container to register it and we can just ready to go.
I Hope this article was helpful to understand RIP.