I have a console program where we have some fictitious CLI commands, for example` create, show, exit, assign and etc, like that
class Program
{
static void Main()
{
new TicketProcessor().Start();
}
}
public class TicketProcessor
{
private readonly Dictionary<int, Ticket> _tickets = new Dictionary<int, Ticket>();
private int _counter = 0;
public void Start()
{
Console.WriteLine("\n\nSUPPORT CLI");
while (true)
{
Console.WriteLine("\n\nAvailable commands:");
Console.WriteLine("create %title% - create a new ticket");
Console.WriteLine("show %ticket id% - show the ticket");
Console.WriteLine("comment %ticket id% %comment% - add a comment to the ticket");
Console.WriteLine("assign %ticket id% %user name% - assign the ticket");
Console.WriteLine("close %ticket id% - close the ticket");
Console.WriteLine("list- show all tickets");
Console.WriteLine("q - exit");
Console.WriteLine();
var input = Console.ReadLine();
if (input.Equals("q")) break;
if (input.Equals("list"))
{
foreach (var ticket in _tickets)
{
Console.WriteLine($"{ticket.Value.Id} | {ticket.Value.Title}");
}
continue;
}
if (input.StartsWith("create "))
{
_counter++;
_tickets[_counter] = new Ticket
{
Id = _counter,
Title = input.Substring(7, input.Length - 7),
CurrentState = Ticket.State.Open,
Comments = new List<string>()
};
Console.WriteLine($"{_counter} has been created ");
continue;
}
if (input.StartsWith("show "))
{
var id = int.Parse(input.Split(' ')[1]);
_tickets[id].Show();
continue;
}
if (input.StartsWith("comment "))
{
var id = int.Parse(input.Split(' ')[1]);
var commentPrefix = 8 + input.Split(' ')[1].Length + 1;
var comment = input.Substring(commentPrefix, input.Length - commentPrefix);
var ticket = _tickets[id];
ticket.Comments.Add(comment);
ticket.CommentsCount++;
Console.WriteLine($"new comment has been added into {id}");
continue;
}
if (input.StartsWith("assign "))
{
var id = int.Parse(input.Split(' ')[1]);
var usernamePrefix = 7 + input.Split(' ')[1].Length + 1;
var username = input.Substring(usernamePrefix, input.Length - usernamePrefix);
var ticket = _tickets[id];
ticket.Comments.Add("assigned " + username + " " + DateTime.UtcNow);
ticket.CommentsCount++;
ticket.AssignedToUser = username;
ticket.CurrentState = Ticket.State.InProgress;
Console.WriteLine($"{id} has been assigned to {username}");
continue;
}
if (input.StartsWith("close "))
{
var id = int.Parse(input.Split(' ')[1]);
var ticket = _tickets[id];
ticket.CurrentState = Ticket.State.Closed;
ticket.Comments.Add("closed " + DateTime.UtcNow);
ticket.CommentsCount++;
Console.WriteLine($"{id} has been closed ");
}
}
}
}
This is not clear code and clean architecture as well.
I try to change this into clear architecture. Firstly, I created CommandHandler class and extracted if blocks into methods in , and singled out TicketProcessor Class from Program Class like this.
public class TicketProcessor
{
private readonly Func<string> _inputProvider;
private readonly Action<string> _outputProvider;
private readonly CommandHandler _commandHandler;
public TicketProcessor(Func<string> inputProvider, Action<string> outputProvider, CommandHandler commandHandler)
{
_inputProvider = inputProvider;
_outputProvider = outputProvider;
_commandHandler = commandHandler;
}
public void Start()
{
_outputProvider("\n\nSUPPORT CLI");
_outputProvider($"\n\nAvailable commands:\n{ string.Join("\n", _commandHandler.GetAvailableCommands())}");
Console.WriteLine();
var input = _inputProvider() ?? string.Empty;
if (!_commandHandler.GetAvailableCommands().Contains(input))
{
_outputProvider("there is no such command");
return;
}
_commandHandler.GetCommand(input);
Console.WriteLine();
}
}
The CommandHandler class is like so.
public class CommandHandler
{
private readonly Dictionary<int, Ticket> _tickets = new Dictionary<int, Ticket>();
private readonly Dictionary<string, Action<string>> _commands;
private readonly Func<string> _inputProvider;
private readonly Action<string> _outputProvider;
private int _counter = 0;
public CommandHandler(Func<string> inputProvider, Action<string> outputProvider)
{
_inputProvider = inputProvider;
_outputProvider = outputProvider;
_commands = new Dictionary<string, Action<string>>()
{
{"create %title% - create a new ticket", Create },
{"show %ticket id% - show the ticket", Show },
{"comment %ticket id% %comment% - add a comment to the ticket", Comment },
{"assign %ticket id% %user name% - assign the ticket", Assign },
{"close %ticket id% - close the ticket", Close },
{"list- show all tickets", List },
{"q - exit", Exit }
};
}
public void GetCommand(string commandName)
{
_commands[commandName](commandName);
}
public IEnumerable<string> GetAvailableCommands()
{
return _commands.Keys;
}
private void Exit(string input)
{
if (input.Equals("q"))
{
Environment.Exit(0);
}
}
private void List(string input)
{
if (input.Equals("list"))
{
foreach (var ticket in _tickets)
{
_outputProvider($"{ticket.Value.Id} | {ticket.Value.Title}");
}
return;
}
}
private void Close(string input)
{
if (input.StartsWith("close "))
{
var id = int.Parse(input.Split(' ')[1]);
var ticket = _tickets[id];
ticket.CurrentState = Ticket.State.Closed;
ticket.Comments.Add("closed " + DateTime.UtcNow);
ticket.CommentsCount++;
Console.WriteLine($"{id} has been closed ");
}
}
private void Assign(string input)
{
if (input.StartsWith("assign "))
{
var id = int.Parse(input.Split(' ')[1]);
var usernamePrefix = 7 + input.Split(' ')[1].Length + 1;
var username = input.Substring(usernamePrefix, input.Length - usernamePrefix);
var ticket = _tickets[id];
ticket.Comments.Add("assigned " + username + " " + DateTime.UtcNow);
ticket.CommentsCount++;
ticket.AssignedToUser = username;
ticket.CurrentState = Ticket.State.InProgress;
_outputProvider($"{id} has been assigned to {username}");
return;
}
}
private void Comment(string input)
{
if (input.StartsWith("comment "))
{
var id = int.Parse(input.Split(' ')[1]);
var commentPrefix = 8 + input.Split(' ')[1].Length + 1;
var comment = input.Substring(commentPrefix, input.Length - commentPrefix);
var ticket = _tickets[id];
ticket.Comments.Add(comment);
ticket.CommentsCount++;
_outputProvider($"new comment has been added into {id}");
return;
}
}
private void Show(string input)
{
if (input.StartsWith("show "))
{
var id = int.Parse(input.Split(' ')[1]);
_tickets[id].Show();
return;
}
}
private void Create(string input)
{
if (input.StartsWith("create "))
{
_counter++;
_tickets[_counter] = new Ticket
{
Id = _counter,
Title = input.Substring(7, input.Length - 7),
CurrentState = Ticket.State.Open,
Comments = new List<string>()
};
_outputProvider($"{_counter} has been created ");
return;
}
}
}
Program class looks like so.
class Program
{
static void Main()
{
var command = new CommandHandler(Console.ReadLine, Console.WriteLine);
var ticket = new TicketProcessor(Console.ReadLine, Console.WriteLine, command);
while (true)
{
ticket.Start();
}
}
}
The problem is that when I type create title, or show 1 (1 is ticket id for example), assigne and other commands in the console, the methods will not be called because in _commands dictionary, the key's are specific strings.
What else to use instead of a dictionary to solve this problem?
PS. Ticket class is not attached.
Thanks in advance.