Android 架構(gòu)師之路11 設(shè)計模式之命令模式

Android 架構(gòu)師之路 目錄

1雕欺、命令模式概念

1.1 介紹

命令模式(Command Pattern)是一種數(shù)據(jù)驅(qū)動的設(shè)計模式,它屬于行為型模式棉姐。請求以命令的形式包裹在對象中屠列,并傳給調(diào)用對象。調(diào)用對象尋找可以處理該命令的合適的對象伞矩,并把該命令傳給相應(yīng)的對象笛洛,該對象執(zhí)行命令。

1.2 定義

將一個請求封裝為一個對象乃坤,從而使你可用不同的請求對客戶進行參數(shù)化苛让,對請求排隊或記錄請求日志沟蔑。以及支持可撤銷的操作。

1.3 使用場景
  • 系統(tǒng)需要將請求調(diào)用者和請求接收者解耦蝌诡,使得調(diào)用者和接收者不直接交互疟游。
  • 系統(tǒng)需要在不同的時間指定請求荔睹、將請求排隊(如:線程池+工作隊列)和執(zhí)行請求刹碾。
  • 系統(tǒng)需要支持命令的撤銷(Undo)操作和恢復(fù)(Redo)操作悲没。
  • 系統(tǒng)需要將一組操作組合在一起剃袍,即支持宏命令嘲恍。

2俗他、命令模式UML類圖

命令模式UML類圖
  • Command(抽象命令類):抽象出命令對象搁胆,可以根據(jù)不同的命令類型例隆。寫出不同的實現(xiàn)類

  • ConcreteCommand(具體命令類):實現(xiàn)了抽象命令對象的具體實現(xiàn)

  • Invoker(調(diào)用者/請求者):請求的發(fā)送者甥捺,它通過命令對象來執(zhí)行請求。一個調(diào)用者并不需要在設(shè)計時確定其接收者镀层,因此它只與抽象命令來之間存在關(guān)聯(lián)镰禾。在程序運行時,將調(diào)用命令對象的execute() 唱逢,間接調(diào)用接收者的相關(guān)操作吴侦。

  • Receiver(接收者):接收者執(zhí)行與請求相關(guān)的操作,真正執(zhí)行命令的對象坞古。具體實現(xiàn)對請求的業(yè)務(wù)處理备韧。未抽象前,實際執(zhí)行操作內(nèi)容的對象痪枫。

  • Client(客戶端):在客戶類中需要創(chuàng)建調(diào)用者對象织堂,具體命令類對象,在創(chuàng)建具體命令對象時指定對應(yīng)的接收者奶陈。發(fā)送者和接收者之間沒有之間關(guān)系,都通過命令對象來調(diào)用易阳。

3、命令模式代碼實現(xiàn)

俄羅斯方塊游戲
向下方塊吃粒、向右方塊潦俺、向左方塊...,每一個方向都是一個命令

Command:
public interface ICommand extends Serializable {
    void execute();
}
ConcreteCommand:
public class LeftCommand implements ICommand {
    private Receiver receiver;
    public LeftCommand(Receiver receiver) {
        this.receiver = receiver;
    }
    @Override
    public void execute() {
        /**
         * 執(zhí)行之前干一些事情
         *  例如 存檔
         */
        this.receiver.onLeft();
    }
}


public class RightCommand implements ICommand {
    private Receiver receiver;
    public RightCommand(Receiver receiver) {
        this.receiver = receiver;
    }
    @Override
    public void execute() {
        this.receiver.onRight();
    }

}



public class BottomCommand implements ICommand {
    private Receiver receiver;
    public BottomCommand(Receiver receiver) {
        this.receiver = receiver;
    }
    @Override
    public void execute() {
        this.receiver.onBottom();
    }
}


public class TransfromCommand implements ICommand {
    private Receiver receiver;

    public TransfromCommand(Receiver receiver) {
        this.receiver = receiver;
    }

    @Override
    public void execute() {
        this.receiver.onTransformation();
    }


}
Invoker:
public class Invoker {
    private ICommand leftCommand;
    private ICommand rightCommand;
    private ICommand bottomCommand;
    private ICommand transfromCommand;
    private List<ICommand> commandList = new ArrayList<>();

    public Invoker() {
    }

    public Invoker(ICommand leftCommand, ICommand rightCommand, ICommand bottomCommand, ICommand transfromCommand) {
        this.leftCommand = leftCommand;
        this.rightCommand = rightCommand;
        this.bottomCommand = bottomCommand;
        this.transfromCommand = transfromCommand;
    }

    public void toLeft() {
        this.leftCommand.execute();
        commandList.add(leftCommand);
    }

    public void toRight() {
        this.rightCommand.execute();
        commandList.add(rightCommand);
    }

    public void toBottom() {
        this.bottomCommand.execute();
        commandList.add(bottomCommand);
    }

    public void toTransfrom() {
        this.transfromCommand.execute();
        commandList.add(transfromCommand);
    }

    /**
     * 回退
     */
    public void fallback() {
        if (commandList.size() > 0) {
            commandList.remove(commandList.size() - 1);
        }
    }

    /**
     * 存檔
     */
    public void saveArchive() {
        Utils.serializable("gameOperation", commandList);
    }

    /**
     * 讀檔
     */
    public void loadArchive() {
        List<ICommand> list = Utils.deserialize("gameOperation");
        this.commandList.clear();
        this.commandList.addAll(list);
        for (ICommand command : list) {
            command.execute();
        }
    }
}
Receiver:
public class Receiver implements Serializable {

    public void onLeft() {
        System.out.println("向左");
    }

    public void onRight() {
        System.out.println("向右");
    }

    public void onBottom() {
        System.out.println("向下");
    }

    public void onTransformation() {
        System.out.println("變形");
    }
}
存檔、讀檔工具類:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.List;


public class Utils {
    /**
     * 序列化訂單對象
     */
    public static void serializable(String name,
                                    List<ICommand> commandList) {
        // 序列化對象的流
        ObjectOutputStream outputStream = null;
        try {
            outputStream = new ObjectOutputStream(new FileOutputStream(
                    new File("C:\\Users\\Administrator\\Desktop\\" + name + ".txt")));
            outputStream.writeObject(commandList);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (outputStream != null) {
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static List<ICommand> deserialize(String name) {
        ObjectInputStream objectInputStream = null;
        try {
            objectInputStream = new ObjectInputStream(new FileInputStream(
                    new File("C:\\Users\\Administrator\\Desktop\\"
                            + name + ".txt")));
            Object readObject = objectInputStream.readObject();
            return (List<ICommand>) readObject;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}
Client :
public class Client {

    /**
     * @param args
     */
    public static void main(String[] args) {
        /**
         * 接收者
         */
        Receiver receiver = new Receiver();

        // 命令對象
        ICommand leftCommand = new LeftCommand(receiver);
        ICommand rightCommand = new RightCommand(receiver);
        ICommand bottomCommand = new BottomCommand(receiver);
        ICommand transfromCommand = new TransfromCommand(receiver);

        //請求者
        Invoker invoker = new Invoker(leftCommand, rightCommand, bottomCommand, transfromCommand);
        invoker.toLeft();
        invoker.toRight();
        invoker.toBottom();
        invoker.toTransfrom();

        //序列化存檔
        System.out.println("----存檔----");
        invoker.saveArchive();

        invoker.toBottom();


        System.out.println("----讀檔----");
        //讀檔
        invoker.loadArchive();
    }
}

結(jié)果輸出:

向左
向右
向下
變形
----存檔----
向下
----讀檔----
向左
向右
向下
變形

4声搁、命令模式Android中使用

IHttpCommand相當(dāng)于命令接口Command:
/**
 * 網(wǎng)絡(luò)請求命令接口
 *
 * 以前采用Map集合傳入黑竞, 現(xiàn)在面向接口編程
 * Created by Xionghu on 2017/7/4.
 * Desc:
 */

public interface IHttpCommand<T extends IRequestParam> {
    public enum RequestType {
        Default(0), Get(1), Post(2), Delete(3);
        private int type;

        private RequestType(int type) {
            this.type = type;
        }

        public int getType() {
            return type;
        }
    }

    public String execute(Context context, String url, RequestType requestType,
                          T requestParam);
}
SystemHttpCommand 相當(dāng)于具體命令實現(xiàn)ConcreteCommand:.
OKHttpCommand 省略
public class SystemHttpCommand extends AbsHttpCommand<SystemRequestParam> {
    @Override
    public String executePost(Context context, String url, SystemRequestParam requestParam) {
        //發(fā)送請求
        try {
            return HttpUtils.post(url, requestParam.getRequestParam());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public String executeGet(Context context, String url, SystemRequestParam requestParam) {
        try {
            return HttpUtils.get(url,requestParam.getRequestParam());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}
HttpUtils 相當(dāng)于接收者Receiver:
public class HttpUtils {

    public static String get(String urlStr, Map<String, Object> paramMap)
            throws Exception {
        // 拼接參數(shù)
        StringBuilder params = new StringBuilder(urlStr + "?");
        int i = 0;
        for (String key : paramMap.keySet()) {
            Object value = paramMap.get(key);
            params.append(key);
            params.append("=");
            params.append(value);
            if (i < paramMap.size() - 1) {
                params.append("&");
            }
            i++;
        }
        return get(params.toString());
    }

    public static String get(String urlStr) {
        String result = null;
        try {
            URL url = new URL(urlStr);
            HttpURLConnection connection = (HttpURLConnection) url
                    .openConnection();
            connection.setReadTimeout(5000);
            connection.setRequestMethod("GET");
            connection.setDoInput(true);
            if (connection.getResponseCode() == 200) {
                InputStream inStream = connection.getInputStream();
                result = new String(StreamTool.readInputStream(inStream));
                return result;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    public static String post(String urlStr, String username, String password)
            throws Exception {
        Map<String, Object> paramMap = new HashMap<String, Object>();
        paramMap.put("username", username);
        paramMap.put("password", password);
        return post(urlStr, paramMap);
    }

    public static String post(String urlStr, Map<String, Object> paramMap)
            throws Exception {
        StringBuffer sb = null;
        // 拼接參數(shù)
        StringBuilder params = new StringBuilder();
        int i = 0;
        for (String key : paramMap.keySet()) {
            Object value = paramMap.get(key);
            params.append(key);
            params.append("=");
            params.append(value);
            if (i < paramMap.size() - 1) {
                params.append("&");
            }
            i++;
        }
        // 創(chuàng)建請求地址
        URL url = new URL(urlStr);
        // 打開連接
        HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
        // 設(shè)置參數(shù)
        httpConn.setDoOutput(true); // 需要輸出
        httpConn.setDoInput(true); // 需要輸入
        httpConn.setUseCaches(false); // 不允許緩存
        httpConn.setRequestMethod("POST"); // 設(shè)置POST方式連接
        // 設(shè)置請求屬性
        httpConn.setRequestProperty("Charset", "UTF-8");
        // 連接,也可以不用明文connect,使用下面的httpConn.getOutputStream()會自動connect
        httpConn.connect();
        // 建立輸入流疏旨,向指向的URL傳入?yún)?shù)
        DataOutputStream dos = new DataOutputStream(httpConn.getOutputStream());
        dos.writeBytes(params.toString());
        dos.flush();
        dos.close();
        // 獲得響應(yīng)狀態(tài)
        int resultCode = httpConn.getResponseCode();
        sb = new StringBuffer();
        if (HttpURLConnection.HTTP_OK == resultCode) {
            // 解析服務(wù)器返回的數(shù)據(jù)
            String readLine = new String();
            BufferedReader responseReader = new BufferedReader(
                    new InputStreamReader(httpConn.getInputStream(), "UTF-8"));
            while ((readLine = responseReader.readLine()) != null) {
                sb.append(readLine).append("\n");
            }
            responseReader.close();
            return sb.toString();
        }
        return null;
    }

    public interface OnHttpResultListener {
        public void onResult(String result);
    }
}
HttpTask 相當(dāng)于請求者Invoker

public class HttpTask extends AsyncTask<String, Void, String> {

    private HttpTask.Builder.Params p;

    protected HttpTask(HttpTask.Builder.Params p) {
        this.p = p;
    }

    @SuppressWarnings("unchecked")
    @Override
    protected String doInBackground(String... params) {
        try {
            // 執(zhí)行命令
            return this.p.httpCommand.execute(this.p.context, this.p.url,
                    this.p.requestType, this.p.requestParam);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    protected void onPostExecute(String result) {
        if (this.p.onHttpResultListener != null) {
            this.p.onHttpResultListener.onResult(result);
        }
    }

    public void builder() {
        execute();
    }

    // 采用Builder設(shè)計模式
    public static class Builder {

        private Params p;

        public Builder(Context context, String url,
                       HttpUtils.OnHttpResultListener onHttpResultListener) {
            this.p = new Params(context, url, onHttpResultListener);
        }

        public Builder setRequestType(IHttpCommand.RequestType requestType) {
            this.p.requestType = requestType;
            return this;
        }

        public Builder setRequestParam(IRequestParam requestParam) {
            this.p.requestParam = requestParam;
            return this;
        }

        public Builder setHttpCommand(IHttpCommand httpCommand) {
            this.p.httpCommand = httpCommand;
            return this;
        }

        public HttpTask build() {
            return new HttpTask(p);
        }

        public static class Params {
            public Context context;
            public IHttpCommand.RequestType requestType;
            public String url;
            public IRequestParam requestParam;
            public HttpUtils.OnHttpResultListener onHttpResultListener;
            public IHttpCommand httpCommand;

            public Params(Context context, String url,
                          HttpUtils.OnHttpResultListener onHttpResultListener) {
                this.context = context;
                this.url = url;
                this.requestType = IHttpCommand.RequestType.Get;
                this.httpCommand = new SystemHttpCommand();
                this.requestParam = new SystemRequestParam();
                this.onHttpResultListener = onHttpResultListener;
            }
        }

    }

}
Client :
        //請求者
        HttpTask.Builder builder = new HttpTask.Builder(this, "", new HttpUtils.OnHttpResultListener() {
            @Override
            public void onResult(String result) {

            }
        });

        IRequestParam requestParam = new SystemRequestParam();
        requestParam.put("","");
        builder.setRequestParam(requestParam)
               .setRequestType(IHttpCommand.RequestType.Post)
               .build()
               .builder();

      //請求者
        HttpTask.Builder builder = new HttpTask.Builder(this, "", new HttpUtils.OnHttpResultListener() {
            @Override
            public void onResult(String result) {

            }
        });
        IRequestParam requestParam = new OKHttpRequestParam();
        requestParam.put("","");
        builder.setRequestParam(requestParam)
                .setHttpCommand(new OKHttpCommand())
                .setRequestType(IHttpCommand.RequestType.Post).build().builder();

整個網(wǎng)絡(luò)請求架構(gòu)采用了命令模式
使我們得程序擴展性更加好很魂,耦合降低了(比如 添加setHttpCommand 設(shè)置okhttp請求很方便)

5、模式總結(jié)

5.1 優(yōu)點
  • 解除了請求者與實現(xiàn)者之間的耦合檐涝,降低了系統(tǒng)的耦合度遏匆。

  • 對請求排隊或記錄請求日志法挨,支持撤銷操作。

  • 可以容易地設(shè)計一個組合命令幅聘。

  • 新命令可以容易地加入到系統(tǒng)中凡纳。

5.2 缺點
  • 因為針對每一個命令都需要設(shè)計一個具體命令類,使用命令模式可能會導(dǎo)致系統(tǒng)有過多的具體命令類帝蒿。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末荐糜,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子葛超,更是在濱河造成了極大的恐慌暴氏,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件绣张,死亡現(xiàn)場離奇詭異答渔,居然都是意外死亡,警方通過查閱死者的電腦和手機侥涵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門沼撕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人芜飘,你說我怎么就攤上這事务豺。” “怎么了燃箭?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵冲呢,是天一觀的道長。 經(jīng)常有香客問我招狸,道長敬拓,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任裙戏,我火速辦了婚禮乘凸,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘累榜。我一直安慰自己营勤,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布壹罚。 她就那樣靜靜地躺著葛作,像睡著了一般。 火紅的嫁衣襯著肌膚如雪猖凛。 梳的紋絲不亂的頭發(fā)上赂蠢,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天,我揣著相機與錄音辨泳,去河邊找鬼虱岂。 笑死玖院,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的第岖。 我是一名探鬼主播难菌,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼蔑滓!你這毒婦竟也來了郊酒?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤烫饼,失蹤者是張志新(化名)和其女友劉穎猎塞,沒想到半個月后试读,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體杠纵,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年钩骇,在試婚紗的時候發(fā)現(xiàn)自己被綠了比藻。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡倘屹,死狀恐怖银亲,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情纽匙,我是刑警寧澤务蝠,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站烛缔,受9級特大地震影響馏段,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜践瓷,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一院喜、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧晕翠,春花似錦喷舀、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至樊卓,卻和暖如春拿愧,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背简识。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工赶掖, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留感猛,地道東北人。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓奢赂,卻偏偏與公主長得像陪白,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子膳灶,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容

  • 1 場景問題# 1.1 如何開機## 估計有些朋友看到這個標(biāo)題會非常奇怪咱士,電腦裝配好了,如何開機轧钓?不就是按下啟動按...
    七寸知架構(gòu)閱讀 2,802評論 1 59
  • 目錄 本文的結(jié)構(gòu)如下: 什么是命令模式 為什么要用該模式 模式的結(jié)構(gòu) 代碼示例 優(yōu)點和缺點 適用環(huán)境 模式應(yīng)用 總...
    w1992wishes閱讀 1,097評論 2 9
  • 3.5 隊列請求## 所謂隊列請求序厉,就是對命令對象進行排隊,組成工作隊列毕箍,然后依次取出命令對象來執(zhí)行弛房。多用多線程或...
    七寸知架構(gòu)閱讀 1,996評論 4 54
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)而柑,斷路器文捶,智...
    卡卡羅2017閱讀 134,601評論 18 139
  • 昨天老婆要求我在朋友圈發(fā)我們的照片。從頭到尾的發(fā)一遍媒咳。我在我們的小號從頭到尾的發(fā)了一遍粹排。可是她要求我涩澡,在我的朋友圈...
    房留金閱讀 193評論 1 1