高性能网站建设进阶,建设银行网站的目的是什么意思,可以下载各种软件的网站,邯郸建设信息网站文章目录 一、什么是命令模式1、命令模式使用场景2、命令模式的主要角色3、命令模式优缺点4、命令模式注意事项及细节 二、使用示例1、命令模式的一般写法2、播放器功能案例3、遥控器案例 三、源码中的命令模式1、Thread 一、什么是命令模式
命令模式#xff08;Command Patt… 文章目录 一、什么是命令模式1、命令模式使用场景2、命令模式的主要角色3、命令模式优缺点4、命令模式注意事项及细节 二、使用示例1、命令模式的一般写法2、播放器功能案例3、遥控器案例 三、源码中的命令模式1、Thread 一、什么是命令模式
命令模式Command Pattern是对命令的封装每一个命令都是一个操作请求的一方发出请求要求执行一个操作接收的一方收到请求并执行操作。命令模式解耦了请求方和接收方请求方只需请求执行命令不用关心命令是怎样被接受怎样被操作以及是否被执行等。命令模式属于行为型模式。
原文将一个请求封装成医德对象从而让你使用不同的请求把客户端参数化对请求排队或者记录请求日志可以提供命令的撤销和恢复功能。
在软件系统中行为请求者与行为实现者通常是一种紧耦合关系因为这样的实现简单明了。但紧耦合关系缺乏扩展性在某些场合中当需要为行为进行记录撤销或重做等处理时只能修改源码。而命令模式通过为请求与实现间引入了一个抽象命令接口解耦了请求与实现并且中间件是抽象的它可以有不同的子类实现因此其具备扩展性。所以命令模式的本质是解耦命令请求与处理。
1、命令模式使用场景
当系统的某项操作具备命令语义时且命令实现不稳定变化那么可以通过命令模式解耦请求与实现利用抽象命令接口使请求方代码架构稳定封装接收方具体命令实现细节。接收方与抽象命令接口呈现弱耦合内部方法无需一致具备良好的扩展性。命令模式适用于以下应用场景
系统需要将请求调用者和请求接收者解耦使得调用者和接收者不直接交互。现实语义中具备“命令”的操作如命令菜单、shell命令等。系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作。系统需要在不同的时间指定请求、将请求排队和执行请求。需要支持命令宏即命令组合操作。
2、命令模式的主要角色 命令模式包含以下主要角色
抽象命令类Command角色 定义命令的接口声明执行的方法。具体命令Concrete Command角色具体的命令实现命令接口通常会持有接收者并调用接收者的功能来完成命令要执行的操作。实现者/接收者Receiver角色 接收者真正执行命令的对象。任何类都可能成为一个接收者只要它能够实现命令要求实现的相应功能。调用者/请求者Invoker角色 要求命令对象执行请求通常会持有命令对象可以持有很多的命令对象。这个是客户端真正触发命令并要求命令执行相应操作的地方也就是说相当于使用命令对象的入口。
从命令模式的UML类图中可以看出Command的出现就是作为Receiver和Invoker的中间件解耦了彼此。而之所以引入Command中间件主要有以下两方面的原因 1解耦请求与实现即解耦了Invoker和Receiver因为在UML类图中Invoker是一个具体的实现等待接收客户端传入命令即Invoker与客户端耦合Invoker处于业务逻辑区域应当是一个稳定的结构。而Receiver是属于业务功能模块是经常变动的如果没有Command则Invoker紧耦合Receiver一个稳定的结构依赖了一个不稳定的结构就会导致整个结构都不稳定了。这也就是Command引入的原因不仅仅是解耦请求与实现同时稳定Invoker依赖文档Command结构还是稳定的。 2扩展性增强扩展性体现在两个方面① Receiver属于底层细节可以通过更换不同的Receiver达到不同的细节实现② Command接口本身就是抽象的本身就具备扩展性而且由于命令对象本身就具备抽象如果结合装饰器模式功能扩展简直如鱼得水。 注在一个系统中不同的命令对应不同的请求也就是说无法把请求抽象化因此命令模式中的Receiver是具体实现但是如果在某一个模块中可以对Receiver进行抽象其实这就变相使用到了桥接模式Command类具备两个变化的维度Command和Receiver这样子的扩展性会更加优秀。 3、命令模式优缺点
优点
降低系统的耦合度。命令模式能将调用操作的对象与实现该操作的对象解耦。增加或删除命令非常方便。采用命令模式增加与删除命令不会影响其他类它满足“开闭原则”对扩展比较灵活。可以实现宏命令。命令模式可以与组合模式结合将多个命令装配成一个组合命令即宏命令。方便实现 Undo 和 Redo 操作。命令模式可以与备忘录模式结合实现命令的撤销与恢复。可以在现有命令的基础上增加额外功能比如日志记录等等结合装饰器模式效果更佳。
缺点
使用命令模式可能会导致某些系统有过多的具体命令类。命令模式的结果其实就是接收方的执行结果但是为了以命令的形式进行架构解耦请求与实现引入了额外类型结构引入了请求方与抽象命令接口增加了理解上的困难不过这也是设计模式带来的一个通病抽象必然会引入额外类型抽象肯定比紧密难理解。使用频率低、理解难度大只在非常特定的应用场景下才会用到。
4、命令模式注意事项及细节
将发起请求的对象与执行请求的对象解耦。发起请求的对象是调用者调用者只要调用命令对象execute()方法就可以让接收者工作而不必知道具体的接收者对象是谁、是如何实现的命令对象会负责让接收者执行请求的动作也就是说“请求发起者”和“请求执行者”之间的解耦是通过命令对象实现的命令对象起到了纽带桥梁的作用。容易设计一个命令队列只要把命令对象放到队列就可以多线程的执行命令。容易实现对请求的撤销和重做。空命令也是一种设计模式它为我们省去了判空的操作。可以使用接口适配器模式将Command接口适配为一个空类。
二、使用示例
1、命令模式的一般写法
//抽象命令接口
public interface ICommand {void execute();
}//具体命令
public class ConcreteCommand implements ICommand {// 直接创建接收者不暴露给客户端private Receiver mReceiver;public ConcreteCommand(Receiver mReceiver) {this.mReceiver mReceiver;}public void execute() {this.mReceiver.action();}
}//请求者
public class Invoker {private ICommand mCmd;public Invoker(ICommand cmd) {this.mCmd cmd;}public void action() {this.mCmd.execute();}
}//接收者
public class Receiver {public void action() {System.out.println(执行具体操作);}
}public class Test {public static void main(String[] args) {ICommand cmd new ConcreteCommand(new Receiver());Invoker invoker new Invoker(cmd);invoker.action();}
}
2、播放器功能案例
假如我们自己开发一个播放器播放器有播放功能、拖拽进度条功能、停止播放功能、暂停播放功能我们自己去操作播放器的时候并不是直接调用播放器的方法而是通过一个控制条去传达指令给播放器内核那么具体传达什么指令会被封装为一个个的按钮。那么每个按钮就相当于是对一条命令的封装。用控制条实现了用户发送指令与播放器内核接收指令的解耦。
// Receiver角色播放器内核GPlayer
public class GPlayer {public void play(){System.out.println(正常播放);}public void speed(){System.out.println(拖动进度条);}public void stop(){System.out.println(停止播放);}public void pause(){System.out.println(暂停播放);}
}
// 抽象命令Command角色命令接口
public interface IAction {void execute();
}
// 具体命令角色
public class StopAction implements IAction {private GPlayer gplayer;public StopAction(GPlayer gplayer) {this.gplayer gplayer;}public void execute() {gplayer.stop();}
}public class PauseAction implements IAction {private GPlayer gplayer;public PauseAction(GPlayer gplayer) {this.gplayer gplayer;}public void execute() {gplayer.pause();}
}public class PlayAction implements IAction {private GPlayer gplayer;public PlayAction(GPlayer gplayer) {this.gplayer gplayer;}public void execute() {gplayer.play();}
}public class SpeedAction implements IAction {private GPlayer gplayer;public SpeedAction(GPlayer gplayer) {this.gplayer gplayer;}public void execute() {gplayer.speed();}
}
// Invoker 角色
public class Controller {private ListIAction actions new ArrayListIAction();public void addAction(IAction action){actions.add(action);}public void execute(IAction action){action.execute();}public void executes(){for (IAction action:actions) {action.execute();}actions.clear();}
}public class Test {public static void main(String[] args) {GPlayer player new GPlayer();Controller controller new Controller();controller.execute(new PlayAction(player));controller.addAction(new PauseAction(player));controller.addAction(new PlayAction(player));controller.addAction(new StopAction(player));controller.addAction(new SpeedAction(player));controller.executes();}
}3、遥控器案例
买了一套智能家电有点灯、风扇、冰箱、洗衣机、电视机我们只需要在手机上安装app就可以控制这些家电。
//创建命令接口
public interface Command {//执行动作(操作)public void execute();//撤销动作(操作)public void undo();
}/*** 没有任何命令即空执行: 用于初始化每个按钮, 当调用空命令时对象什么都不做* 其实这样是一种设计模式, 可以省掉对空判断*/
public class NoCommand implements Command {Overridepublic void execute() {}Overridepublic void undo() {}
}
// 电灯Receiver
public class LightReceiver {public void on() {System.out.println( 电灯打开了.. );}public void off() {System.out.println( 电灯关闭了.. );}
}
// 电灯具体命令类
public class LightOffCommand implements Command {// 聚合LightReceiverLightReceiver light;// 构造器public LightOffCommand(LightReceiver light) {super();this.light light;}Overridepublic void execute() {// 调用接收者的方法light.off();}Overridepublic void undo() {// 调用接收者的方法light.on();}
}
// 电灯具体命令类
public class LightOnCommand implements Command {//聚合LightReceiverLightReceiver light;//构造器public LightOnCommand(LightReceiver light) {super();this.light light;}Overridepublic void execute() {//调用接收者的方法light.on();}Overridepublic void undo() {//调用接收者的方法light.off();}
}
// 电视机Receiver
public class TVReceiver {public void on() {System.out.println( 电视机打开了.. );}public void off() {System.out.println( 电视机关闭了.. );}
}
// 电视机具体命令
public class TVOnCommand implements Command {// 聚合TVReceiverTVReceiver tv;// 构造器public TVOnCommand(TVReceiver tv) {super();this.tv tv;}Overridepublic void execute() {// 调用接收者的方法tv.on();}Overridepublic void undo() {// 调用接收者的方法tv.off();}
}public class TVOffCommand implements Command {// 聚合TVReceiverTVReceiver tv;// 构造器public TVOffCommand(TVReceiver tv) {super();this.tv tv;}Overridepublic void execute() {// 调用接收者的方法tv.off();}Overridepublic void undo() {// 调用接收者的方法tv.on();}
}
// 控制器Invoker角色
public class RemoteController {// 开 按钮的命令数组Command[] onCommands;Command[] offCommands;// 执行撤销的命令Command undoCommand;// 构造器完成对按钮初始化public RemoteController() {onCommands new Command[5];offCommands new Command[5];for (int i 0; i 5; i) {onCommands[i] new NoCommand();offCommands[i] new NoCommand();}}// 给我们的按钮设置你需要的命令public void setCommand(int no, Command onCommand, Command offCommand) {onCommands[no] onCommand;offCommands[no] offCommand;}// 按下开按钮public void onButtonWasPushed(int no) { // no 0// 找到你按下的开的按钮 并调用对应方法onCommands[no].execute();// 记录这次的操作用于撤销undoCommand onCommands[no];}// 按下开按钮public void offButtonWasPushed(int no) { // no 0// 找到你按下的关的按钮 并调用对应方法offCommands[no].execute();// 记录这次的操作用于撤销undoCommand offCommands[no];}// 按下撤销按钮public void undoButtonWasPushed() {undoCommand.undo();}}
// 客户端
public class Client {public static void main(String[] args) {//使用命令设计模式完成通过遥控器对电灯的操作//创建电灯的对象(接受者)LightReceiver lightReceiver new LightReceiver();//创建电灯相关的开关命令LightOnCommand lightOnCommand new LightOnCommand(lightReceiver);LightOffCommand lightOffCommand new LightOffCommand(lightReceiver);//需要一个遥控器RemoteController remoteController new RemoteController();//给我们的遥控器设置命令, 比如 no 0 是电灯的开和关的操作remoteController.setCommand(0, lightOnCommand, lightOffCommand);System.out.println(--------按下灯的开按钮-----------);remoteController.onButtonWasPushed(0);System.out.println(--------按下灯的关按钮-----------);remoteController.offButtonWasPushed(0);System.out.println(--------按下撤销按钮-----------);remoteController.undoButtonWasPushed();System.out.println(使用遥控器操作电视机);TVReceiver tvReceiver new TVReceiver();TVOffCommand tvOffCommand new TVOffCommand(tvReceiver);TVOnCommand tvOnCommand new TVOnCommand(tvReceiver);//给我们的遥控器设置命令, 比如 no 1 是电视机的开和关的操作remoteController.setCommand(1, tvOnCommand, tvOffCommand);System.out.println(--------按下电视机的开按钮-----------);remoteController.onButtonWasPushed(1);System.out.println(--------按下电视机的关按钮-----------);remoteController.offButtonWasPushed(1);System.out.println(--------按下撤销按钮-----------);remoteController.undoButtonWasPushed();}
}三、源码中的命令模式
1、Thread
Runable是一个典型命令模式Runnable担当命令的角色Thread充当的是调用者start方法就是其执行方法。
//命令接口(抽象命令角色)
public interface Runnable {public abstract void run();
}
//调用者
public class Thread implements Runnable {private Runnable target;public synchronized void start() {if (threadStatus ! 0)throw new IllegalThreadStateException();group.add(this);boolean started false;try {start0();started true;} finally {try {if (!started) {group.threadStartFailed(this);}} catch (Throwable ignore) {}}}private native void start0();
}会调用一个native方法start0(),调用系统方法开启一个线程。而接收者是对程序员开放的可以自己定义接收者。
/**
* jdk Runnable 命令模式
* TurnOffThread 属于具体
*/
public class TurnOffThread implements Runnable{private Receiver receiver;public TurnOffThread(Receiver receiver) {this.receiver receiver;}public void run() {receiver.turnOFF();}
}/**
* 测试类
*/
public class Demo {public static void main(String[] args) {Receiver receiver new Receiver();TurnOffThread turnOffThread new TurnOffThread(receiver);Thread thread new Thread(turnOffThread);thread.start();}
}实际上调用线程的start方法之后就有资格去抢CPU资源而不需要我们自己编写获得CPU资源的逻辑。而线程抢到CPU资源后就会执行run方法中的内容用Runnable接口把用户请求和CPU执行进行了解耦。