首页 > 解决方案 > 使用泛型实现命令模式

问题描述

我正在尝试实现命令模式,因此它的工作方式如下:

这是我尝试实现它的方法:

// 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关键字作为其参数。我做了一些阅读,我明白为什么会这样,但我不知道如何实现我所描述的。我该如何实施?

如果这是不可能的,我应该如何改变我的描述,所以它尽可能接近这个,但有效吗?

标签: c#genericsinterfacecommand-pattern

解决方案


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

但是如果你这样做了,并且MoveCommand.ExecuteCommandICommand<ITurntable>,调用MoveCommand.Execute会失败,因为它想要一个IMovable.

您的基本选择是:

  1. 要么你不让actor自己调用命令:直接在actor上调用命令,或者让一个单独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();
    }
}

推荐阅读