命令模式返回状态

一旦我有关于devise的讨论,相对于命令模式。 我的同事说,在调用.execute()方法之后,命令对象不应返回状态(成功,失败,为什么)。 原因是你不应该担心命令是否被执行,因为命令不能包含任何状态。 但是,如果命令具有预期的效果,则必须在调用之后进行检查。 他提出的另一个观点是,在四人帮上,指挥模式并没有出现这种情况(返回状态)。

我声明了相反的观点。 GoF没有提供这种情况,但是一个模式可以模拟您的需求。 如果命令不成功,调用客户端必须接收状态certificate,并最终部署适当的响应。 通过强制客户检查行动取得成功是否容易出错,并产生重复的代码。 此外,在某些情况下,命令会产生一个结果(例如,一个命令添加一条线到一个情节,将以某种方式将行ID返回到客户端),假装没有状态的命令意味着你必须从数据模型中“捞出”新的对象标识符。

最后,我们通过不返回状态而达成了一个妥协,但将新创build的对象的id保存在命令对象中,而且应用程序工作得很好,但是我现在好奇地知道你的意见。

有两个问题在多个答案的问题:)第一个问题是应该返回一个错误状态?

每次应用这个模式时,每个程序都没有明确的答案,需要再次考虑。

你需要考虑的事情之一是:

  • 我是否只为一些特定的错误情况添加更多耦合到许多命令和客户端?

在最糟糕的情况下,你有很多不关心错误的命令,但是有一两个命令对客户端来说是非常重要的。 现在,您将检查到的exception添加到接口,因此每个客户端和每个命令都必须执行error handling,并与exception耦合。 如果你有一个客户端只处理那些不会抛出exception的命令,那么在代码中会有很大的开销。

这是你不想拥有的东西。 因此,您可以将需要error handling的命令移出命令结构,因为它们似乎与其他命令不同,或者如果您的语言允许,您可以添加运行时exception,这些exception仅由关心和抛出的客户机处理需要抛出它们的命令。

另一个极端是每个命令都可能失败,客户端有一致的处理错误的方式,这意味着错误不依赖于特定的命令。 客户端不必知道什么样的命令失败,它可以以相同的方式处理每个错误。 现在你可以让命令的界面返回错误状态,客户端可以处理错误。 但是处理这些错误不应该取决于客户端的命令types。

第二个问题是:一个命令是否应该有一个状态?

有一些命令需要一个状态的体系结构和一些不需要状态的体系结构。

一些可能性来决定这一点:

  • 如果你想对你的命令进行撤销,那么命令需要有一个状态。
  • 如果这些命令只是用来隐藏一个函数,而这个函数只对一小组参数起作用,而且结果只依赖于命令的相同状态模式,则不需要一个状态,你可以使用相同的对象并结束。

  • 如果使用该命令在线程之间进行通信,并且要将数据从一个线程传输到另一个线程,则该命令需要一个状态。

  • …如果有什么你认为应该在这个列表中留下评论。

我目前还没有devise模式:可重用面向对象软件的元素,但是我敢肯定作者甚至说他们提供的devise模式是一个可以修改以适应特定情况。

这个问题切入了devise模式的核心 – 模板。 这不是必须由书实现的东西。 您确定了一个案例,其中对本书中介绍的模式进行了逻辑修改将有助于该应用程序,而且这非常好,特别是在您权衡利益和成本之后。

我将参考“头一个devise模式”。 他们用于命令模式的例子是:

  1. 用餐场景(客户创build订单,等待的工作人员通过在厨房工作人员打电话来调用,厨房工作人员接受订单)
  2. 远程控制场景(用户点击button,遥控器调用该命令,设备接收该命令)

显然,在第一种情况下,接收者会产生某种状态:“这是gr”“,或者”我们没有黑麦面包“。 在一家高级餐厅,你可能会通过exception处理来达到这个目的(maitre d'来到桌子上,道歉,提供一个替代品并且补充你的甜点),而等待的人员只需要正确地调用命令就不用做任何事情。 但是在一个小餐馆里,也许厨师继续前进,代替棕色面包 – 等待的工作人员(和客户)需要能够处理,而不要盯着柜台想知道“我的金枪鱼在黑麦的哪里? 这本书没有直接解决,但我认为这显然是一个有效的案例。

但在第二种情况下,调用者是故意做的愚蠢的。 如果出现错误,这不会在你身上闪现一个错误,它根本就没有任何作用。 所有的智能都在客户端,以确定它的命令是否及时成功(“废话,我忘了插入”),或在接收器弄清楚该怎么做(“播放CD:closuresCD托盘第一”)。

我不是一个专家,但我会说,返回状态调用者是完全可以的一些应用程序。

这绝对是有争议的,但它清楚地表明了两种思维方式:

  • 检查是否有问题,然后进行相应处理
  • 反正继续处理,如果有什么不好的事情发生

我不认为一种方式比另一种更好。 例如,在Java中,通常最好不要滥用exception处理,并在将您的手(和例外)抛在空中之前处理任何可能的问题。 对于Python来说,不pipe状态码如何,只要继续前进并尝试做任何事情,就可以简单地处理任何exception。

您是否希望命令模式返回状态真的取决于您。

难道这里的问题是命令将由一些执行者类执行,而这些执行者类将不直接知道命令实际执行的内容。

如果我们正在讨论向execute方法添加返回types,那么就有可能向执行程序公开实现特定的返回types。 通过这个,我的意思是说,你打开了一扇门,让不同的命令可能有不同的返回值组。 如果执行者不得不处理这些事情,那么它将变得更紧密地耦合到命令实现。

不过,我经常给出命令状态 – 允许它们在构造时由客户端configuration工作值,然后提供getter来允许客户端在完成时提取命令执行的结果。 在这种情况下,我可能没有严格遵循命令模式 – 但devise运行良好 – 除非有明确的代码味道 – 这真的是一个问题吗?

注:也就是说,我有兴趣听到为什么这可能是一个代码味道的想法。

非常好的讨论。 我已经在这个哲学问题上好几个小时了,我find了一个解决scheme来满足我的痴迷。 (我喜欢这个东西的原因是它结合了具体和抽象的逻辑 – 布尔+devise。)

我简单地考虑过使用exception来返回结果。 我放弃了这个想法,因为在许多情况下,它会消除脱钩,这是模式本身的核心,正如你们中一些人所指出的那样。 另外,结果往往不是一个Exception,而是一个标准的返回值。 我可能会溃疡。

最终,我写了一个客户端,它自己实例化一个接收器,将接收器中的所有逻辑保留在它所属的地方。 客户端只是调用命令的execute()并继续。 接收者可以在客户端调用公共方法。 没有什么可以回来的

这是一些示例代码。 我没有写命令课,因为我觉得你会没有它的想法。 它的execute()方法调用接收者的run()方法。

客户端:

Class ClientType{ CommandType m_Command; ReceiverType m_Receiver; boolean m_bResult; ClientType(){ m_Receiver = new ReceiverType(this); m_Command = new CommandType(m_Receiver); } public void run(){ ... m_Command.execute(); } /* Decoupled from both the * command and the receiver. * It's just a public function that * can be called from anywhere. / public setResult(boolean bResult){ m_bResult = bResult; } } 

收件人:

 Class ReceiverType{ ClientType m_Client; boolean m_bResult; ReceiverType(ClientType client){ m_Client = client; } public void run(){ ... m_Client.setResult(m_bResult); } } 

乍看之下,似乎我违反了解耦要求。 但是考虑到客户端对接收器的实现一无所知。 接收者知道在客户端上调用公共方法的事实是标准票价。 接收者总是知道如何处理他们的参数对象。 没有依赖关系。 接收者的构造函数接受ClientType参数的事实是不相关的。 它也可以是任何对象。

我知道这是一个古老的线索,但希望你们中的一些人再次join。 如果您发现缺陷,请随时打破我的心。 这是我们所做的。

正如你的问题所说:

如果命令不成功,调用CLIENT必须接收状态certificate,并最终部署适当的响应。

在这种情况下,我把运行时exception作为状态,包含必要的信息。 你可以试试。

问候,

另一个折衷办法是在具体的命令上暴露一个属性“exception处理程序”,这个命令可能会失败。 通过这种方式,命令的创build者可以处理exception,并且不会将代码开销添加到客户端。 当大多数命令不应该失败时,这是非常有用的。

在我的CAD / CAM软件中,包含命令的程序集引用了包含接口和对象层次结构的程序集,该程序包含了我软件的各种UI元素。 它类似于被动视图

命令可以通过视图界面操作UI,并自己报告任何错误。

基本上就是这样

表单实现IFormInterfaces并注册自己与EXE中的ScreenViews

ScreenObjects实现IScreenView并向ScreenView程序集注册自己,以及从命令程序集中获取命令

命令程序集引用ScreenView程序集和模型

ScreenView程序集不仅仅是视图接口的集合,而且还包含应用程序实现。