java 当我可以轻松调用所需的方法时,为什么要使用命令设计模式?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/32597736/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why should I use the command design pattern while I can easily call required methods?
提问by Daniel Newtown
I am studying the command design pattern, and I am quite confused with the way of using it. The example that I have is related to a remote control class that is used to turn lights on and off.
我正在研究命令设计模式,我对它的使用方式很困惑。我的示例与用于打开和关闭灯的远程控制类有关。
Why should I not use the switchOn() / switchOff() methods of Light class rather than having separate classes and methods that eventually call switchOn / switchOff methods?
为什么我不应该使用 Light 类的 switchOn() / switchOff() 方法,而不是使用最终调用 switchOn / switchOff 方法的单独类和方法?
I know my example is quite simple, but that is the point. I could not find any complex problem anywhere on the Internet to see the exact usage of the command design pattern.
我知道我的例子很简单,但这就是重点。我在 Internet 上的任何地方都找不到任何复杂的问题来查看命令设计模式的确切用法。
If you are aware of any complex real world problem that you solved that can be solved using this design pattern please share that with me. It helps me and future readers of this post to better understand the usage of this design pattern. Thanks
如果您知道您解决的任何复杂的现实世界问题可以使用此设计模式解决,请与我分享。它可以帮助我和这篇文章的未来读者更好地理解这种设计模式的用法。谢谢
//Command
public interface Command {
public void execute();
}
//Concrete Command
public class LightOnCommand implements Command {
//Reference to the light
Light light;
public LightOnCommand(Light light) {
this.light = light;
}
public void execute() {
light.switchOn(); //Explicit call of selected class's method
}
}
//Concrete Command
public class LightOffCommand implements Command {
//Reference to the light
Light light;
public LightOffCommand(Light light) {
this.light = light;
}
public void execute() {
light.switchOff();
}
}
//Receiver
public class Light {
private boolean on;
public void switchOn() {
on = true;
}
public void switchOff() {
on = false;
}
}
//Invoker
public class RemoteControl {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void pressButton() {
command.execute();
}
}
//Client
public class Client {
public static void main(String[] args) {
RemoteControl control = new RemoteControl();
Light light = new Light();
Command lightsOn = new LightsOnCommand(light);
Command lightsOff = new LightsOffCommand(light);
//Switch on
control.setCommand(lightsOn);
control.pressButton();
//Switch off
control.setCommand(lightsOff);
control.pressButton();
}
}
Why should not I easily use code like the following?
为什么我不应该轻松地使用如下代码?
Light light = new Light();
switch(light.command) {
case 1:
light.switchOn();
break;
case 2:
light.switchOff();
break;
}
回答by sprinter
The main motivation for using the Commandpattern is that the executor of the command does not need to know anything at all about what the command is, what context information it needs on or what it does. All of that is encapsulated in the command.
使用命令模式的主要动机是命令的执行者根本不需要知道关于命令是什么、它需要什么上下文信息或它做什么的任何信息。所有这些都封装在命令中。
This allows you to do things such as have a list of commands that are executed in order, that are dependent on other items, that are assigned to some triggering event etc.
这允许你做一些事情,比如有一个按顺序执行的命令列表,这些命令依赖于其他项目,分配给某个触发事件等。
In your example, you could have other classes (e.g. Air Conditioner
) that have their own commands (e.g. Turn Thermostat Up
, Turn Thermostat Down
). Any of these commands could be assigned to a button or triggered when some condition is met without requiring any knowledge of the command.
在您的示例中,您可以拥有其他类(例如Air Conditioner
),它们具有自己的命令(例如Turn Thermostat Up
, Turn Thermostat Down
)。这些命令中的任何一个都可以分配给按钮或在满足某些条件时触发,而无需了解该命令。
So, in summary, the pattern encapsulates everything required to take an action and allows the execution of the action to occur completely independently of any of that context. If that is not a requirement for you then the pattern is probably not helpful for your problem space.
因此,总而言之,该模式封装了采取行动所需的一切,并允许行动的执行完全独立于任何上下文。如果这不是您的要求,那么该模式可能对您的问题空间没有帮助。
Here's a simple use case:
这是一个简单的用例:
interface Command {
void execute();
}
class Light {
public Command turnOn();
public Command turnOff();
}
class AirConditioner {
public Command setThermostat(Temperature temperature);
}
class Button {
public Button(String text, Command onPush);
}
class Scheduler {
public void addScheduledCommand(Time timeToExecute, Command command);
}
Then you can do things such as:
然后,您可以执行以下操作:
new Button("Turn on light", light.turnOn());
scheduler.addScheduledCommand(new Time("15:12:07"), airCon.setThermostat(27));
scheduler.addScheduledCommand(new Time("15:13:02"), light.turnOff());
As you can see the Button
and Scheduler
don't need to know anything at all about the commands. Scheduler
is an example of a class that might hold a collection of commands.
正如您所看到的,Button
并且Scheduler
根本不需要了解有关命令的任何信息。Scheduler
是一个可能包含命令集合的类的示例。
Note also that in Java 8 functional interfaces and method references have made this type of code even neater:
另请注意,在 Java 8 中,函数式接口和方法引用使这种类型的代码更加简洁:
@FunctionalInterface
interface Command {
void execute();
}
public Light {
public void turnOn();
}
new Button("Turn On Light", light::turnOn);
Now the methods that are turned into commands don't even need to know about commands - as long as they have the correct signature you can quietly create a anonymous command object by referencing the method.
现在,变成命令的方法甚至不需要知道命令——只要它们具有正确的签名,您就可以通过引用该方法悄悄地创建一个匿名命令对象。
回答by holroy
Let us focus on the non-implementation aspect of the command design, and some main reasons for using the Command desing pattern grouped in two major categories:
让我们关注命令设计的非实现方面,以及使用分为两大类的命令设计模式的一些主要原因:
- Hiding actual implementation of how the command is executed
- Allow methods to be built around command, aka command extensions
- 隐藏命令执行方式的实际实现
- 允许围绕命令构建方法,也就是命令扩展
Hide implementation
隐藏实现
In most programming, you'll want to hide away implementation so that when looking at the top-most problem, it consists of a comprehensible subset of commands/code. I.e. You don't need/want to know the gory details of how a light is switched on, or a car is started. If your focus is to get the car started, you don't need to understand how the engine works, and how it needs fuel to enter the engine, how the valves work, ...
在大多数编程中,您会希望隐藏实现,以便在查看最重要的问题时,它由可理解的命令/代码子集组成。即您不需要/想知道如何打开灯或汽车启动的血腥细节。如果您的重点是让汽车启动,您就不需要了解发动机是如何工作的,以及它如何需要燃料才能进入发动机,阀门是如何工作的,......
Indicating the action, not how it is done
表明行动,而不是如何完成
A command gives you this kind of view. You'll immediately understand what the TurnLightOn
command does, or StartCar
. Using a command you'll hide the details of how something is done, whilst clearly indicating the action which is to be executed.
命令为您提供这种视图。您将立即了解该TurnLightOn
命令的作用,或者StartCar
. 使用命令,您将隐藏某事如何完成的详细信息,同时清楚地指示要执行的操作。
Allow changing of inner details
允许更改内部细节
In addition, lets say you later on rebuild your entire Light
class, or a Car
class, which requires you instantiate several different objects, and possibly you need to something else before actually doing your wanted operation. In this case if you had implemented the direct access method, in a lot of places, you would need to change it in all places where you've coded it beforehand. Using a command, you can change the inner details of how to do stuff without changing the calling of the command.
此外,假设您稍后重建整个Light
类或一个Car
类,这需要您实例化几个不同的对象,并且在实际执行所需操作之前可能需要进行其他操作。在这种情况下,如果您已经实现了直接访问方法,那么在很多地方,您需要在预先对其进行编码的所有地方对其进行更改。使用命令,您可以在不更改命令调用的情况下更改如何执行操作的内部细节。
Possible command extensions
可能的命令扩展
Using a Command interface gives you an extra layer between the code using the command, and the code doing the actual action of the command. This can allow for multiple good scenarios.
使用命令接口为您在使用命令的代码和执行命令的实际操作的代码之间提供了一个额外的层。这可以允许多个好的场景。
Security extension, or interface exposure
安全扩展,或接口暴露
Using a command interface, you can also limit access to your objects, allowing you to define another level of security. It could make sense to have a module/library with a fairly open access so that you could handle internal special cases easily.
使用命令界面,您还可以限制对对象的访问,从而定义另一个安全级别。拥有一个具有相当开放访问权限的模块/库是有意义的,这样您就可以轻松处理内部特殊情况。
From the outside, however, you might want to restrict the access to the light so that it is only to be turned on or off. Using commands gives you the ability to limit the interface towards a class.
但是,从外面看,您可能希望限制对灯的访问,以便它只能打开或关闭。使用命令使您能够将接口限制为一个类。
In addition if you want, you could build a dedicated user access system around the commands. This would leave all of your business logic open and accessible and free of restrictions, but still you could easily restrict access at the command level to enforce proper access.
此外,如果您愿意,您可以围绕命令构建一个专用的用户访问系统。这将使您的所有业务逻辑保持开放、可访问且不受限制,但您仍然可以轻松地在命令级别限制访问以强制执行适当的访问。
Common interface to executing stuff
执行东西的通用接口
When building a sufficient large system, commands gives a neat way to bridge between different modules/libraries. Instead of you needing to examine every implementation detail of any given class, you can look into which commands are accessing the class.
在构建足够大的系统时,命令提供了一种在不同模块/库之间桥接的巧妙方法。您无需检查任何给定类的每个实现细节,而是可以查看哪些命令正在访问该类。
And since you are leaving out implementation details to the command itself, you could use a common method to instantiate the command, execute it and review the result. This allows for easier coding, instead of needing to read up on how to instantiate that particular Light
or Car
class, and determining the result of it.
由于您将实现细节留给命令本身,因此您可以使用通用方法来实例化命令、执行它并查看结果。这允许更轻松的编码,而不是需要阅读如何实例化该特定Light
或Car
类,并确定其结果。
Sequencing of commands
命令的排序
Using commands, you can also do stuff like sequencing of commands. That is since you don't really matter if you are executing the TurnOnLight
or StartCar
command, you can execute sequences of theses as they are executed the same way. Which in turns can allow for chains of commands to be executed, which could be useful in multiple situation.
使用命令,您还可以执行命令排序等操作。那是因为你是否正在执行TurnOnLight
orStartCar
命令并不重要,你可以执行这些序列,因为它们以相同的方式执行。这反过来又可以允许执行命令链,这在多种情况下可能很有用。
You could build macros, execute sets of commands which you believe are grouped together. I.e. the command sequence: UnlockDoor
, EnterHouse
, TurnOnLight
. A natural sequence of commands, but not likely to made into a method as it uses different objects and actions.
您可以构建宏,执行您认为组合在一起的命令集。即命令序列:UnlockDoor
, EnterHouse
, TurnOnLight
. 命令的自然序列,但由于它使用不同的对象和动作,因此不太可能成为一种方法。
Serialisation of commands
命令序列化
Commands being rather small in nature, also allows for serialisation quite nicely. This is useful in server-client context, or program-microservice context.
命令本质上相当小,也可以很好地进行序列化。这在服务器-客户端上下文或程序-微服务上下文中很有用。
The server (or program) could trigger a command, which then serialises the command, sends it over some communication protocol (i.e. event queue, message queue, http, ... ) to someone actually handling the command. No need to first instantiate the object at the server, i.e. a Light
which could be light-weight (pun intended), or a Car
which could be a really large structure. You just need the command, and possibly a few parameters.
服务器(或程序)可以触发一个命令,然后该命令序列化该命令,通过某种通信协议(即事件队列、消息队列、http 等)将其发送给实际处理该命令的人。无需首先在服务器实例化对象,即 aLight
可能是轻量级的(双关语),或者 aCar
可能是一个非常大的结构。您只需要命令,可能还有一些参数。
This could be a good place to introduce to the CQRS - Command Query Responsibility Separation patternfor further studies.
这可能是介绍CQRS - 命令查询职责分离模式以供进一步研究的好地方。
Tracking of commands
命令跟踪
Using the extra layer of commands in your system could also allow for logging or tracking commands, if that is a business need. Instead of doing this all over the place, you can gather tracking/logging within the command module.
如果业务需要,在您的系统中使用额外的命令层还可以允许记录或跟踪命令。您可以在命令模块中收集跟踪/日志记录,而不是到处执行此操作。
This allows for easy changes of logging system, or addition of stuff like timeing, or enabling/disabling logging. And it's all easily maintained within the command module.
这允许轻松更改日志记录系统,或添加诸如计时之类的内容,或启用/禁用日志记录。而且这一切都可以在命令模块中轻松维护。
Tracking of commands also allows for allowing undo actions, as you can choose to re-iterate the commands from a given state. Needs a little bit of extra setup, but rather easily doable.
命令的跟踪还允许允许撤消操作,因为您可以选择从给定状态重新迭代命令。需要一些额外的设置,但很容易做到。
In short, commands can be really useful as they allow connection between different parts of your soon-to-be-large program, as they are light-weight easily remembered/documented and hides the implementation details when so needed. In addition the commands allows for several useful extensions, which when building a larger system will come in handy: i.e. common interface, sequencing, serialisation, tracking, logging, security.
简而言之,命令非常有用,因为它们允许在即将成为大型程序的不同部分之间进行连接,因为它们是轻量级的,易于记忆/记录,并在需要时隐藏实现细节。此外,这些命令允许进行一些有用的扩展,在构建更大的系统时会派上用场:即通用接口、排序、序列化、跟踪、日志记录、安全性。
回答by yshavit
The possibilities are many, but it'd typically be something like:
可能性有很多,但通常是这样的:
- building a command-line framework that abstracts out the parsing of options from the action. Then you can register an action with something like
opts.register("--on", new LightOnCommand())
. - letting users drag and drop a sequence of actions to execute as a macro
- registering a callback when some event is triggered, like
on(Event.ENTER_ROOM, new LightOnCommand())
- 构建一个命令行框架,从动作中抽象出选项的解析。然后你可以用类似的东西注册一个动作
opts.register("--on", new LightOnCommand())
。 - 让用户拖放一系列动作以作为宏执行
- 在触发某些事件时注册回调,例如
on(Event.ENTER_ROOM, new LightOnCommand())
The general pattern here is that you've got one piece of the code responsible for figuring out that someaction needs to be taken without knowing what that action is, and another piece of the code knows how to do an action but not when to do it.
这里的一般模式是,您有一段代码负责在不知道该操作是什么的情况下确定需要采取某些操作,而另一段代码知道如何执行某个操作但不知道何时执行它。
For instance, in that first example, the opts
instance knows that when it sees a command line option --on, it should turn the lights on. But it knows this without actually knowing what "turn the lights on" means. In fact, it could well be that the opts instance came from a third-party library, so it can'tknow about lights. All it knows about is how to accociate actions (commands) with command-line options that it parses.
例如,在第一个示例中,opts
实例知道当它看到命令行选项 --on 时,它应该打开灯。但它知道这一点,但实际上并不知道“打开灯”是什么意思。实际上,很可能 opts 实例来自第三方库,因此它无法了解灯光。它只知道如何将操作(命令)与它解析的命令行选项相关联。
回答by jhn
You don't have to. Design patterns are simply guidelinesthat some people in the past have found useful when writing applications of significant complexity.
你不必。设计模式只是一些指南,过去有些人在编写非常复杂的应用程序时发现它们很有用。
In your case, if what you have to do is turn the light switch on and off, and not much else, the second option is a no brainer.
在您的情况下,如果您必须做的是打开和关闭电灯开关,而不是其他任何事情,那么第二个选项是明智之举。
Less code is almost always better than more code.
更少的代码几乎总是比更多的代码更好。
回答by Ian Ringrose
The example ICommand
you give is rather limited and is only of real use in a programming language that does not have lambda expressions. Sprinter covers this well in his answers showing the use of command factories.
ICommand
您给出的示例相当有限,并且仅在没有 lambda 表达式的编程语言中真正使用。Sprinter 在他展示命令工厂使用的答案中很好地涵盖了这一点。
Most cases of the command pattern include other methods for example, CanRun
and/or Undo
. These allows a button to update its enable state based on the state of the command, or a application to implement an undo stack.
命令模式的大多数情况包括其他方法,例如,CanRun
和/或Undo
。这些允许按钮根据命令的状态更新其启用状态,或允许应用程序实现撤消堆栈。
Like most design patterns, the Command Pattern comes into its own where things get a little more complex. It is also well know, so helps make your code clearly to most programmers.
像大多数设计模式一样,命令模式在事情变得更复杂的地方出现了。它也是众所周知的,因此有助于使大多数程序员清楚地了解您的代码。
回答by Ravindra babu
You can do whatever you think but it is good to follow the pattern for usability
of manageability
of code.
你可以做任何你的看法,但它是很好的遵循模式的usability
的manageability
代码。
In real life example, Light
would be an interface. You have different implementations of Light
like LEDLight
, TubeLight
在现实生活中,Light
将是一个界面。你有Light
类似的不同实现LEDLight
,TubeLight
If you execute the concrete command through Inovker
( RemoteControl
), you need not worry about changes in method name in Receiver
. switchOn() in Light can be changed to switchOnDevice() in future
. But it does not effect Invoker since ConcreteCommand
(LightsOnCommand
) will make relevant changes.
如果通过Inovker
( RemoteControl
)执行具体命令,则不必担心Receiver
. switchOn() in Light can be changed to switchOnDevice() in future
. 但它不会影响 Invoker,因为ConcreteCommand
( LightsOnCommand
) 会进行相关更改。
Assume the scenario, where you publish your interfaces to one service (Service A) and implementation in other services (Service B).
假设您将接口发布到一项服务(服务 A)并在其他服务(服务 B)中实现。
Now Service A should not know about changes in Receiver
.
现在服务 A 不应该知道Receiver
.
Invoker
provides loose coupling between Sender & Receiver of the message.
Invoker
提供消息的发送者和接收者之间的松散耦合。
Have a look at some more related SE questions:
看看一些更相关的 SE 问题:
Command Pattern seems needlessly complex (what am I failing to understand?)
回答by stdout
There're couple of benefits of Command pattern that I've experienced. But first of all, I would like to remind that as all other patterns this pattern is also to increase code readability and bring some common understanding to your (probably) shared code base.
我体验过命令模式有几个好处。但首先,我想提醒的是,与所有其他模式一样,这种模式也是为了提高代码可读性,并为您(可能)共享的代码库带来一些共同的理解。
I use this pattern to transition from a method-oriented interface to a command-oriented interface. That means, I'm encapsulating method calls into concrete commands along with necessary data. It makes the code more readable yes but more importantly I can treat methods as objects which lets you easily add/remove/modify commands without increasing the complexity of the code. So it makes it easy to manage as well easy to read.
我使用这种模式从面向方法的界面过渡到面向命令的界面。这意味着,我将方法调用与必要的数据一起封装到具体的命令中。它使代码更具可读性,是的,但更重要的是,我可以将方法视为对象,这样您就可以轻松添加/删除/修改命令,而不会增加代码的复杂性。因此,它既易于管理又易于阅读。
Secondly, since you've your methods as objects, you can store/queue them to execute them later. Or to cancel them even after you've executed them. This is where this pattern helps you to implement "Undo".
其次,由于您将方法作为对象,您可以存储/排队它们以便稍后执行它们。或者甚至在您执行它们之后取消它们。这是该模式帮助您实现“撤消”的地方。
Finally, command pattern also used to decouple command execution and commands themselves. It's like a waiter doesn't know about how to cook the orders that he received. He doesn't care. He doesn't have to know. If he'd know that, he'd be working as a cook as well. And if the restaurant owner wants to fire the waiter, he/she ends up with having no cook as well. Of course this definition is not special or related to command pattern only. That's explains why we need decoupling or dependency management in general.
最后,命令模式还用于解耦命令执行和命令本身。这就像服务员不知道如何烹饪他收到的订单。他不在乎。他不必知道。如果他知道这一点,他也会成为一名厨师。如果餐厅老板想解雇服务员,他/她最终也没有厨师。当然,这个定义并不特殊,也不是仅与命令模式有关。这就是为什么我们通常需要解耦或依赖管理的原因。
回答by som
Design patterns are basically developed to solve complex problem or in other words we can say that they are used to prevent your solution to become complex. They provide us the flexibility to add new functionalities in future without making much changes in our existing code. Open for extension, closed for modification.
设计模式基本上是为了解决复杂的问题而开发的,换句话说,我们可以说它们是用来防止您的解决方案变得复杂的。它们为我们提供了在未来添加新功能的灵活性,而无需对现有代码进行太多更改。对扩展开放,对修改关闭。
Command Pattern basically encapsulates a request as an object and thereby letting you parameterize other objects with different requests and support undoable operations.
命令模式基本上将请求封装为一个对象,从而让您可以使用不同的请求参数化其他对象并支持可撤销的操作。
When we see a remote control that is a best example of command pattern. In this case we have associated a concrete command to each button and that command has an information of the receiver to act upon.
当我们看到遥控器时,这是命令模式的最佳示例。在这种情况下,我们将一个具体的命令与每个按钮相关联,并且该命令具有要执行的接收器的信息。
In your example of switch case, suppose if you want to associate a button on remote control to fan instead of light then again you will have to change the existing code because you need to associate a button with a command. This is the essence of command pattern where it provides you a flexibility to associate any command with a button so that at run time you can change the functionality. Although in general TV remote you don't have this feasibility i.e. you can't make volume up button to work as volume down. But if you use any application to control your TV then you can assign any button any of the available command.
在您的开关盒示例中,假设您想将遥控器上的按钮与风扇而不是灯关联,那么您将不得不再次更改现有代码,因为您需要将按钮与命令关联。这是命令模式的本质,它为您提供了将任何命令与按钮相关联的灵活性,以便在运行时您可以更改功能。虽然在一般的电视遥控器中你没有这种可行性,即你不能让音量增大按钮作为音量减小。但是,如果您使用任何应用程序来控制您的电视,那么您可以为任何按钮分配任何可用命令。
Further, using command pattern you can have a set of commands for a particular functionality to achieve, i.e. macro command. Hence if you think at a broader level then this pattern can get you the flexibility to extend functionalities.
此外,使用命令模式,您可以拥有一组用于实现特定功能的命令,即宏命令。因此,如果您在更广泛的层面上思考,那么这种模式可以让您灵活地扩展功能。
Command pattern help us in decoupling invoker(remote control) and Receiver (Light,Fan,etc) with the help of command object(LightOnCommand, FanOffCommand, etc).
命令模式在命令对象(LightOnCommand、FanOffCommand 等)的帮助下帮助我们解耦调用者(远程控制)和接收者(Light、Fan 等)。