Command

Command is a single unit of work, a task or a job. Each command implements the interface IMcmCommand.

    public interface IMcmCommand
    {
        Exception Error { get; }
        bool HasError { get; }

        bool Execute();
        event EventHandler ExecuteCompleted;
    }
You can derive your own commands from the generic class Command<TArgument, TResult>. In such case you need only to define the argument and result types and implement the abstract method ExecuteCore(). Error handling is already implemented in the base Command class, so try/catch block is not necessary.

    public abstract class Command<TArgument, TResult> : IMcmCommand
    {
        protected Command() {...}
        protected Command(TArgument argument) {...}

        public TArgument Argument {...}
        public TResult Result {...}

        public Exception Error {...}
        public bool HasError {...}

        public bool Execute() { try... ExecuteCore(); ... catch}
        public event EventHandler ExecuteCompleted;

        protected abstract void ExecuteCore();
    }

Unit Tests

The command should not access external services and components. The whole environment required to run the ExecuteCore() method should be defined in the command argument. Due to this requirement it's possible to test commands using unit tests.

Naming

Each command should have the suffix Command (eg. DoJobCommand). Command argument and result types should be named by the same pattern as the command with additional suffixes Argument or Result (eg. DoJobCommandArgument, DoJobCommandResult).

Example

public class LoginCommand : Command<LoginCommandArgument, LoginCommandResult>
{
    public LoginCommand(LoginCommandArgument argument)
        : base(argument)
    {
    }

    protected override void ExecuteCore()
    {
        // simulating a long lasting operation...
        Thread.Sleep(2000);

        if (string.IsNullOrEmpty(Argument.Name))
        {
            try/catch is implemented in the base Command class
            throw new InvalidOperationException("Please enter the name.");
        }

        // Current time is taken using the ITimeProvier, not DateTime.Now
        // For unit tests the TimeProvider can be replaced with a mock.
        Result = new LoginCommandResult(Argument.TimeProvider.Now, string.Format("Hello {0}", Argument.Name));
    }
}

Last edited Jan 24, 2013 at 9:04 PM by polo, version 4

Comments

No comments yet.