HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpModerate

Design patterns for console commands

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
commandsdesignpatternsforconsole

Problem

I've made a pretty standard console in which you type a command and it does something. However, the issue that comes to my mind is scaling: if I want to add hundreds of commands, I have to manually add a new Command instance for each one individually, which is... less than ideal.

The full code is stored in this GitHub repository.

Program.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ConsolePlus.Commands;

namespace ConsolePlus
{
    public class Program
    {
        /// 
        /// The version of the program.
        /// 
        public const string Version = "1.0.0.0";

        public static CommandRegistry Registry = new CommandRegistry();

        /// 
        /// The application's entry point.
        /// 
        /// The command-line arguments passed to the program.
        public static void Main(string[] args)
        {
            CommandHandler.RegisterCommands();

            Console.WriteLine("ConsolePlus v." + Version);
            while (true)
            {
                Console.Write(">>> ");
                string line = Console.ReadLine();
                List parts = line.Split(' ').ToList();
                string commandName = parts[0];
                parts.RemoveAt(0);
                string[] commandArgs = parts.ToArray();

                try
                {
                    string result = Registry.Execute(commandName, commandArgs);
                    if (result != null)
                    {
                        Console.WriteLine("[{0}] {1}", commandName, result);
                    }
                }
                catch (CommandNotFoundException)
                {
                    Console.WriteLine("[ConsolePlus] No such command.");
                }
            }
        }
    }
}


CommandRegistry.cs

```
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace

Solution

Why do you have the Command class? Why not make life much simpler for yourself by making AllCommands a List, and having classes like ClearCommand implement ICommand?

You can then query your assembly file for classes where ICommand is implemented; that way you don't even need to fill AllCommands "by hand":

AllCommands = Assembly.GetExecutingAssembly().GetTypes()
    .Where(x => x.GetInterfaces().Contains(typeof(ICommand))
                && x.GetConstructor(Type.EmptyTypes) != null)
    .Select(x => Activator.CreateInstance(x) as ICommand);


Also, does AllCommands need to be called AllCommands? Can't it just be called Commands?

I would also recommend to remove the setters from the properties in ICommand. That way you'd end up with something like this:

public interface ICommand
{
    string Name { get; }

    string HelpText { get; }

    bool IsPrivileged { get; }

    string Execute(string[] args);
}

public class ClearCommand : ICommand
{
    public string Name { get { return "clear"; } }

    public string HelpText { get { return "Clears the console screen."; } }

    public bool IsPrivileged { get { return false; } }

    public string Execute(string[] args)
    {
        Console.Clear();
        return null;
    }
}

Code Snippets

AllCommands = Assembly.GetExecutingAssembly().GetTypes()
    .Where(x => x.GetInterfaces().Contains(typeof(ICommand))
                && x.GetConstructor(Type.EmptyTypes) != null)
    .Select(x => Activator.CreateInstance(x) as ICommand);
public interface ICommand
{
    string Name { get; }

    string HelpText { get; }

    bool IsPrivileged { get; }

    string Execute(string[] args);
}

public class ClearCommand : ICommand
{
    public string Name { get { return "clear"; } }

    public string HelpText { get { return "Clears the console screen."; } }

    public bool IsPrivileged { get { return false; } }

    public string Execute(string[] args)
    {
        Console.Clear();
        return null;
    }
}

Context

StackExchange Code Review Q#105675, answer score: 17

Revisions (0)

No revisions yet.