先来一张图看一下命令模式的总体流程。
image.png
调用者发送一条命令给接收者,然后接收者接收到命令后执行.
该模式要解决的问题是,使调用者和接收者通过命令解耦,如需要扩展功能,一般只需要添加命令,尽量不去修改接收者。(开放封闭原则)
UML:
image.png
代码模版
public interface Command {
void execute();
}
public class ConcreteCommand implements Command{
private Receiver receiver;
public ConcreteCommand(Receiver receiver) {
this.receiver = receiver;
}
@Override
public void execute() {
receiver.action();
}
}
public class Receiver {
/**
* 真正执行具体命令逻辑的方法
*/
public void action(){
System.out.println("执行具体操作");
}
}
public class Invoker {
private Command command;
public Invoker(Command command){
this.command = command;
}
public void action(){
command.execute();
}
}
public class Client {
public static void main(String args[]){
Receiver receiver = new Receiver();
Command command = new ConcreteCommand(receiver);
Invoker invoker = new Invoker(command);
invoker.action();
}
}
Receiver:接收者角色
Command:命令角色
ConcreteCommand :具体命令角色
Invoker:请求者角色
Client:客户端角色
我们通过Invoker调用ConcreteCommand去通知Receiver执行,这样就实现了Invoker和Receiver之间的解耦操作。
下面具体来一个需求实战一下,拿俄罗斯方块这款游戏来说,它有左右移动和变换,快速下落四个按钮,这个需求可以用命令模式来实现,调用者通过左右移动和变换,快速下落四个命令让机器执行左右移动和变换,快速下落的动作。
具体代码实现如下:
public class TetrisMachine {
/**
* 真正处理"向左"操作的逻辑代码
*/
public void toLeft(){
System.out.println("向左");
}
/**
* 真正处理"向右"操作的逻辑代码
*/
public void toRight(){
System.out.println("向右");
}
/**
* 真正处理"快速落下"操作的逻辑代码
*/
public void fastToBottom(){
System.out.println("快速落下");
}
/**
* 真正处理"改变形状"操作的逻辑代码
*/
public void transform(){
System.out.println("改变形状");
}
}
public interface Command {
void execute();
}
public class LeftCommand implements Command{
TetrisMachine tetrisMachine;
public LeftCommand(TetrisMachine tetrisMachine) {
this.tetrisMachine = tetrisMachine;
}
@Override
public void execute() {
tetrisMachine.toLeft();
}
}
public class RightCommand implements Command {
TetrisMachine tetrisMachine;
public RightCommand(TetrisMachine tetrisMachine) {
this.tetrisMachine = tetrisMachine;
}
@Override
public void execute() {
tetrisMachine.toRight();
}
}
public class FallCommand implements Command{
TetrisMachine tetrisMachine;
public FallCommand(TetrisMachine tetrisMachine) {
this.tetrisMachine = tetrisMachine;
}
@Override
public void execute() {
tetrisMachine.fastToBottom();
}
}
public class TransformCommand implements Command{
private TetrisMachine machine;
public TransformCommand(TetrisMachine machine) {
this.machine = machine;
}
@Override
public void execute() {
machine.transform();
}
}
public class Buttons {
private LeftCommand leftCommand;
private RightCommand rightCommand;
private FallCommand fallCommand;
private TransformCommand transformCommand;
public void setLeftCommand(LeftCommand leftCommand) {
this.leftCommand = leftCommand;
}
public void setRightCommand(RightCommand rightCommand) {
this.rightCommand = rightCommand;
}
public void setFallCommand(FallCommand fallCommand) {
this.fallCommand = fallCommand;
}
public void setTransformCommand(TransformCommand transformCommand) {
this.transformCommand = transformCommand;
}
public void toLeft(){
leftCommand.execute();
}
public void toRight(){
rightCommand.execute();
}
public void fall(){
fallCommand.execute();
}
public void transform(){
transformCommand.execute();
}
}
public class Player {
public static void main(String[] args){
TetrisMachine tetrisMachine = new TetrisMachine();
LeftCommand leftCommand = new LeftCommand(tetrisMachine);
RightCommand rightCommand = new RightCommand(tetrisMachine);
FallCommand fallCommand = new FallCommand(tetrisMachine);
TransformCommand transformCommand = new TransformCommand(tetrisMachine);
Buttons buttons = new Buttons();
buttons.setLeftCommand(leftCommand);
buttons.setRightCommand(rightCommand);
buttons.setFallCommand(fallCommand);
buttons.setTransformCommand(transformCommand);
buttons.toLeft();
buttons.toRight();
buttons.fall();
buttons.transform();
}
}
TetrisMachine : 游戏机
Command :命令抽象
LeftCommand :左移动命令
RightCommand :右移动命令
FallCommand :快速下落命令
TransformCommand :变换命令
Buttons :调用者角色
Player :客户端角色
这里可能会有个疑问 , 就是在Player直接调用TetrisMachine是否会更简洁,我们完全可以通过直接调用TetrisMachine中函数减少代码量,如下:
TetrisMachine tetrisMachine = new TetrisMachine();
tetrisMachine.toLeft();
tetrisMachine.toRight();
tetrisMachine.fastToBottom();
tetrisMachine.transform();
这样的确减少了代码,如果说需求简单,逻辑简单,仅仅只是调用toLeft,toRight这些单独的函数,我建议完全没必要用命令行模式实现。但是如果说需求并不是单单调用一个toLeft这种情况呢,比如
- 说你需要去做一个命令的日志记录,这个时候你是在调用者去记录日志呢,还是在接收者去接收日志呢?显然都不是好的选择,违背了开发封闭原则,最好的方法是使用命令模式,在命令中去记录日志。
- 比如说我现在有一个教程演示功能,每一个步骤对应一连串操作,可能toLeft,toRight,toRight,transform,fastToBottom这种,那么现在最好的方法是把这个这些函数封装成一个命令给调用者调用,而不是直接在调用者里面去实现这一连串操作
当然命令模式的好处还不止上面说这些,例如你可以去在命令中封装开关,设置权限等,这都是命令模式带来的优势.
网友评论