使用泛型实现命令模式

Implementing the command pattern using generics

我正在尝试实现命令模式,所以它是这样工作的:

以下是我尝试实现它的方法:

// 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();
    }
}

这是我正在尝试做的一个例子:

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

我的实现的问题是 ICommand 接口必须为其参数使用 out 关键字。我做了一些阅读,我明白为什么会这样,但我不知道如何实现我所描述的。我该如何实施?

如果这不可能,我应该如何更改我的描述,使其尽可能接近这个,但有效?

这里的基本问题是您的 MoveCommand.Execute 必须被赋予 IMovable -- 该方法取决于 IMovable。但是,您可以将任何内容传递给 Actor.ExecuteCommand,甚至是 ICommand<ITurntable> 之类的命令:没有什么可以阻止您这样做。

但是如果你那样做,并用 ICommand<ITurntable> 调用 MoveCommand.ExecuteCommandMoveCommand.Execute 就会失败,因为它需要一个 IMovable.

您的基本选项是:

  1. 要么你不让演员自己调用命令:直接在演员上调用命令,要么让一个单独的CommandInvoker同时执行动作和命令
  2. 准备好应对有人可能会向无法接受命令的参与者发送命令的可能性,并在运行时检查并处理它。然后它只是 Actor.ExecuteCommand(ICommand command),然后您进行运行时检查以查看它是否是正确的命令。

看起来像这样:

// 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();
    }
}