設計模式-命令模式
命令模式:將一個請求封裝爲一個對象,從而使你可用不同的請求對客戶進行參數化;對請求排隊或者記錄請求日誌,可撤銷的操作。
行爲的請求者 和 行爲的執行者 解耦
典型例子:服務員(調用者 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加人和減少會議次數,還真是有用的模式呀。但是真正能爲低下員工扛的產品經理還是比較少的,如果你遇到了,那麼請你珍惜。
說遠了,設計模式沒有固定的形態,放心大膽的去用,被坑過纔會更愛。