java-設計模式-命令模式

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

說遠了,設計模式沒有固定的形態,放心大膽的去用,被坑過纔會更愛。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章