Android 架构师之路 目录
1、命令模式概念
1.1 介绍
命令模式(Command Pattern)是一种数据驱动的设计模式,它属于行为型模式。请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。
1.2 定义
将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化,对请求排队或记录请求日志。以及支持可撤销的操作。
1.3 使用场景
- 系统需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互。
- 系统需要在不同的时间指定请求、将请求排队(如:线程池+工作队列)和执行请求。
- 系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作。
- 系统需要将一组操作组合在一起,即支持宏命令。
2、命令模式UML类图
Command(抽象命令类):抽象出命令对象,可以根据不同的命令类型。写出不同的实现类
ConcreteCommand(具体命令类):实现了抽象命令对象的具体实现
Invoker(调用者/请求者):请求的发送者,它通过命令对象来执行请求。一个调用者并不需要在设计时确定其接收者,因此它只与抽象命令来之间存在关联。在程序运行时,将调用命令对象的execute() ,间接调用接收者的相关操作。
Receiver(接收者):接收者执行与请求相关的操作,真正执行命令的对象。具体实现对请求的业务处理。未抽象前,实际执行操作内容的对象。
Client(客户端):在客户类中需要创建调用者对象,具体命令类对象,在创建具体命令对象时指定对应的接收者。发送者和接收者之间没有之间关系,都通过命令对象来调用。
3、命令模式代码实现
俄罗斯方块游戏
向下方块、向右方块、向左方块...,每一个方向都是一个命令
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() {
/**
* 执行之前干一些事情
* 例如 存档
*/
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 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 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 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 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) 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();
}
}
结果输出:
向左
向右
向下
变形
----存档----
向下
----读档----
向左
向右
向下
变形
4、命令模式Android中使用
IHttpCommand相当于命令接口Command:
/**
* 网络请求命令接口
*
* 以前采用Map集合传入, 现在面向接口编程
* Created by Xionghu on 2017/7/4.
* Desc:
*/
public interface IHttpCommand {
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 相当于具体命令实现ConcreteCommand:.
OKHttpCommand 省略
public class SystemHttpCommand extends AbsHttpCommand {
@Override
public String executePost(Context context, String url, SystemRequestParam requestParam) {
//发送请求
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 相当于接收者Receiver:
public class HttpUtils {
public static String get(String urlStr, Map paramMap)
throws Exception {
// 拼接参数
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 paramMap = new HashMap();
paramMap.put("username", username);
paramMap.put("password", password);
return post(urlStr, paramMap);
}
public static String post(String urlStr, Map paramMap)
throws Exception {
StringBuffer sb = null;
// 拼接参数
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++;
}
// 创建请求地址
URL url = new URL(urlStr);
// 打开连接
HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
// 设置参数
httpConn.setDoOutput(true); // 需要输出
httpConn.setDoInput(true); // 需要输入
httpConn.setUseCaches(false); // 不允许缓存
httpConn.setRequestMethod("POST"); // 设置POST方式连接
// 设置请求属性
httpConn.setRequestProperty("Charset", "UTF-8");
// 连接,也可以不用明文connect,使用下面的httpConn.getOutputStream()会自动connect
httpConn.connect();
// 建立输入流,向指向的URL传入参数
DataOutputStream dos = new DataOutputStream(httpConn.getOutputStream());
dos.writeBytes(params.toString());
dos.flush();
dos.close();
// 获得响应状态
int resultCode = httpConn.getResponseCode();
sb = new StringBuffer();
if (HttpURLConnection.HTTP_OK == resultCode) {
// 解析服务器返回的数据
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 相当于请求者Invoker
public class HttpTask extends AsyncTask {
private HttpTask.Builder.Params p;
protected HttpTask(HttpTask.Builder.Params p) {
this.p = p;
}
@SuppressWarnings("unchecked")
@Override
protected String doInBackground(String... params) {
try {
// 执行命令
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设计模式
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();
整个网络请求架构采用了命令模式
使我们得程序扩展性更加好,耦合降低了(比如 添加setHttpCommand 设置okhttp请求很方便)
5、模式总结
5.1 优点
解除了请求者与实现者之间的耦合,降低了系统的耦合度。
对请求排队或记录请求日志,支持撤销操作。
可以容易地设计一个组合命令。
新命令可以容易地加入到系统中。
5.2 缺点
- 因为针对每一个命令都需要设计一个具体命令类,使用命令模式可能会导致系统有过多的具体命令类。