以下是我想以干净的方式实现的状态:
如您所见,我遇到的问题是Invoker
只知道Command
接口,但想要调用ConcreteCommand
。此实现再次需要来自Invoker
的一些参数。
现在是我到目前为止使用的脏解决方案:
我对这个解决方案有几个问题:
Invoker
具有关于计数和命令参数类型的元知识。Invoker
使用魔术字符串来获取所需的命令。Invoker
没有传递正确的参数时,我收到运行时错误。我更喜欢编译错误。将参数传递给具体命令的常用方法是什么,而不会破坏松耦合(例如Invoker
需要知道ConcreteCommand
)?
问候,Yggdrasil
答案 0 :(得分:1)
使用某种工厂来创建/访问ConcreteCommands?
这样Invoker只知道Command接口,而Factory知道如何将它与具体实现配对。
public class Factory {
// Find by key, can vary implementation at runtime
Command makeCommand(final String key);
// Compile-safe invocation, but you can still vary implementation at runtime
Command makeMySpecificCommand();
}
答案 1 :(得分:1)
如果您的Invoker需要向命令发送参数,并且您确实希望以类型安全的方式执行它(如果我们处理组合而不是继承而通常不那么容易),那么我将参数化Invoker它可以调用的参数类型 - 参数,以便现在Invoker可以调用支持这种类型参数的命令。斯塔姆就是这样。
public abstract class Command<T> where T:class
{
public abstract void Execute(T par);
}
public class ConcreteCommand<T> : Command<T> where T : class
{
private readonly Receiver<T> _receiver;
public ConcreteCommand(Receiver<T> receiver)
{
_receiver = receiver;
}
#region Overrides of Command<T>
public override void Execute(T par)
{
_receiver.MenuItemClick(par);
}
#endregion
}
public class Invoker<T> where T : class
{
private readonly T par;
private readonly Command<T> cmd;
public Invoker(T par, Command<T> cmd)
{
this.par = par;
this.cmd = cmd;
}
public void Invoke()
{
cmd.Execute(par);
}
}
public class Receiver<T> where T : class
{
public void MenuItemClick(T e)
{
Console.WriteLine("Parameter types {0}", e.GetType().FullName);
}
}
static internal class CmdBuilder
{
public static Command<T> PrepareCommand<T>() where T : class
{
Receiver<T> rcv = new Receiver<T>();
Command<T> cmd = new ConcreteCommand<T>(rcv);
return cmd;
}
}
class Program
{
static void Main(string[] args)
{
var cmd = CmdBuilder.PrepareCommand<EventArgs>();
cmd.Execute(new EventArgs());
Console.ReadKey(true);
}
}
其他选项是使用参数对象。由于所有命令都“知道”他们期望的参数类型,因此可以将其转换为参数对象的特定实现。但这不会给你编译错误
答案 2 :(得分:0)
我在这里可以看到2个选项。首先,您可以在 ConcreteCommand 构造函数中传递参数并将其保留为属性。稍后执行()方法将使用该属性。 如果你想直接从 Invoker 传递一个参数,那么另一个选项。您可以将 ICommand 界面更改为此执行(IArgument arg)每个参数都将从 IArgument 继承,最近将转换为原始类型ConcreteCommand实现。 如果当前的ConcreteCommand实现无法将参数转换为原始类型,比如 ArgumentImpl ,它将跳过执行并屈服于其他 ConcreteCommand 实现
答案 3 :(得分:0)
您可以在创建命令实例时传递参数,而不是向'execute'方法发送参数。
public class Factory {
Command createCommand(Object[] objs,String key){
return new ConcreteCommand(objs,key);
}
}