设计模式-命令模式
命令模式:将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或者记录请求日志,可撤销的操作。
行为的请求者 和 行为的执行者 解耦
典型例子:服务员(调用者 invoker)、厨师(接收者),菜单(命令)
命令模式的结构也很简单,先认识下面的三个角色:
invoker 调用命令的人
command、ConcreteCommand 命令接口和具体的命令
receiver 执行命令的人
来个类图:
命令的执行者是Receiver,所以命令类上有执行者。
客户端要做的事:
1:只需要对invoker进行发布命令
2:设置命令的接收者(其实这个可以对客户端也透明的)
下面用一个例子来解释命令模式。
1:项目经理接收客户的需求(命令)
2:工人(程序员)执行开发命令
3:命令有:开发产品A、开发产品B、进行开会
先来看命令接口:
这个命令定义了命令的执行者和命令名(为了方便我们知道调用了哪个命令)
/**
* 命令模式- 命令,这里是一个任务
*/
public abstract class ICommand {
/**
* 命令的执行者
*/
protected WorkerReceiver workerReceiver;
private String commandName;
/**
* 构造方法
*/
ICommand(WorkerReceiver workerReceiver,String commandName){
this.workerReceiver = workerReceiver;
this.commandName = commandName;
}
/**
* 定义一个执行方法
*/
abstract void execute();
public String getCommandName() {
return commandName;
}
public void setCommandName(String commandName) {
this.commandName = commandName;
}
}
具体的命令:
生产产品A的命令:
生产产品A需要的时间是10,执行命令时,让在其它线程上执行。
public class ProductACommand extends ICommand {
private int timeSecond = 10;//生产这个产品需要10秒
/**
* 构造方法
*
* @param workerReceiver
*/
ProductACommand(WorkerReceiver workerReceiver) {
super(workerReceiver,"生产产品A命令");
}
@Override
void execute() {
new Thread(()->{workerReceiver.work(timeSecond);}).start();
}
}
生产产品B的命令:
和生产产品A的命令相似
public class ProductBCommand extends ICommand {
private int timeSecond = 20;//生产这个产品需要20秒
/**
* 构造方法
*
* @param workerReceiver
*/
ProductBCommand(WorkerReceiver workerReceiver) {
super(workerReceiver,"生产产品B命令");
}
@Override
void execute() {
new Thread(()->{workerReceiver.work(timeSecond);}).start();
}
}
开会的命令:
开会命令,调用的接收者的是开会的方法。
/**
* 开会的命令
*/
public class MeetingCommand extends ICommand {
private int timeSecond = 15;//开会需要15秒
/**
* 构造方法
*
* @param workerReceiver
*/
MeetingCommand(WorkerReceiver workerReceiver) {
super(workerReceiver,"开会命令");
}
@Override
void execute() {
//另起线程
new Thread(()->{workerReceiver.meeting(timeSecond);}).start();
}
}
员工类(命令执行者):
有两个动作,一个是进行工作,另外一个是进行开会,对应上诉的工作和开会的命令。
有个状态 freeStatus,只有在空闲状态,才可以进行工作或开会。
/**
* 员工 - 命令模式的receiver 执行任务的人
* 这里其实可以把这个对象抽象化
*/
public class WorkerReceiver {
private boolean freeStatus = true;//是否在空闲状态
private String name;
WorkerReceiver(String name){
this.name = name;
}
/**
* 进行工作 需要加锁
*/
public synchronized void work(int second){
System.out.println("********接受者"+name+": 进行生产 需要"+second+"秒");
freeStatus = false;
try {
Thread.sleep(second * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("接受者"+name+" 结束生产 释放锁");
freeStatus=true;
}
/**
* 进行开会 需要加锁
*/
public synchronized void meeting(int second){
freeStatus = false;
System.out.println("*********接受者"+name+":进行会议 需要开"+second+"秒");
try {
Thread.sleep(second * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("接受者"+name+":结束会议 释放锁");
freeStatus = true;
}
//
public boolean isFreeStatus() {
return freeStatus;
}
public void setFreeStatus(boolean freeStatus) {
this.freeStatus = freeStatus;
}
}
研发经理类(命令调用者invoker)
初始化时默认添加4个 命令接收者(程序员)
对外提供三个动作: 1、发命令 2、加人 3、找到一个空闲的接收者(只有找到空闲的接收者,才能发指令)
import java.util.ArrayList;
import java.util.List;
/**
* 研发经理-命令模式的调用者
*/
public class ManagerInvoker {
private List<WorkerReceiver> workerReceivers = new ArrayList<>();
public ManagerInvoker() {
System.out.println("初始化执行者");
for (int i=0;i<4;i++){
workerReceivers.add(new WorkerReceiver(i+""));
}
}
/**
* 设置命令
* @param iCommand 命令
*/
public void setCommand(ICommand iCommand){
iCommand.execute();
}
/**
* 添加接收者
*/
public void addReceiver(){
System.out.println("添加工作者成功");
workerReceivers.add(new WorkerReceiver(workerReceivers.size()+""));
}
/**
* 获取空闲的工作者
*/
public WorkerReceiver getFreeReceiver(){
boolean workFlag = false;
for (int i=0;i<workerReceivers.size();i++){
if(workerReceivers.get(i).isFreeStatus()){
workFlag = true;
System.out.println("worker "+ i +" 正在空闲");
return workerReceivers.get(i);
}
}
if(!workFlag){
System.out.println("没有工人是空闲的 这里可以弄个等待队列,这里暂时不弄");
}
return null;
}
}
测试方法:
public static void main(String[] args) {
ManagerInvoker managerInvoker = new ManagerInvoker();
Scanner scanner = new Scanner(System.in);
String text = null;
System.out.println("请输入命令 1 生产产品A ; 2 生产产品B ; 3 进行开会; 0 添加工人");
while ((text = scanner.next())!= null){
switch (text){
case "1":{
WorkerReceiver workerReceiver = managerInvoker.getFreeReceiver();
if(workerReceiver != null){
managerInvoker.setCommand(new ProductACommand(workerReceiver));
}
}
break;
case "2":{
WorkerReceiver workerReceiver = managerInvoker.getFreeReceiver();
if(workerReceiver != null){
managerInvoker.setCommand(new ProductBCommand(workerReceiver));
}
}
break;
case "3":{
WorkerReceiver workerReceiver = managerInvoker.getFreeReceiver();
if(workerReceiver != null){
managerInvoker.setCommand(new MeetingCommand(workerReceiver));
}
}
break;
case "0":
managerInvoker.addReceiver();
break;
}
System.out.println("请输入命令 1 生产产品A ; 2 生产产品B ; 3 进行开会; 0 添加工人");
}
}
测试结果:
初始化执行者
请输入命令 1 生产产品A ; 2 生产产品B ; 3 进行开会; 0 添加工人
2
worker 0 正在空闲
请输入命令 1 生产产品A ; 2 生产产品B ; 3 进行开会; 0 添加工人
********接受者0: 进行生产 需要20秒
2
worker 1 正在空闲
请输入命令 1 生产产品A ; 2 生产产品B ; 3 进行开会; 0 添加工人
********接受者1: 进行生产 需要20秒
3
worker 2 正在空闲
请输入命令 1 生产产品A ; 2 生产产品B ; 3 进行开会; 0 添加工人
*********接受者2:进行会议 需要开15秒
1
worker 3 正在空闲
请输入命令 1 生产产品A ; 2 生产产品B ; 3 进行开会; 0 添加工人
********接受者3: 进行生产 需要10秒
2
没有工人是空闲的 这里可以弄个等待队列,这里暂时不弄
请输入命令 1 生产产品A ; 2 生产产品B ; 3 进行开会; 0 添加工人
0
添加工作者成功
请输入命令 1 生产产品A ; 2 生产产品B ; 3 进行开会; 0 添加工人
2
worker 4 正在空闲
请输入命令 1 生产产品A ; 2 生产产品B ; 3 进行开会; 0 添加工人
********接受者4: 进行生产 需要20秒
这个例子模拟了我们程序开发过程的一个现象:
大Boss对研发经理说:你们今天需要给我 把XXX 做完
产品经理说:XXX分为 1,2,3 部分,每个部分完成需要 xxx 时间,我们的员工现在都没有空。
大Boss说:让大家加加班呀
产品经理说:我们现在每天都在加班了,你看看我们的周报。。。。
大Boss看看周报,发现员工们真的很晚才下班,咦,怎么这么多人在开会,发费好多时间呢
大Boss说:这会议。。。(说不下去了)
产品经理说:这些会议都是您下发的通知,说一定要全员到齐,不然扣工资的,但是很多会议内容都是无意义的。
大Boss也尴尬了,说:以后提高会议效率,只让相关的人去听。
产品经理说:嗯嗯,是的。但是我们也就只有这几个人,真的没办法在这个月完成那么多工作量。
大Boss说:加人!!
产品经理笑了。
看看,这个命令模式,帮助产品经理说服Boss加人和减少会议次数,还真是有用的模式呀。但是真正能为低下员工扛的产品经理还是比较少的,如果你遇到了,那么请你珍惜。
说远了,设计模式没有固定的形态,放心大胆的去用,被坑过才会更爱。