繁体   English   中英

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);

想到以下几点:

  1. 对于返回对象的函数调用:从不返回 null。
  2. 对于不(否则)返回任何内容的函数调用:返回this
  3. 接受 API 中的功能接口,以便用户可以自定义行为
  4. 对于如上所述公开 API 的有状态对象,提供一个构建器模式,以便用户最终不会在构造函数之间进行选择
  5. 描述的 Builder 的所有方法都必须是无效的,因此返回this

您可以创建一个ResponseStringBuilder类,该类采用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 中实现承诺。 问题是,您正试图通过“管道”传递两种不同的东西:请求,它是可变的并且(有时)会发生变化,以及在调用过程中累积的结果。

我已经通过创建一个名为Pipe的类来解决这个问题,该类有一个请求,以及迄今为止结果的累加器。 它具有两者的 getter,并且它有一些方便的方法来返回一个带有累积结果的新对象,甚至改变请求并在一次调用中累积。 这使得 API 链接的代码更加清晰。

字段、构造函数和 getter 之后的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 类型推断,但promiseresult的类型分别是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;
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM