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類圖
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)有過多的具體命令類帝蒿。