加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Java > 正文

java – 返回类型的静态工厂模式的有界通配符

发布时间:2020-12-15 02:08:17 所属栏目:Java 来源:网络整理
导读:我在Effective Java中读到你不应该使用有界通配符作为返回类型,但我不知道我该怎么做.我的代码编译的唯一方法是使用RequestCloner?扩展HttpUriRequest作为静态工厂中的返回类型.我做错了什么或有解决方法吗? 注意:有一点需要注意的是HttpUriRequest有方法
我在Effective Java中读到你不应该使用有界通配符作为返回类型,但我不知道我该怎么做.我的代码编译的唯一方法是使用RequestCloner<?扩展HttpUriRequest>作为静态工厂中的返回类型.我做错了什么或有解决方法吗?

注意:有一点需要注意的是HttpUriRequest有方法setHeader,但只有HttpPost有方法setEntity.

abstract class RequestCloner<T extends HttpUriRequest> {

  protected T clonedRequest;

  private enum RequestType {
    GET,POST,DELETE
  }

  static RequestCloner<? extends HttpUriRequest> newInstance(
      String type,String url) {
    RequestType requestType = RequestType.valueOf(type);
    switch (requestType) {
    case GET:
      return new GetRequestCloner(url);
    case POST:
      return new PostRequestCloner(url);
    case DELETE:
      return new DeleteRequestCloner(url);
    default:
      throw new IllegalArgumentException(String.format(
          "Method '%s' not supported",type));
    }
  }

  public abstract HttpUriRequest clone(HttpServletRequest servletRequest) throws IOException;

  protected void cloneHeaders(HttpServletRequest servletRequest) {
    @SuppressWarnings("unchecked")
    Enumeration<String> e = servletRequest.getHeaderNames();
    while (e.hasMoreElements()) {
        String header = e.nextElement();
        if (!header.equalsIgnoreCase("Content-Length")
                && !header.equalsIgnoreCase("Authorization")
                && !header.equalsIgnoreCase("Host")) {
            clonedRequest.setHeader(new BasicHeader(header,servletRequest.getHeader(header)));
        }
    }
  }
}

...

class GetRequestCloner extends RequestCloner<HttpGet> {

  GetRequestCloner(String url) {
    this.clonedRequest = new HttpGet(url);
  }

  @Override
  public HttpUriRequest clone(HttpServletRequest servletRequest) {
    cloneHeaders(servletRequest);
    return clonedRequest;
  }
}

...

class PostRequestCloner extends RequestCloner<HttpPost> {

  private static final int MAX_STR_LEN = 1024;

  PostRequestCloner(String url) {
    this.clonedRequest = new HttpPost(url);
  }

  @Override
  public HttpUriRequest clone(HttpServletRequest servletRequest) throws IOException {
    cloneHeaders(servletRequest);
    cloneBody(servletRequest);
    return clonedRequest;
  }

  private void cloneBody(HttpServletRequest servletRequest) throws IOException {
    StringBuilder sb = new StringBuilder("");
    BufferedReader br = new BufferedReader(new InputStreamReader(
            servletRequest.getInputStream(),"UTF-8"));
    String line = "";
    while ((line = br.readLine()) != null && sb.length() < MAX_STR_LEN) {
        sb.append(line);
    }
    br.close();
    clonedRequest.setEntity(new StringEntity(sb.toString(),"UTF-8"));
  }
}

...

class DeleteRequestCloner extends RequestCloner<HttpDelete> {

  DeleteRequestCloner(String url) {
    this.clonedRequest = new HttpDelete(url);
  }

  @Override
  public HttpUriRequest clone(HttpServletRequest servletRequest) {
    cloneHeaders(servletRequest);
    return clonedRequest;
  }
}

解决方法

查看您的代码,您的类不需要是通用的.进一步看,存在一个奇怪的问题,即调用者在URL中传递以创建克隆者,然后将HttpServletRequest(理论上可以是不同类型的请求)传递到克隆方法中.

我可以看到两种解决方案,具体取决于您是否真的需要RequestCloner是通用的.

如果RequestCloner不需要通用

更改基类如下:

abstract class RequestCloner {

  private enum RequestType {
    GET,DELETE
  }

  public static HttpUriRequest cloneRequest(HttpServletRequest servletRequest)
        throws IOException {
    RequestCloner cloner = createCloner(servletRequest);
    String uri = servletRequest.getRequestURI();
    return cloner.clone(uri,servletRequest);
  }

  private static RequestCloner createCloner(HttpServletRequest servletRequest) {
    RequestType requestType = RequestType.valueOf(servletRequest. getMethod());
    switch (requestType) {
    case GET:
      return new GetRequestCloner();
    case POST:
      return new PostRequestCloner();
    case DELETE:
      return new DeleteRequestCloner();
    default:
      throw new AssertionFailedError(String.format(
          "RequestType '%s' not supported",requestType));
    }
  }

  protected abstract HttpUriRequest clone(
      String uri,HttpServletRequest servletRequest)
      throws IOException;

  protected final void cloneHeaders(
      HttpServletRequest servletRequest,HttpUriRequest clonedRequest) { // note addition of parameter
    // same code as before,but modify the passed-in clonedRequest
  }
}

RequestCloner的子类将覆盖clone(),可选地更改返回值以返回HttpUriRequest的子类:

class PostRequestCloner extends RequestCloner {
  private static final int MAX_STR_LEN = 1024;

  @Override
  protected HttpPost clone(
      String uri,HttpServletRequest servletRequest)
      throws IOException {
    HttpPost clonedRequest = new HttpPost(uri);
    cloneHeaders(servletRequest,clonedRequest);
    cloneBody(servletRequest,clonedRequest);
    return clonedRequest;
  }

  ...
}

上述解决方案的缺点是cloneRequest()的返回值与作为POST请求的GET请求相同.

如果您愿意,可以通过向枚举添加代码来删除开关:

abstract class RequestCloner {

  private enum RequestType {
    GET(new GetRequestCloner()),POST(new PostRequestCloner()),DELETE(new DeleteRequestCLoner());

    private final RequestCloner requestCloner;

    private RequestType(RequestCloner requestCloner) {
      this.requestCloner = requestCloner();
    }
  }

  public static HttpUriRequest cloneRequest(HttpServletRequest servletRequest)
        throws IOException {
    RequestType requestType = RequestType.valueOf(servletRequest. getMethod());
    String uri = servletRequest.getRequestURI();
    return requestType.requestCloner.clone(uri,servletRequest);
  }

  ...
}

如果希望返回值依赖于请求的类型,则调用者需要指定某种类型的标记,显式引用RequestCloner的子类,或者为每种类型的请求向RequestCloner添加一个静态方法.

如果RequestCloner需要通用

鉴于问题中的代码,使RequestCloner通用的唯一好处是使clone()的返回值与GET或POST不同.

为此,您有两种选择

>使子类(及其构造函数)公开.
>用多种创建方法替换newInstance()方法

以下是选项2的示例:

public static RequestCloner<HttpPost> forPostRequest(String URL) {
  return new PostRequestCloner(URL);
}

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读