命令模式返回状态
有一次我讨论了与命令模式相关的设计。 我的同行指出,在调用 .execute() 方法后,命令对象不应返回状态(成功、不成功以及原因)。 原因是您不应该关心命令是否被执行,因为命令必须不包含任何状态。 但是,您必须在调用后检查该命令是否具有预期效果。 他争论的另一点是,在“四人帮”中,命令模式并没有呈现这种情况(返回状态)。
我主张相反的观点。 GoF 不提供这种情况,但可以根据您的需要对模式进行建模。 如果命令不成功,调用客户端必须收到状态证明,并最终部署适当的反应。 通过强制客户端检查操作是否成功,很容易出错并产生重复的代码。 此外,在某些情况下,命令会产生结果(例如,向绘图添加一条线的命令,将以某种方式将线 ID 返回给客户端),并且假装具有没有状态的命令意味着您必须从数据模型中“找出”新的对象标识符。
最后,我们达成了妥协,不返回状态,而是将新创建的对象的 id 保留在命令对象中,并且应用程序无论如何都运行得很好,但我现在也很想知道您的意见。
Once I had a discussion about design, relative to the command pattern.
My peer stated that a command object should not return the status (successful, unsuccessful, and why) after the .execute() method is called. The reason is that you should not be concerned if the command gets executed or not, because the command must contain no state. You must however check after the invocation if the command had the expected effect. Another point he argued was that on the Gang of Four, the command pattern does not present this case (of returning status).
I claimed the opposite point. The GoF does not present this case, but a pattern can be modeled to your needs. If a command is unsuccessful, the invoking client must receive a proof of the status, and eventually deploy an appropriate reaction. By forcing the client to check if the action achieved success was error prone and produced duplicated code. Moreover, there are cases where the command produces a result (eg. a command that adds a line to a plot, will somehow have the line id to return to the client), and pretending to have commands with no state meant that you had to "fish out" the new object identifier from the data model.
In the end, we reached a compromise by not returning status but keeping the id's of newly created objects in the command object, and the application worked pretty well anyway, but I am now curious to know your opinion as well.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
目前我面前还没有《设计模式:可重用面向对象软件的元素》,但我很确定作者甚至说他们提出的设计模式是一个可以修改以适应特定需求的模型。情况。
这个问题切入了设计模式的核心——模板。 这不是必须按书本执行的事情。 您确定了一种情况,对书中介绍的模式进行逻辑修改会对应用程序有所帮助,这完全没问题,尤其是在您权衡了收益和成本之后。
I don't have Design Patterns: Elements of Reusable Object-Oriented Software in front of me at the moment, but I'm pretty sure the authors even say that the design patterns they present are a model that can be modified to fit a specific situation.
This question cuts to the core of what a design pattern is - a template. It's not something that must be implemented by-the-book. You identified a case where a logical modification to the pattern as presented in the book would have helped the application, and that's perfectly fine, especially once you weigh the benefits and costs.
问题中有两个问题有多个答案:)
第一个问题是命令是否应该返回错误状态?
每个程序都没有明确的答案,每次应用该模式时,您都必须重新考虑。
您需要考虑的事情之一是:
在最坏的情况下,您有许多命令不关心错误,但一两个命令会执行一些对于客户端了解其是否有效很重要的操作。 现在,您将检查的异常添加到接口中,因此每个客户端和每个命令都必须执行错误处理并与异常耦合。 如果您的客户端仅处理不抛出异常的命令,那么您的代码会产生很大的开销。
这是你不想拥有的东西。 因此,您可以将需要错误处理的命令移出命令结构,因为它们似乎与其他命令不同,或者如果您的语言允许,您可以添加仅由关心并抛出的客户端处理的运行时异常需要抛出它们的命令。
另一个极端是每个命令都可能失败,并且客户端有一致的方式来处理错误,这意味着错误不依赖于特定的命令。 客户端不必知道哪种命令失败了,它可以以相同的方式处理每个错误。 现在您可以让命令的接口返回错误状态,并且客户端可以处理错误。 但处理错误不应取决于客户端命令的类型。
第二个问题是:命令应该有状态吗?
在某些体系结构中,命令需要状态,而在某些体系结构中,命令不需要状态。
决定这一点的一些可能性:
如果命令仅用于隐藏作用于一小组参数的函数的方式,并且结果仅取决于与状态模式相同的命令,则不需要状态,您可以使用同一个物体一遍又一遍。
如果您使用该命令在线程之间进行通信,并且希望将数据从一个线程传输到另一个线程,则该命令需要一个状态。
There are two questions in the question with multiple answers :)
The first question is should a command return an error state?
There is no clear answer for every program every time you apply the pattern you have to think about it again.
One of the things you need to think about is:
In the worst case you have many commands that don't care about errors but one or two commands do something that is important for the client to know if it worked. You now add checked Exceptions to the Interface and so every client and every Command are bound to do error handling and are coupled to the Exception. If you have a client that only deals with commands that are not throwing the exceptions you have a big overhead in your code.
This is a thing that you don't want to have. So you can either move the commands that need error handling out of the command structure because they seem to be different from the other commands, or if your language allows it you can add runtime exceptions that are only handled by the clients that care and thrown by the commands that need to throw them.
The other extreme is that every command can fail and the client has a consistent way of handling the errors this means that the errors doesn't depend on the specific command. The client does not have to know what kind of command failed it can handle every error in the same way. Now you could have the interface of the command return an error state and the client can deal with the errors. But dealing with the errors shouldn't depend on the kind of the command for the client.
The second question is: Should a command have a state?
There are architectures where a command needs a state and some where they don't need a state.
Some possibilities to decide this:
If the commands are only used for hiding a way a function that works on a small set of parameters and the outcomes only depends on the same of the command like the state pattern there is no need for a state and you can use the same object over and over.
If you use the command to communicate between threads and you want to transfer data from one thread to another the command needs a state.
我将参考“Head First 设计模式”。 他们用于命令模式的示例是:
显然,在第一种情况下,接收者会产生某种状态:“这是幼虫”,或“我们没有黑麦面包了”。 在一家高档餐厅,您可以通过更高级别的异常处理来做到这一点(领班来到餐桌上道歉,提供替代品并为您准备甜点),而服务员除了正确调用命令之外什么都不用做。 但在餐馆里,也许厨师会继续用黑面包代替——服务员(和顾客)需要能够处理这个问题,而不必盯着柜台想知道“我的黑麦金枪鱼在哪里?” 这本书没有直接解决这个问题,但我认为这显然是一个有效的案例。
但在第二种情况下,调用者被故意弄得愚蠢。 如果出现问题,它不会向您显示错误,它只是根本没有任何效果。 所有的智能都在客户端中,以确定其命令是否及时成功(“糟糕,我忘了插入它”),或者在接收器中确定要做什么(“播放 CD:关闭 CD 托盘”)第一的”)。
我不是专家,但我想说,对于某些应用程序来说,将状态返回给调用者是完全可以的。
I'll refer to "Head First Design Patterns". The examples they use for the Command Pattern are:
Obviously in the first case, some kind of state is produced by the receiver: "here's the grub", or "we're out of rye bread". In a fancy restaurant you might do this through exception handling at a higher level (maitre d' comes to the table and apologizes, offers a substitute and comps your dessert), and the wait staff needs to do nothing but properly invoke the commands. But at a diner, maybe the cook goes ahead and substitutes brown bread -- the wait staff (and customer) need to be able to handle that without staring at the counter wondering "where's my tuna on rye?" This isn't addressed directly by the book, but I think it's clearly a valid case.
But in the second scenario, the invoker is deliberately made stupid. It's not going to flash an error at you if something's wrong, it's just going to have no effect at all. All of the smarts are in the client to determine if its command was successful in a timely fashion ("crap, I forgot to plug it in"), or in the receiver to figure out what to do ("play CD: close CD tray first").
I am not an expert, but I would say that returning status to the invoker is totally okay for some applications.
非常好的讨论。 我已经研究这个哲学问题几个小时了,我找到了一个满足我痴迷的解决方案。 (我喜欢这个东西的原因是它结合了具体和抽象的逻辑——布尔+设计。)
我简单地考虑过使用异常来返回结果。 我放弃了这个想法,因为在许多情况下,它会消除解耦,即模式本身的核心,正如你们中的一些人所指出的。 此外,结果通常不是异常,而是标准返回值。 我可能会得溃疡。
最终,我编写了一个客户端,它用自身实例化接收器,将接收器中的所有逻辑保留在其所属的位置。 客户端只需调用命令的execute() 并继续。 然后接收者可以调用客户端上的公共方法。 没有什么可以回报的。
这是一些示例代码。 我没有编写命令类,因为我想即使没有它你也会明白的。 它的execute()方法调用接收者的run()方法。
客户端:
接收者:
乍一看,我似乎违反了解耦要求。 但请考虑客户端对接收方的实现一无所知。 接收者知道调用客户端上的公共方法这一事实是标准费用。 接收者总是知道如何处理他们的参数对象。 没有依赖性。 接收者的构造函数采用 ClientType 参数这一事实是无关紧要的。 它也可以是任何物体。
我知道这是一个老话题,但希望你们中的一些人能再次参与进来。 如果您发现任何缺陷,请随时让我心碎。 这就是我们所做的。
Very nice discussion. I’ve been on this philosophical question for hours, and I came to a solution that satisfies my obsession. (The reason I love this stuff is that it combines concrete and abstract logic - Booleans + designs.)
I briefly considered using Exceptions to return results. I abandoned that idea because in many cases it would eliminate decoupling, the heart of the pattern itself, as some of you have noted. In addition, the result is frequently not an Exception, but rather a standard return value. I’d probably get ulcers.
Ultimately, I wrote a client that instantiates a receiver with itself, keeping all of the logic in the receiver where it belongs. The client just calls the command’s execute() and continues. The receiver can then call public methods on the client. There’s nothing to return.
Here’s some sample code. I didn’t write the command class because I think you’ll get the idea without it. Its execute() method calls the receiver’s run() method.
The client:
The receiver:
At first glance, it might appear that I’ve violated the decoupling requirement. But consider that the client knows nothing about the receiver’s implementation. The fact that the receiver knows to call public methods on the client is standard fare. Receivers always know what to do with their parameter objects. There are no dependencies. The fact that the receiver’s constructor takes a ClientType parameter is irrelevant. It could just as well be any object.
I know that this is an old thread, but hope that some of you will chime in again. Feel free to break my heart if you see a flaw. That’s kinda what we do.
这绝对是值得商榷的,但它清楚地表明了两种思维方式:
我不认为一种方法比另一种更好。 例如,在 Java 中,通常最好不要滥用异常处理,并在简单地将双手(和异常)抛向空中之前解决任何可能的问题。 对于 Python,更好的做法是继续尝试执行任何操作,而不管状态代码如何,并让任何异常都得到相应的处理。
是否希望命令模式返回状态实际上取决于您。
This is definitely debatable, but it clearly shows the two styles of thinking:
I don't think one way is better than the other. For example in Java, it's typically best to not abuse your exception handling and to take care of any possible problems prior to simply throwing your hands (and exceptions) in the air. With Python, it's more preferable to just go ahead and attempt to do whatever, regardless of status code, and let any exception simply be dealt with accordingly.
It really is up to you as to whether or not you would like the command pattern to return a status.
这里的问题可能是该命令将由某个执行器类执行,而该执行器类不直接了解该命令的实际用途。
如果我们谈论向执行方法添加返回类型,则有可能向执行器公开实现特定的返回类型。 我的意思是,您正在为不同的命令可能具有不同的返回值集的情况打开一扇门。 如果执行器必须处理这些,那么它将与命令实现更加紧密地耦合。
但是,我经常给出命令状态 - 允许客户端在构造时为它们配置工作值,然后提供 getter 以允许客户端在完成时提取命令执行的结果。 在这种情况下,我可能没有严格遵循命令模式 - 但设计效果很好 - 除非有明确的代码味道 - 这真的是一个问题吗?
注意:也就是说,我很想听听关于为什么这可能是代码异味的想法。
Could the issue here be that the command will be executed by some executor class that will have no direct knowledge of what the command actually does.
If we are talking about adding a return type to the execute method there is a potential for exposing implementation specific return types to the executor. By this I mean that you are opening a door to a situation where different commands might have different sets of return values. If the executor were to the have to handle these then it would become more tightly coupled to the command implementations.
However, I have often given commands state - allowing them to be configured with working values by the client on construction, and then providing getters to allow the client to extract the results of command execution on completion. In this case I may not have strictly followed the command pattern - but the design worked well - and unless there is a definite code smell about this - is this really an issue?
Note: That said, I would be interested to hear thoughts on why this may be a code smell.
正如你的问题中所说:
在这种情况下,我会抛出运行时异常作为状态,其中包含有关它的必要信息。 你可以尝试一下。
问候,
As said in your question:
In that case, i throw runtime exceptions as status, containing necessary information about it. You could try it.
regards,
另一个妥协是在可能失败的具体命令上公开属性“异常处理程序”。 这样,命令的创建者就可以处理异常,并且您不会向客户端添加代码开销。 当大多数命令不应失败时,这非常有用。
Another compromise is to expose a property "Exception handler" on a concrete command which can fail. This way the creator of the command can handle the exception, and you don't add code overhead to your client. This is very useful when most of your commands shouldn't fail.
在我的 CAD/CAM 软件中,包含命令的程序集引用包含接口和对象层次结构的程序集,这些接口和对象层次结构保存了我的软件的各种 UI 元素。 它类似于 被动视图
命令可以通过视图界面操作 UI,自行报告任何错误。
基本上,
表单实现 IFormInterfaces 并在 EXE 中向 ScreenView 注册自己。
ScreenObject 实现 IScreenView 并向 ScreenView 程序集注册自己,并从命令程序集中获取
命令
。查看接口并保存应用程序实现。
In my CAD/CAM Software, the Assemblies containing the Commands references the assemblies containing the interfaces and object hierarchy that holds the various UI elements of my software. It is similar to a Passive View
The Commands can manipulate the UI through the View Interfaces and report any errors themselves.
Basically it goes
Forms implement IFormInterfaces and register themselves with ScreenViews in the EXE
ScreenObjects implement IScreenView and register themselves with the ScreenView assembly as well as grab commands from the command assemblies
Command Assemblies reference the ScreenView Assembly and the Model
ScreenView Assembly little more than a collection of View Interfaces and holds the Application Implementation.