I am currently developing an application in which there are several roles.
For a feature, I need to return information according to the role. Thus, I created a switch on the role enumeration for set it up.
public Enum Role {
admin = 1,
agent = 2,
responsible = 3};
switch(value){
case Role.admin :
//Do something
break;
case Role.agent :
//Do something
break;
case Role.responsible :
//Do something
break}
I read in the clean code book that we should avoid having switch in our code.
So, for my case what is solution to avoid this switch.
CodePudding user response:
Such switches are usually bad in every architecture. Instead of switching on enum, use polymorphism:
interface IRole
{
void Do();
}
class Admin: IRole
{
override void Do()
{
//do the admin thing
}
}
class User : IRole
{
override void Do()
{
//do the user thing
}
}
Etc. This will allow you to separate functionalities today and easily add more roles in the future.
CodePudding user response:
Hopefully, your clean code book explains why you should avoid using a switch
. If your enum is a small set and you have only one place where you will handle such a check, abstracting it away will most likely be overengineering and make it less readable.
You can make the code cleaner by applying SOLID principles, which would most likely lead to a GOF pattern. GOF patterns will generally help you avoid large if/else
chains and bloated switch
statements.
How you make this code cleaner would depend on the feature you are writing. A pattern using handlers of sorts will most likely fit the scenario. I will present you with a solution using a strategy pattern. Let's say this switch is for a UI handler of sorts, and you want to colour the text to a table showing users with these roles.
Lets say the role belongs to a user:
public class UserEntry
{
public Role UserRole {get;set;}
}
And you want to colour the role or user entry according to a selected theme:
public interface ITheme
{
string GetColour(UserEntry entry);
}
public class DarkTheme : Theme
{
public DarkTheme() : base(new[]
{
new AdminColourizer("darkblue"),
new AgentColourizer("darkgreen"),
new ResponsibleColourizer("darkred")})
{}
}
public class LightTheme : Theme
{
public LightTheme() : base(new[]
{
new AdminColourizer("lightblue"),
new AgentColourizer("lightgreen"),
new ResponsibleColourizer("lightred")})
{}
}
public abstract class Theme : ITheme
{
public Theme(IEnumerable[] colourizers)
{
_colourizers = colourizers;
}
private IEnumerable<Colourizer> _colourizers
public string GetColour(UserEntry entry)
{
return _colourizers.SingleOrDefault(c => c.Colour(entry));
}
}
Then we break the switch into handlers
public abstract class Colourizer
{
public abstract class Colourizer(string colour)
{
this.colour = colour;
}
protected string colour;
public abstract string Colour(UserEntry entry);
}
public class AdminColourizer : Colourizer
{
AdminColourizer(string colour) : base(colour) {}
public override string Colour(UserEntry entry)
{
if (entry.UserRole == Role.Admin)
return colour;
return null;
}
}
public class ResponsibleColourizer : Colourizer
{
ResponsibleColourizer(string colour) : base(colour) {}
public override string Colour(UserEntry entry)
{
if (entry.UserRole == Role.Responsible)
return colour;
return null;
}
}
public class AgentColourizer : Colourizer
{
AgentColourizer(string colour) : base(colour) {}
public override string Colour(UserEntry entry)
{
if (entry.UserRole == Role.Agent)
return colour;
return null;
}
}
And we can use the handlers in the Theme strategy like so:
public class UserTableRenderer
{
public UserTableRenderer(ITheme theme)
{
_theme = theme;
}
private ITheme _theme;
public void RenderTable(IRepository respository)
{
var users = repository.GetUsers(); // list of UserEntry
foreach (var user in users)
{
WriteText(entry: user, colour: _theme.GetColour(user));
}
}
public void Colour(UserEntry entry)
{
}
public class MyApp()
{
var repo = new MyDatabaseRepo();
var renderer = new UserTableRenderer(new DarkTheme() /* or LightTheme() */);
renderer.RenderTable(repo);
}
In this code, the switch statement is abstracted into the handlers called Colourizers in this case. This approach emphasizes the S (Single-responsibility) principle in SOLID as each handler applies logic concerning a single concern (i.e. role is admin -> do x).
Any modifications or extensions of handling admin will only involve a programmer meddling with the AdminColourizer and may not even encounter the other avenues of the switch statement.