在 JAVA 中 API 链接的最佳设计模式
Best design pattern for API chaining in JAVA
我想在 Java 中调用一系列 API 调用。要求是某些 API 的响应将用于后续 API 调用的请求。我可以使用某些循环来实现这一点。但我想以实现通用的方式使用设计模式。有帮助吗?
责任链不能满足我的需要,因为我一开始不知道我的请求上下文是什么。
String out = null;
Response res = execute(req);
out += res.getOut();
req.setXYZ(res.getXYZ);
Response res = execute(req);
out += res.getOut();
req.setABC(res.getABC);
Response res = execute(req);
out += res.getOut();
System.out.println("Final response::"+out);
想到以下内容:
- 对于 return 一个对象的函数调用:从不 return null。
- 对于不(否则)return任何东西的函数调用:return
this
.
- 在您的 API 中接受功能接口,以便用户可以自定义行为
- 对于如上所述公开 API 的有状态对象,提供构建器模式,这样用户就不会最终在构造函数之间进行选择
- 描述的Builder的所有方法都必须是void,因此return
this
您可以创建一个 ResponseStringBuilder
class,它需要 Function<Response,String>
从 Response
获得 String
。
public ResponseStringBuilder {
private Request request;
public StringBuilder resultBuilder = new StringBuilder();
public ResponseBuilder(Request req) {
this.request = req;
}
public ResponseStringBuilder fromExtractor(Function<Request, Response> getResponse, Function<Response,String> extract) {
Response response = getResponse.apply(request);
resultBuilder.append(extract.apply(response));
return this;
}
public String getResult() {
return resultBuilder.toString();
}
}
那会让你打电话
ResponseStringBuilder builder = new ResponseStringBuilder(req);
@SuppressWarnings("unchecked")
Function<Response,String> extractors = new Function[] {
Response::getABC, Response::getXYZ
};
for (Function<Response,String> ext : extractors) {
builder = builder.fromExtractor(this::execute, ext);
}
System.out.println("final response: " + builder.getResult());
不确定数组声明是否真的可以编译,但稍作修改后它应该可以工作,你明白了要点。
您可以使用 CompletableFuture
来实现 Java 中的承诺。问题是,您试图将两种不同的东西传递给 'pipeline:' 请求,它是可变的并且(有时)会发生变化,而结果是在调用过程中累积的。
我已经通过创建一个名为 Pipe
的 class 来解决这个问题,它有一个请求,以及到目前为止结果的累加器。它有两者的吸气剂,它有一些方便的方法来 return 一个新的对象与累积的结果,甚至改变请求并在一次调用中累积。这使得 API 链接的代码更加清晰。
fields、constructor 和getters 之后的with*
方法是处理累积和变异的方法。 chain
方法将它们放在一起:
import java.util.concurrent.CompletableFuture;
public class Pipe {
private Request req;
private String out;
public Pipe(Request req, String out) {
this.req = req;
this.out = out;
}
public Request getReq() {
return req;
}
public String getOut() {
return out;
}
public Pipe with(String data) {
return new Pipe(req, out + data);
}
public Pipe withABC(String abc, String data) {
req.setABC(abc);
return new Pipe(req, out + data);
}
public Pipe withXYZ(String xyz, String data) {
req.setXYZ(xyz);
return new Pipe(req, out + data);
}
public static void chain(Request req) throws Exception {
var promise = CompletableFuture.supplyAsync(() -> new Pipe(req, ""))
.thenApply(pipe -> {
Response res = execute(pipe.getReq());
return pipe.withABC(res.getABC(), res.getOut());
})
.thenApply(pipe -> {
Response res = execute(pipe.getReq());
return pipe.withXYZ(res.getXYZ(), res.getOut());
})
.thenApply(pipe -> {
Response res = execute(pipe.getReq());
return pipe.with(res.getOut());
});
var result = promise.get().getOut();
System.out.println(result);
}
public static Response execute(Request req) {
return req.getResponse();
}
}
因为它是异步运行的,所以它可以抛出 InterruptedException,如果其他东西中断,它也可以抛出 ExecutionException。我不知道你想怎么处理,所以我只是宣布 chain
抛出。
如果您想在循环中应用 n 操作,您必须继续重新分配承诺,如下所示:
var promise = CompletableFuture.supplyAsync(() -> new Pipe(req, ""));
for (...) {
promise = promise.thenApply(pipe -> {
Response res = execute(pipe.getReq());
return pipe.with(res.getOut());
});
}
var result = promise.get().getOut();
我在这里对 var
使用了 Java 10 类型推断,但是 promise
和 result
的类型将是 CompletableFuture<Pipe>
和 String
, 分别.
(注意:最好使 Request
不可变并在管道中传递一个新的、改变的而不是改变它。另一方面,你也可以包装一个 StringBuilder
而不是 String
,并且让你积累的数据也是可变的。现在它是可变和不可变的奇怪组合,但这与你的代码正在做的相匹配。)
感谢大家的意见,最后我找到了一个满足我需要的解决方案。我使用一个 Singleton 来执行请求。对于每种类型的命令,将有一组请求以一个特定的顺序执行。每个命令都有特定的请求执行顺序,我将其存储在具有请求唯一 ID 的数组中。然后将数组保存在针对命令名称的映射中。
在一个循环中,我 运行 数组并执行,在每次迭代后我不断将响应设置回请求对象并最终准备输出响应。
private static Map<RequestAction,String[]> actionMap = new HashMap<RequestAction, String[]>();
static{
actionMap.put(RequestAction.COMMAND1,new String[]{WebServiceConstants.ONE,WebServiceConstants.TWO,WebServiceConstants.FOUR,WebServiceConstants.THREE});
actionMap.put(RequestAction.THREE,new String[]{WebServiceConstants.FIVE,WebServiceConstants.ONE,WebServiceConstants.TWO});}
public Map<String,Object> execute(ServiceParam param) {
String[] requestChain = getRequestChain(param);
Map<String,Object> responseMap = new HashMap<String, Object>();
for(String reqId : requestChain) {
prepareForProcessing(param, tempMap,responseMap);
param.getRequest().setReqId(reqId);
//processing the request
tempMap = Service.INSTANCE.process(param);
//prepare responseMap using tempMap
param.setResponse(response);
}
return responseMap;
}
我想在 Java 中调用一系列 API 调用。要求是某些 API 的响应将用于后续 API 调用的请求。我可以使用某些循环来实现这一点。但我想以实现通用的方式使用设计模式。有帮助吗?
责任链不能满足我的需要,因为我一开始不知道我的请求上下文是什么。
String out = null;
Response res = execute(req);
out += res.getOut();
req.setXYZ(res.getXYZ);
Response res = execute(req);
out += res.getOut();
req.setABC(res.getABC);
Response res = execute(req);
out += res.getOut();
System.out.println("Final response::"+out);
想到以下内容:
- 对于 return 一个对象的函数调用:从不 return null。
- 对于不(否则)return任何东西的函数调用:return
this
. - 在您的 API 中接受功能接口,以便用户可以自定义行为
- 对于如上所述公开 API 的有状态对象,提供构建器模式,这样用户就不会最终在构造函数之间进行选择
- 描述的Builder的所有方法都必须是void,因此return
this
您可以创建一个 ResponseStringBuilder
class,它需要 Function<Response,String>
从 Response
获得 String
。
public ResponseStringBuilder {
private Request request;
public StringBuilder resultBuilder = new StringBuilder();
public ResponseBuilder(Request req) {
this.request = req;
}
public ResponseStringBuilder fromExtractor(Function<Request, Response> getResponse, Function<Response,String> extract) {
Response response = getResponse.apply(request);
resultBuilder.append(extract.apply(response));
return this;
}
public String getResult() {
return resultBuilder.toString();
}
}
那会让你打电话
ResponseStringBuilder builder = new ResponseStringBuilder(req);
@SuppressWarnings("unchecked")
Function<Response,String> extractors = new Function[] {
Response::getABC, Response::getXYZ
};
for (Function<Response,String> ext : extractors) {
builder = builder.fromExtractor(this::execute, ext);
}
System.out.println("final response: " + builder.getResult());
不确定数组声明是否真的可以编译,但稍作修改后它应该可以工作,你明白了要点。
您可以使用 CompletableFuture
来实现 Java 中的承诺。问题是,您试图将两种不同的东西传递给 'pipeline:' 请求,它是可变的并且(有时)会发生变化,而结果是在调用过程中累积的。
我已经通过创建一个名为 Pipe
的 class 来解决这个问题,它有一个请求,以及到目前为止结果的累加器。它有两者的吸气剂,它有一些方便的方法来 return 一个新的对象与累积的结果,甚至改变请求并在一次调用中累积。这使得 API 链接的代码更加清晰。
fields、constructor 和getters 之后的with*
方法是处理累积和变异的方法。 chain
方法将它们放在一起:
import java.util.concurrent.CompletableFuture;
public class Pipe {
private Request req;
private String out;
public Pipe(Request req, String out) {
this.req = req;
this.out = out;
}
public Request getReq() {
return req;
}
public String getOut() {
return out;
}
public Pipe with(String data) {
return new Pipe(req, out + data);
}
public Pipe withABC(String abc, String data) {
req.setABC(abc);
return new Pipe(req, out + data);
}
public Pipe withXYZ(String xyz, String data) {
req.setXYZ(xyz);
return new Pipe(req, out + data);
}
public static void chain(Request req) throws Exception {
var promise = CompletableFuture.supplyAsync(() -> new Pipe(req, ""))
.thenApply(pipe -> {
Response res = execute(pipe.getReq());
return pipe.withABC(res.getABC(), res.getOut());
})
.thenApply(pipe -> {
Response res = execute(pipe.getReq());
return pipe.withXYZ(res.getXYZ(), res.getOut());
})
.thenApply(pipe -> {
Response res = execute(pipe.getReq());
return pipe.with(res.getOut());
});
var result = promise.get().getOut();
System.out.println(result);
}
public static Response execute(Request req) {
return req.getResponse();
}
}
因为它是异步运行的,所以它可以抛出 InterruptedException,如果其他东西中断,它也可以抛出 ExecutionException。我不知道你想怎么处理,所以我只是宣布 chain
抛出。
如果您想在循环中应用 n 操作,您必须继续重新分配承诺,如下所示:
var promise = CompletableFuture.supplyAsync(() -> new Pipe(req, ""));
for (...) {
promise = promise.thenApply(pipe -> {
Response res = execute(pipe.getReq());
return pipe.with(res.getOut());
});
}
var result = promise.get().getOut();
我在这里对 var
使用了 Java 10 类型推断,但是 promise
和 result
的类型将是 CompletableFuture<Pipe>
和 String
, 分别.
(注意:最好使 Request
不可变并在管道中传递一个新的、改变的而不是改变它。另一方面,你也可以包装一个 StringBuilder
而不是 String
,并且让你积累的数据也是可变的。现在它是可变和不可变的奇怪组合,但这与你的代码正在做的相匹配。)
感谢大家的意见,最后我找到了一个满足我需要的解决方案。我使用一个 Singleton 来执行请求。对于每种类型的命令,将有一组请求以一个特定的顺序执行。每个命令都有特定的请求执行顺序,我将其存储在具有请求唯一 ID 的数组中。然后将数组保存在针对命令名称的映射中。
在一个循环中,我 运行 数组并执行,在每次迭代后我不断将响应设置回请求对象并最终准备输出响应。
private static Map<RequestAction,String[]> actionMap = new HashMap<RequestAction, String[]>();
static{
actionMap.put(RequestAction.COMMAND1,new String[]{WebServiceConstants.ONE,WebServiceConstants.TWO,WebServiceConstants.FOUR,WebServiceConstants.THREE});
actionMap.put(RequestAction.THREE,new String[]{WebServiceConstants.FIVE,WebServiceConstants.ONE,WebServiceConstants.TWO});}
public Map<String,Object> execute(ServiceParam param) {
String[] requestChain = getRequestChain(param);
Map<String,Object> responseMap = new HashMap<String, Object>();
for(String reqId : requestChain) {
prepareForProcessing(param, tempMap,responseMap);
param.getRequest().setReqId(reqId);
//processing the request
tempMap = Service.INSTANCE.process(param);
//prepare responseMap using tempMap
param.setResponse(response);
}
return responseMap;
}