Limited pattern in reverse type of static factory pattern

I read in Effective Java that you should not use restricted wildcards as return types, but I don’t know how to do this. The only way my code compiles is to use RequestCloner<? extends HttpUriRequest>as the return type in a static factory. Am I doing something wrong or is there a workaround?

NOTE. It should be noted that it HttpUriRequesthas a method setHeader, but only HttpPosthas a method 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;
  }
}
+4
source share
3 answers

, . , , URL- , HttpServletRequest ( ) clone.

, , RequestCloner.

RequestCloner

:

abstract class RequestCloner {

  private enum RequestType {
    GET, POST, 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() GET POST.

, , :

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 generic - clone() GET POST.

  • ( ).
  • newInstance()

2:

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

, , : Class factory:

abstract class RequestCloner<T extends HttpUriRequest> {

    protected T clonedRequest;

    @SuppressWarnings("unchecked")
    static <U extends HttpUriRequest, V extends RequestCloner<U>> V newInstance(
        U request, String url) {

        Class<U> clazz = request.getClass();
        Class<V> clz = null;
        if (HttpGet.class == clazz) {
            clz = GetRequestCloner.class;
        } else if (HttpPost.class == clazz) {
            clz = PostRequestCloner.class;
        } // etc

        try {
            return clz.getDeclaredConstructor(String.class).newInstance(url);
        } catch (Exception e) {
            throw new IllegalArgumentException("Factory error", e);
        }
    }

    // no need for this method to be abstract
    public T cloneRequest(HttpServletRequest servletRequest) throws IOException {
        cloneHeaders(servletRequest);
        return clonedRequest;
    }

    protected void cloneHeaders(HttpServletRequest servletRequest) {
        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)));
            }
        }
    }
}

GET :

class GetRequestCloner extends RequestCloner<HttpGet> {

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

    // no need to override cloneRequest here
}

POST:

class PostRequestCloner extends RequestCloner<HttpPost> {

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

    @Override
    public HttpPost cloneRequest(HttpServletRequest servletRequest) throws IOException {
        super.cloneRequest(servletRequest);
        cloneBody(servletRequest); // Adding POST body here
        return clonedRequest;
    }
}

:

GetRequestCloner getCloner = RequestCloner.newInstance(servletRequest, url);
HttpGet get = getCloner.cloneRequest(servletRequest);

PostRequestCloner postCloner = RequestCloner.newInstance(servletRequest, url);
HttpPost post = postCloner.cloneRequest(servletRequest);

. clone() cloneRequest(), Java Clonable.clone().

0

?

static <V extends HttpUriRequest, K extends RequestCloner<V>> K newInstance((String type, String url) 

, , .

, , - , , .

, .

-1

Source: https://habr.com/ru/post/1599818/


All Articles