使用泛型实现命令模式
Implementing the command pattern using generics
我正在尝试实现命令模式,所以它是这样工作的:
- 有一个基础 class
Command<T>
,所有其他命令都继承自该基础。
- 可以在从
Actor
派生的 class 上执行命令。
- 每个接口都定义了一个
Actor
可以执行的操作。例如。 IMovable
或 ITurnable
.
- 每个命令class作用于特定接口。例如。
Command<IMovable>
.
- Actor 实现接口,定义它们可以执行的操作。
以下是我尝试实现它的方法:
// 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.ExecuteCommand
,MoveCommand.Execute
就会失败,因为它需要一个 IMovable
.
您的基本选项是:
- 要么你不让演员自己调用命令:直接在演员上调用命令,要么让一个单独的
CommandInvoker
同时执行动作和命令
- 准备好应对有人可能会向无法接受命令的参与者发送命令的可能性,并在运行时检查并处理它。然后它只是
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();
}
}
我正在尝试实现命令模式,所以它是这样工作的:
- 有一个基础 class
Command<T>
,所有其他命令都继承自该基础。 - 可以在从
Actor
派生的 class 上执行命令。 - 每个接口都定义了一个
Actor
可以执行的操作。例如。IMovable
或ITurnable
. - 每个命令class作用于特定接口。例如。
Command<IMovable>
. - Actor 实现接口,定义它们可以执行的操作。
以下是我尝试实现它的方法:
// 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.ExecuteCommand
,MoveCommand.Execute
就会失败,因为它需要一个 IMovable
.
您的基本选项是:
- 要么你不让演员自己调用命令:直接在演员上调用命令,要么让一个单独的
CommandInvoker
同时执行动作和命令 - 准备好应对有人可能会向无法接受命令的参与者发送命令的可能性,并在运行时检查并处理它。然后它只是
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();
}
}