职责链模式(Chain of Responsibility):使多个对象都有机会处理同一个请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
类图:
应用场景:
为完成同一个请求,如果存在多个请求处理器以及未知请求处理器个数或者请求处理器可动态配置的情况下,可以考虑使用责任链模式。如OKHttp的拦截器就是使用的责任链模式。
我先描述一下我的使用场景。新接一个需求,拍照上传到服务器。我调用系统自带的拍照功能完成拍照并将图片进行压缩,加水印,然后我需要将图片上传到阿里云的OSS服务器,最后再将上传结果反馈到项目的服务器。也许有人说,可以把所有的方法组装在一起按顺序执行,但是上传照片或其他文件到OSS服务器是需要等待结果的,先要鉴权获得一个临时凭证,再通过这个凭证构造成OSSClient 作为参数传递到OSSService中,调用异步方法 asyncPutImage 上传完成。如果一直在回调方法里面往下写,可能要层层往下,代码可读性太低。于是我想到了用责任链设计模式来完成此功能。
由于是线上项目,我只能贴出部分代码供参考。
这个Request类封装了所有的输入参数
public class Request {
/**
* 订单编号
*/
String orderid;
String object;//上传图片别名
String realPath;//图片实际路径
String targetDir;//目标根目录
String compressPath;//压缩后路径(图片压缩后的图径)
String targetPath;//目标路径
String remarks; //备注
OSSTempKey ossTempKey; //OSS临时授权凭证
OSS oss; //客户端
public String getTargetPath() {
return targetPath;
}
public void setTargetPath(String targetPath) {
this.targetPath = targetPath;
}
public String getCompressPath() {
return compressPath;
}
public void setCompressPath(String compressPath) {
this.compressPath = compressPath;
}
public String getTargetDir() {
return targetDir;
}
public void setTargetDir(String targetDir) {
this.targetDir = targetDir;
}
public String getOrderid() {
return orderid;
}
public String getObject() {
return object;
}
public String getRealPath() {
return realPath;
}
public OSSTempKey getOssTempKey() {
return ossTempKey;
}
public void setOrderid(String orderid) {
this.orderid = orderid;
}
public String getRemarks() {
return remarks;
}
public void setRemarks(String remarks) {
this.remarks = remarks;
}
public OSS getOss() {
return oss;
}
public Request(String orderid) {
this.orderid = orderid;
}
public void setObject(String object) {
this.object = object;
}
public void setRealPath(String realPath) {
this.realPath = realPath;
}
public void setOssTempKey(OSSTempKey ossTempKey) {
this.ossTempKey = ossTempKey;
}
public void setOss(OSS oss) {
this.oss = oss;
}
}
输出类:
/** * author : dawn * e-mail : [email protected] * date : 2020/6/30 11:03 * desc : 处理完成的结果 */ public class Result { final int code; final String message; public Result(Builder builder) { this.code = builder.code; this.message = builder.message; } public static class Builder { int code; String message; public Builder(){ this.code = -1000; } public int getCode() { return code; } public String getMessage() { return message; } public Builder setCode(int code) { this.code = code; return this; } public Builder setMessage(String message) { this.message = message; return this; } public Result build() { if (code < 0) throw new IllegalStateException("code < 0: " + code); if (message == null) throw new IllegalStateException("message == null"); return new Result(this); } } }
所有的实际处理功能类都将继承此类
/** * author : dawn * e-mail : [email protected] * date : 2020/6/30 11:02 * desc : 图片上传链式结构 */ public interface Helper { void onNext(Helper.Chain chain) throws IOException; /** * */ interface Chain{ Request request(); void proceed(Request request) throws IOException; } }
/** * author : dawn * e-mail : [email protected] * date : 2020/6/30 11:30 * desc : */ class RealHelperChain implements Helper.Chain { private final int index; private List<Helper> helpers; Request request; public RealHelperChain(int index,Request request,List<Helper> helpers){ this.index = index; this.request = request; this.helpers = helpers; } @Override public Request request() { return request; } @Override public void proceed(Request request) throws IOException { doproceed(request); } private void doproceed(Request request) throws IOException { if (index >= helpers.size()) throw new AssertionError(); RealHelperChain chain = new RealHelperChain(index+1,request,helpers); Helper helper = helpers.get(index); helper.onNext(chain); } }
/** * author : dawn * e-mail : [email protected] * date : 2020/6/30 11:25 * desc : */ public class RealController { public void execute(Request request) throws IOException { getResponseWithHelperChain(request); } public void getResponseWithHelperChain(Request request) throws IOException { List<Helper> list = new ArrayList<>(); list.add(new CompressHelper());//压缩 list.add(new AddMarkerHelper()); //添加水印 list.add(new GetOssKeyHelper());//获取凭证 list.add(new OSSCredentialHelper());//鉴权凭证 list.add(new AsyncPutImageHelper());//上传图片到OSS list.add(new UploadDeliveryImgHelper());//上传图片结果到服务器 Helper.Chain chain = new RealHelperChain(0,request,list); chain.proceed(request); } }
我贴上其中的鲁班压缩功能作为参考,具体功能都放在onNext中,要继续往下执行,就调用chain.proceed(chain.request());代码就会继续往下执行。
/** * author : dawn * e-mail : [email protected] * date : 2020/7/1 17:40 * desc : 鲁班压缩后的路径 */ public class CompressHelper implements Helper { @Override public void onNext(Chain chain) throws IOException { Request request = chain.request(); //将处理完的图片再放回到Object中 Luban.with(BaseApplication.getAppContext()) .load(request.getRealPath()) .ignoreBy(100) .setTargetDir(request.getTargetDir()) .filter(path -> !(TextUtils.isEmpty(path) || path.toLowerCase().endsWith(".gif"))) .setCompressListener(new OnCompressListener() { @Override public void onStart() { // TODO 压缩开始前调用,可以在方法内启动 loading UI } @Override public void onSuccess(File file) { // TODO 压缩成功后调用,返回压缩后的图片文件 String compressPath = file.getAbsolutePath(); request.setCompressPath(compressPath); try { chain.proceed(chain.request()); } catch (IOException e) { e.printStackTrace(); } } @Override public void onError(Throwable e) { // TODO 当压缩过程出现问题时调用 RxBus.getInstance().post(new NotifyEvent(-1, "压缩图片出现问题:" + e.getMessage())); } }).launch(); } }
调用方法如下: