We all know them SOLID principles. Every developer knows what are SOLID principles because every developer has to go through an interview process to get a job. Some can explain really good while interview.
Recently, I have been interviewed by a very reputed financial company. They have 4 rounds of interview and almost 3 round has asked a question about SOLID principles. They all have asked very good interesting questions and it seems they were looking for some specific expertise. I have started working there and their code base is awesome. They are not working anywhere near what they asked in an interview. I also found some controllers code files has 2-3k lines of code which is a presentation layer. Though they have very nice onion architecture for the project. I hope whoever worked there all gone through those interview process and know about at least SOLID principles. These principles are not just to perform in the interview. We need to apply it in real code. Maybe they have their own reasons for not following them.
Anyway, here I wanted to show some example which I saw in their code base which can very easily make it closed for modification.
Check following code.
public void Work(string switchoption)
{
switch (switchoption)
{
case "Fun1":
///
/// So many lines of code
///
break;
case "Fun2":
///
/// So many lines of code
///
break;
case "Fun3":
///
/// So many lines of code
///
break;
}
}
If you come across this kind of code or you may end up writing this kind of code then what can be at least done, I have explained it here. Above code is not closed because you don't know what kind of another case may have to add in future. What if there is a change in code for a particular switch case. You have to test all cases because you have changed it the function. If your unit test covered all switch cases then you are a little bit good position. QA has to check every case manually. If something missed in testing then you may end up in production bugs. Overall all these increases the maintenance cost.
I do refactor it to the following levels.
At least have a private function for long lines of code for each case.
public void Work(string switchoption)
{
switch (switchoption)
{
case "Fun1":
function1();
break;
case "Fun2":
function2();
break;
case "Fun3":
function3();
break;
}
}
{
Console.WriteLine("Function 3");
}
private void function2()
{
Console.WriteLine("Function 2");
}
private void function1()
{
Console.WriteLine("Function 1");
}
If you create private function then the code will at least increase readability. By giving a proper name to function, it describes that what that piece of code is doing. But still, it has an issue of what if we may need to write more cases there. This code is still not a closed code.
Instead of Switch case, you can implement Dictionary.
public void Work2(string switchoption)
{
Dictionary<string, Action> switchfunction = new Dictionary<string, Action>();
switchfunction.Add("Fun1", function1);
switchfunction.Add("Fun2", function2);
switchfunction.Add("Fun3", function3);
switchfunction[switchoption].Invoke();
}
You can simply add a dictionary with string and Action type. Add all functions to dictionary collection. Dictionary can pick a particular case by Key name and execute the function by invoking it. You can also generate this dictionary list from outside of this function or get dictionary values from any data store. In case in future, you may need to add more cases then you don't have to change this function. This way your existing function Work2 here in the example can be closed for modification.
This implementation has resolved our problem of closing the function "Work" for modification. But we still have to add a new function to the class for adding new cases. It is also not following Single responsibility principle.
Implement Strategy pattern using Dependency Injection
Basic strategy pattern implementation explained here. http://www.dofactory.com/net/strategy-design-patternThe basic concept of pattern defines an Interface with strategy and have the concrete classes with different strategies implementation. Based on your need you can create an object of particular strategy dynamically. google it for "strategy design pattern" and you will get plenty of examples.
Here I wanted to show an example using dependency framework. In above-given URL of dofactory example of strategy pattern, has context class to create an object of different strategies. We can rely on dependency injection framework for that responsibility.
In the following code, IExampleStrategy is an interface and has functionexample(). This interface implemented in three different ExampleStrategy classes. each class has different implementation of functionexample.
public interface IExampleStrategy{
void functionexample();
}
public class ExampleStrategy1 : IExampleStrategy
{
public void functionexample()
{
Console.WriteLine("ExampleStrategy1.functionexample() executed.");
}
}
public class ExampleStrategy2 : IExampleStrategy
{
public void functionexample()
{
Console.WriteLine("ExampleStrategy2.functionexample() executed.");
}
}
public class ExampleStrategy3 : IExampleStrategy
{
public void functionexample()
{
Console.WriteLine("ExampleStrategy3.functionexample() executed.");
}
}
Following is the context class where we instantiate the class based on strategy. In the following class, we have injected IUnityContainer. It resolves right Strategy class based on a parameter.
The Work function is the function that we are changing to our main problem of removing switch cases. The first Work function can pass parameter and decide what strategy need to execute. The container can get a right concrete class based on a parameter passed. Instantiating an object is a responsibility of container based on a parameter.
Another Work function also does the same thing but in some cases, the developer has to do things in a loop. The container can take care of creating an object and also can take care of lifetime of your object. So in case if you have to create the same object multiple times for our second work function then a container can reuse the same object created previously.
public class CloseCodeExample2{
private readonly IUnityContainer _container;
public CloseCodeExample2(IUnityContainer container)
{
_container = container;
}
public void Work(string switchcall)
{
var examplefunction= _container.Resolve<IExampleStrategy>(switchcall);
examplefunction.functionexample();
}
public void Work(List<string> switchcalls)
{
foreach (var item in switchcalls)
{
var examplefunction = _container.Resolve<IExampleStrategy>(item);
examplefunction.functionexample();
}
}
}
Here is our Main function where we have defined our Unity container registration for an interface and its implementation.
class Program{
static void Main(string[] args)
{
CloseCodeExample codeExample = new CloseCodeExample();
codeExample.Work("Fun1");
codeExample.Work2("Fun2");
codeExample.Work2("Fun3");
//Unity container registration.
var container = new UnityContainer();
container.RegisterType<IExampleStrategy, ExampleStrategy1>("Fun1");
container.RegisterType<IExampleStrategy, ExampleStrategy2>("Fun2");
container.RegisterType<IExampleStrategy, ExampleStrategy3>("Fun3");
CloseCodeExample2 codeExample2 = new CloseCodeExample2(container);
codeExample2.Work("Fun1");
codeExample2.Work("Fun2");
Console.WriteLine("---------------------------------");
codeExample2.Work(new List<string>() { "Fun2", "Fun3", "Fun1" });
Console.ReadKey();
}
}
Let's validate our two SOLID principles that we wanted to implement. In above code, If you want to add new case then you need to add another class which implements the same interface and just need to register for the container. We don't need to change function "Work" It is basically closed for modification. Also, by creating a separate class for each case, We have implemented single responsibility principle. If you implement Unit test then it would be a separate unit test for all classes. Easy to implement the unit test.
Note: In above example, you still have to consider little bit defensive code, for example, what if no any class implementation available to unity config. It will throw an exception of "Unity.Exceptions.ResolutionFailedException". So you still have to take care of this kind of things.
I hope you like it. If you have any query, add it to comments.
You can find code sample of above code here at GitHub. https://github.com/atulpatel/AspCoreRnd
We are the one stop solution to all your glitches in the Quickbooks Payroll Support Phone Number 1-800-986-4607. We are Providing immediate & effective response to the user. Our team constitutes of various qualities.Our team is very friendly & polite. So, whatever the issue you are preoccupied.
ReplyDeleteQuickbooks Payroll is really an outstanding product that comes into existence to cater to the needs of small as well as medium-size business. If ypu are facing any technical support dial Quickbooks Payroll Support Phone Number 1-800-986-4607.
ReplyDeleteThe team at QuickBooks Support +1-844-233-5335 constitutes of great professionals who have accumulated many years of experience in handling technical grievances of QuickBooks. Their dedication and hard work help us to develop the most optimal solutions for QuickBooks errors that we provide to our customers all over the world. Read more- https://tinyurl.com/yxn4tq6d & visit us- https://www.enetserves.com
ReplyDeleteNice Blog ! Do you need instant help to fix your QuickBooks issues? If yes, we are here to help you. Dial our QuickBooks Toll Free Phone Number +1-800-986-4607 now.our team have are 24*7 availability & very Experienced.
ReplyDeleteView on Map: https://tinyurl.com/yyah7zez.
Whenever you face any technical glitches, you can call us at our QuickBooks Support Phone Number 1(800)986-4591 We will deliver you the finest solutions. Do not go here and there as we are here to assist you in every possible manner. Connect with us now!. For More Visit: http://www.santrasolutions.com/quickbooks-support-phone-number/
ReplyDeletehttps://tinyurl.com/y569xkcy
Nice Blog ! Let our support team solve your issues. No matter how complex the problem is, we are always here to support you. Right from the installation process to resolving any complex error. Dial our QuickBooks Customer Service Number 1-800-986-4607.
ReplyDeleteView on Map: https://tinyurl.com/vq37tnv
Nice & informative Blog ! We are here to solve your QuickBooks issues efficiently. QuickBooks versions for which we provide support are:
ReplyDelete• QuickBooks Premier
• QuickBooks Pro
• QuickBooks Enterprise
• QuickBooks Mac
• QuickBooks Payroll
Just connect with our QuickBooks Payroll Support Phone Number 855-907-0406 accounting professionals and fix your issues in seconds!
Excellent and very informative blog click here
ReplyDeleteQuickbooks Proadvisor Support Phone Number and for more detail dial on 844-908-0801
QuickBooks For Mac Support Phone Number
ReplyDeleteQuickBooks Support Phone Number Seattle
QuickBooks Support Phone Number California
quickBooks Support Phone Number Pennsylvania
QuickBooks Support Phone Number Texas
Nice Blog ! Do you Need instant help fixing problems with your QuickBooks? Now dial our QuickBooks Payroll Support Phone Number 855 -9O7-O4O6! We have technical experts who can instantly fix your problems.
ReplyDeleteNice & Informative Blog ! We are the one-stop solution to all your issues & query, if facing issues in QuickBooks. Get valuable aid with QuickBooks Customer Helpline Number 855-9O7-O4O6.
ReplyDeleteLooking for expert advice? Contact QuickBooks Support Phone Number 1-833-780-0086 to get assistance for error related issues. For More: https://g.page/qb-support-oregon
ReplyDeleteLooking for expert advice? Contact QuickBooks Customer Service Number 1-833-780-0086 to get assistance for error related issues. For More: https://g.page/quickbookssupporttexas
ReplyDeleteFacing issues in QuickBooks? Dial QuickBooks Customer Support Number & get effective solutions from our QuickBooks experts. Our support team consists of a large panel of experts who give reliable aid to the users. For More: https://g.page/quickbookssupporttexas
ReplyDelete