Your cyclomatic complexity is high because of the switch
statement! We can get rid of it using two solutions. The first one would be inheritance of command builders. But that's overkill, you don't want to have 3 classes for 3 builders that simply returns a string
. But maybe you'd accept having 3 methods for 3 builders. (As long as you don't have too many ways to build an URL, that'd be good!)
I used some C#6 features but it's nothing can be changed a little to work with other versions of C#!
First, as @BCdotWEB wrote, you should use Format
, which I did using String interpolation in C#6. (You can use String.Format
if you can't use C#6!)
I created a test class to show you how I'd do it.
public class Test
{
private readonly Dictionary<CommandType, Func<String>> commandBuilders;
//Properties that you already have : IpAddress,Username,Password,Ports
public Test()
{
commandBuilders = new Dictionary<CommandType, Func<String>>();
commandBuilders.Add(CommandType.SetPower, BuildSetPower);
commandBuilders.Add(CommandType.ReadPower, BuildReadPower);
commandBuilders.Add(CommandType.ReadCurrent, BuildReadCurrent);
}
private string GetUrlBaseFormat() => $"http://{IpAddress}/set.cmd?user={Username}+pass={Password}+";
private string BuildSetPower()
{
var urlBuilder = new StringBuilder(GetUrlBaseFormat());
urlBuilder.Append("cmd=setpower+");
for (int i = 0; i < (Ports.Length-1); i++)
{
urlBuilder.Append(BooleanToStringPortRemap(Ports[i], i + 1) + "+");
}
urlBuilder.Append(BooleanToStringPortRemap(Ports[Ports.Length - 1], Ports.Length));
return urlBuilder.ToString();
}
private string BuildReadPower() => $"{GetUrlBaseFormat()}cmd=getpower";
private string BuildReadCurrent() => $"{GetUrlBaseFormat()}cmd=getcurrent";
internal String GenerateUrl()
{
Func<string> urlBuilder;
bool builderExists = commandBuilders.TryGetValue(Command, out urlBuilder);
if(!builderExists)
{
throw new ArgumentException("Command type has not been set");
}
return urlBuilder();
}
}
That's a simple example I made that could work out well in your code. We use a Dictionary to hold which Func<String>
to call when given a certain Command
.
Now, each way to build an URL has its own method, most of them are one-liners.
This way, you don't have the switch
in your code, and if you happen to have more ways to build URL your switch wouldn't grow in complexity! (But you'd have more methods, solution incoming!)
You could argue that I'm just moving the problem. You don't have an high complexity now, but you have more methods in your class. You could move these methods to a static class
that would help to build URLs. And if that ever becomes a problem, you could use inheritance to have multiple instance of.. IUrlBuilder
. These are the solutions to deal with your high cyclomatic complexity.
Finally, as said @Heslacher, if we knew what is inside the BooleanToStringPortRemap
method, we could rework it to use something like String.Join
instead of using a for
loop to join your ports.
+
as a separator? The only allowed ones are;
and&
as far as I know.+
is a url encoded space. \$\endgroup\$