c# - 使用泛型实现命令模式
问题描述
我正在尝试实现命令模式,因此它的工作方式如下:
- 有一个基类
Command<T>
,所有其他命令都从该基类继承。 - 可以在派生自
Actor
. - 每个接口都定义了一个
Actor
可以执行的操作。例如IMovable
或ITurnable
。 - 每个命令类都作用于特定的接口。例如
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>
: 之类的命令,没有什么可以阻止您这样做。
但是如果你这样做了,并且MoveCommand.ExecuteCommand
用ICommand<ITurntable>
,调用MoveCommand.Execute
会失败,因为它想要一个IMovable
.
您的基本选择是:
- 要么你不让actor自己调用命令:直接在actor上调用命令,或者让一个单独
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();
}
}
推荐阅读
- php - 作为会话检查的一部分重定向到登录
- api - 尝试将新闻 API 与 Rails 应用程序一起使用
- java - 在单个 Java 类的命令行中使用 JUnit 编译和运行测试
- apache-kafka - 基于kafka分区的结构化流式读取
- amazon-web-services - 如何在 AWS cloudformation 模板中将批处理作业队列配置为 AWS::Events::Rule 的目标
- python - 如何使用 zipfile 将文件压缩到 STDOUT?
- c# - 使用像实体框架这样的 ORM 来更新保存在数据库中的数据的方法是什么?
- scikit-learn - 随机森林 sklearn-OOB 分数
- html - 使用链接或 CSS 按钮更改悬停时的图像
- git - 在gitlab中本地合并后无法推送