0

I'm trying to implement the command pattern, so it works like this:

  • There is a base class Command<T>, from which all other commands inherit.
  • A command can be executed on a class deriving from an Actor.
  • Each interface defines an action an Actor can perform. E.g. IMovable or ITurnable.
  • Each command class acts on a specific interface. E.g. Command<IMovable>.
  • Actors implement interfaces, which define what actions they can perform.

Here is how I tried implementing it:

// base interface for all action interfaces
public interface ICommandable { }

// interface for a move action
public interface IMovable : ICommandable
{
    void Move();
}

// base actor class
public class Actor : ICommandable
{
    public void ExecuteCommand(ICommand<ICommandable> command)
    {
        (command as Command<ICommandable>).Execute(this);
    }
}

// an actor which can be moved
public class MovableActor: Actor, IMovable
{
    public void Move()
    {
        Console.WriteLine("Move");
    }
}

// an interface for commands
public interface ICommand<out T> { }

// base command class
public class Command<T> : ICommand<T> where T : ICommandable
{
    public virtual void Execute(T robot) { }
}

// move command
public class MoveCommand : Command<IMovable>
{
    public override void Execute(IMovable actor)
    {
        actor.Move();
    }
}

This is an example of what I'm trying to do:

MovableActor actor = new MovableActor();
Command<IMovable> cmd = new MoveCommand();
actor.ExecuteCommand(cmd);

The issue with my implementation is that the ICommand interface has to use the out keyword for its parameter. I did some reading and I understand why that is, but I don't know how to achieve what I described. How can I implement this?

If this is not possible, how should I change my description, so it's as close to this one as possible, but works?

8
  • I don't understand why you need ICommand at all. If I remove it, your code still compiles fine. It also compiles fine if I remove the out Commented Aug 6, 2021 at 13:30
  • @canton7 The code compiles fine, but when I try to create a MoveCommand and pass it to ExecuteCommand() of the MovableActor, it doesn't work. Commented Aug 6, 2021 at 13:45
  • If would be helpful if you included that code in your question Commented Aug 6, 2021 at 13:49
  • The basic problem here is that your MoveCommand.Execute must be given an IMovable -- that method depends on having an IMovable. However, you're allowed to pass anything to Actor.ExecuteCommand, even a command such as an ICommand<ITurntable>: there's nothing stopping you doing this. But if you did that, and called MoveCommand.ExecuteCommand with an ICommand<ITurntable>, MoveCommand.Execute would fail, because it wants an IMovable. Commented Aug 6, 2021 at 13:53
  • 1
    @canton7 Thanks, that's exactly what I was looking for. If you want, add it as an answer so I can accept it. Commented Aug 6, 2021 at 14:16

1 Answer 1

1

The basic problem here is that your MoveCommand.Execute must be given an IMovable -- that method depends on having an IMovable. However, you're allowed to pass anything to Actor.ExecuteCommand, even a command such as an ICommand<ITurntable>: there's nothing stopping you doing this.

But if you did that, and called MoveCommand.ExecuteCommand with an ICommand<ITurntable>, MoveCommand.Execute would fail, because it wants an IMovable.

Your basic options are:

  1. Either you don't let actors invoke commands on themselves: invoke the command directly on the actor, or have a separate CommandInvoker take both the action and the command
  2. Prepare for the possibility that someone might send a command to an actor which can't accept that, and check for that and deal with it at runtime. Then it's just Actor.ExecuteCommand(ICommand command), and you do a runtime check to see whether it's the right sort of command.

That would look something like:

// base interface for all action interfaces
public interface ICommandable { }

// interface for a move action
public interface IMovable : ICommandable
{
    void Move();
}

// base actor class
public abstract class Actor : ICommandable
{
    public void ExecuteCommand(ICommand command)
    {
        command.Execute(this);
    }
}

// an actor which can be moved
public class MovableActor: Actor, IMovable
{
    public void Move()
    {
        Console.WriteLine("Move");
    }
}

// an interface for commands
public interface ICommand
{
    void Execute(ICommandable robot);
}

// base command class
public abstract class Command<T> : ICommand where T : ICommandable
{
    void ICommand.Execute(ICommandable robot)
    {
        if (robot is T typedRobot)
        {
            Execute(typedRobot);
        }
        else
        {
            // Handle this error
        }
    }
    
    protected abstract void Execute(T robot);
}

// move command
public class MoveCommand : Command<IMovable>
{
    protected override void Execute(IMovable actor)
    {
        actor.Move();
    }
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.