I'm trying to implement the command pattern, so it works like this:
Command<T>
, from which all other commands inherit.Actor
.Actor
can perform. E.g. IMovable
or ITurnable
.Command<IMovable>
.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?
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:
CommandInvoker
take both the action and the commandActor.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();
}
}