命令模式
命令模式:將一個(gè)請求封裝為一個(gè)對象,從而使你可用不同的請求對客戶進(jìn)行參數(shù)化。可以對請求進(jìn)行排隊(duì)或者記錄請求日志,以及支持可撤銷的操作。命令模式通過這種封裝的方式實(shí)現(xiàn)將請求動(dòng)作者和動(dòng)作執(zhí)行者解耦。
控制小車移動(dòng)
接下來創(chuàng)建一個(gè)需求來用命令模式實(shí)現(xiàn)。
我們有一個(gè)Car對象,它有向前移動(dòng)的方法forward()和向后移動(dòng)的方法backward()的方法?,F(xiàn)在我們要控制小車的移動(dòng),要怎么做呢?
緊耦合設(shè)計(jì)
我們先來看看不使用命令模式的緊耦合設(shè)計(jì)
Car類:
public class Car {
/**
* 向前移動(dòng)
*/
public void forward(){
System.out.println("向前移動(dòng)");
}
/**
* 向后移動(dòng)
*/
public void backward(){
System.out.println("向后移動(dòng)");
}
}
Client類:
/**
* 緊耦合客戶端類
*/
public class Client {
public static void main(String[] args) {
//創(chuàng)建小車對象
Car car = new Car();
//小車向前移動(dòng)
car.forward();
//小車向前移動(dòng)
car.forward();
//小車向后移動(dòng)
car.backward();
}
}
向前移動(dòng)
向前移動(dòng)
向后移動(dòng)
我們可以看到客戶端(Client)與動(dòng)作執(zhí)行者(Car)是緊密耦合的。
松耦合命令模式設(shè)計(jì)
既然想要將請求動(dòng)作者和動(dòng)作執(zhí)行者進(jìn)行解耦,那么我們就設(shè)計(jì)一個(gè)遙控器(RemoteControl)來幫我們傳達(dá)命令,控制小車移動(dòng)。
Car類不變
public class Car {
/**
* 向前移動(dòng)
*/
public void forward(){
System.out.println("向前移動(dòng)");
}
/**
* 向后移動(dòng)
*/
public void backward(){
System.out.println("向后移動(dòng)");
}
}
Command類
/**
* 命令接口 所以命令類都要實(shí)現(xiàn)此接口
*/
public interface Command {
void execute();
}
ForwardCommand類
/**
* 向前移動(dòng)的命令類
*/
public class ForwardCommand implements Command {
private Car car;
/**
* 接受操作執(zhí)行者
* @param car
*/
public ForwardCommand(Car car){
this.car = car;
}
public void execute() {
car.forward();
}
}
BackwardCommand類
/**
* 向后移動(dòng)的命令類
*/
public class BackwardCommand implements Command {
private Car car;
/**
* 接受操作執(zhí)行者
* @param car
*/
public BackwardCommand(Car car){
this.car = car;
}
public void execute() {
car.backward();
}
}
RemoteControl類
/**
* 遙控器類
*/
public class RemoteControl {
private Command command;
/**
* 設(shè)置控制命令
* @param command
*/
public void setCommand(Command command){
this.command = command;
}
/**
* 執(zhí)行命令
*/
public void executeCommand(){
command.execute();
}
}
Client類
public class Client {
public static void main(String[] args) {
Car car = new Car();
ForwardCommand forwardCommand = new ForwardCommand(car);
BackwardCommand backwardCommand = new BackwardCommand(car);
RemoteControl control = new RemoteControl();
//設(shè)置向前的命令
control.setCommand(forwardCommand);
//執(zhí)行命令
control.executeCommand();
control.setCommand(backwardCommand);
control.executeCommand();
}
}
向前移動(dòng)
向后移動(dòng)
現(xiàn)在如果我們想讓小車移動(dòng),那么我們就只需要給遙控器傳達(dá)移動(dòng)的命令就行了。小車接收到命令就會(huì)去執(zhí)行對應(yīng)的動(dòng)作。
添加命令隊(duì)列
我們可以把小車需要執(zhí)行的命令一次性的添加進(jìn)去,形成一個(gè)隊(duì)列統(tǒng)一執(zhí)行。
我們需要對代碼稍微調(diào)整一下
RemoteControl類
/**
* 遙控器類
*/
public class RemoteControl {
private List<Command> commands = new ArrayList<Command>();
/**
* 設(shè)置控制命令
* @param command
*/
public void setCommand(Command command){
commands.add(command);
}
/**
* 執(zhí)行命令
*/
public void executeCommand(){
for (Command command : commands) {
command.execute();
}
}
}
遙控器的命令屬性我們采用集合來接受,執(zhí)行時(shí)候循環(huán)調(diào)用command的execute方法。
Client類
public class Client {
public static void main(String[] args) {
Car car = new Car();
ForwardCommand forwardCommand = new ForwardCommand(car);
BackwardCommand backwardCommand = new BackwardCommand(car);
RemoteControl control = new RemoteControl();
//設(shè)置向前和向后的命令
control.setCommand(forwardCommand);
control.setCommand(backwardCommand);
//執(zhí)行命令
control.executeCommand();
}
}
Client這邊只要添加多次命令統(tǒng)一執(zhí)行就好了。
添加命令執(zhí)行控制
命令模式可以選擇命令是否執(zhí)行
舉個(gè)例子,雖然我向遙控器傳遞了向后移動(dòng)的命令,但是我們有具體的校驗(yàn)邏輯決定是否執(zhí)行后退命令。
/**
* 遙控器類
*/
public class RemoteControl {
private List<Command> commands = new ArrayList<Command>();
/**
* 設(shè)置控制命令
* @param command
*/
public void setCommand(Command command){
if(check(command)){
commands.add(command);
}
}
/**
* 執(zhí)行命令
*/
public void executeCommand(){
for (Command command : commands) {
command.execute();
}
}
/**
* 校驗(yàn)方法
* @return
*/
private boolean check(Command command){
//如果是后退命令則忽略
return !(command instanceof BackwardCommand);
}
}
UML

總結(jié)
我們可以看到命令模式達(dá)到了將請求動(dòng)作者和動(dòng)作執(zhí)行者進(jìn)行解耦的效果。這樣我們可以比較方便的設(shè)計(jì)一個(gè)命令隊(duì)列,也可以對請求是否執(zhí)行進(jìn)行控制,比如還可以對請求進(jìn)行撤銷、重做操作、記錄執(zhí)行日志等。
個(gè)人博客:https://www.zhaojun.ink