【问题标题】:Java generic parameter method to avoid code duplication避免代码重复的Java泛型参数方法
【发布时间】:2017-03-31 07:46:21
【问题描述】:

我有一个解析对象的函数。但是这个函数在两个服务中是必需的,并且参数具有相同的类名,但不同的包名。我需要的是避免重复代码。

假设函数为:

    private HashMap<String, Integer> getPagination(PagingRequestType pagingRequestType) {
        int pageSize = 200;
        int pageNumber = 1;
        if(pagingRequestType != null) {
            if (pagingRequestType.getNumberOfRecordsPerPage() != 0) {
                pageSize = pagingRequestType.getNumberOfRecordsPerPage();
            }
            if (pagingRequestType.getStartAtRecordNumber() != 0) {
                pageNumber = pagingRequestType.getStartAtRecordNumber();
            }
        }
        HashMap<String, Integer> result = new HashMap<>();
        result.put("pageNumber", pageNumber);
        result.put("pageSize", pageSize);
        return result;
    }

可能的函数调用:

- getPagination(new Abc.PagingRequestType());
- getPagination(new Xyz.PagingRequestType());

PagingRequestType 是两个不同包中的自动生成类。该功能需要实现一次并在两个服务中使用。

谢谢。

【问题讨论】:

  • 等等,你有两个不同的类叫做PagingRequestType吗?这只是尖叫要修复,特别是如果使用相同的两个实现。
  • 显而易见的答案是不要在两个地方自动生成PagingRequestType
  • 或者,如果无法避免,让两个 PagingRequestType 类实现一个通用接口,然后可以在此方法中使用该接口。
  • 是的,这不是因为类有相同的名称,你可以在上面做一些通用的东西。
  • 有两个不同的 WSDL 文件来支持旧系统。

标签: java generic-programming code-reuse


【解决方案1】:

如果您可以修改您的PagingRequestType 类,最好使用通用接口:

class Abc.PagingRequestType implements PagingRequestType
class Xyz.PagingRequestType implements PagingRequestType

interface PagingRequestType {
    getNumberOfRecordsPerPage();
    getStartAtRecordNumber();
}

【讨论】:

    【解决方案2】:

    显而易见的答案是不要在两个地方自动生成PagingRequestType

    如果你不能做到这一点,你需要两个类来实现一个公共接口,通过这个接口可以获得必要的字段(getNumberOfRecordsPerPagegetStartAtRecordNumber)。

    如果你不能改变类,你可以用这些字段创建一个接口:

    interface YourInterface {
      int getNumberOfRecordsPerPage();
      int getStartAtRecordNumber();
    }
    

    并为两个PagingRequestTypes 实现:

    class AbcYourInterface implements YourInterface {
      final Abc.PagingRequestType delegate;  // Set in constructor.
    
      @Override public int getNumberOfRecordsPerPage() {
       return delegate.getNumberOfRecordsPerPage();
      }
    
      // Same for other method.
    }
    

    如果所有其他方法都失败,则将类字段作为单独的参数传递:

    private HashMap<String, Integer> getPagination(int numberOfRecordsPerPage, int startAtRecordNumber) {
    

    使用一些“特殊”值来表示null,例如0,因为如果两个参数都为零,则条件是无操作的。

    【讨论】:

    【解决方案3】:

    PagingRequestType 都可以实现一个通用接口或扩展一个通用类,您可以将此“通用部分”作为函数的参数。虽然我不知道你是否可以用这种方式修改自动生成的代码。

    【讨论】:

    • 这是我这边的问题@LLL 我无法修改 WSDL 文件。
    • 然后你可以使用反射 API 但它可能是矫枉过正
    【解决方案4】:

    如果让这两个PagingRequestTypes 实现一个通用接口的选项是不可能的,你可以反过来做,定义一个proxy 类型来包装这两个类型:

    public final class PagingRequestTypeProxy {
        private a.b.c.PagingRequestType abcPagingRequestType;
    
        private a.b.d.PagingRequestType abdPagingRequestType;
    
        public PagingRequestTypeProxy(PagingRequestType abcPagingRequestType) {
            this.abcPagingRequestType = abcPagingRequestType;
        }
    
        public PagingRequestTypeProxy(a.b.d.PagingRequestType abdPagingRequestType) {
            this.abdPagingRequestType = abdPagingRequestType;
        }
    
        public int getNumberOfRecordsPerPage() {
            return abcPagingRequestType != null ? abcPagingRequestType.getNumberOfRecordsPerPage() : abdPagingRequestType.getNumberOfRecordsPerPage();
        }
    
        public int getStartAtRecordNumber() {
            return abcPagingRequestType != null ? abcPagingRequestType.getStartAtRecordNumber() : abdPagingRequestType.getStartAtRecordNumber();
        }
    }
    

    并将getPagination的参数类型改为:

    private HashMap<String, Integer> getPagination(PagingRequestTypeProxy pagingRequestType) {...}
    

    然后你可以像这样使用它:

    getPagination(new PagingRequestTypeProxy(abcPagingReqType));
    getPagination(new PagingRequestTypeProxy(abdPagingReqType));
    

    缺点是有额外的代码来定义PagingRequestTypeProxy,优点是PagingRequestTypeProxylogic 非常简单且易于维护,您可以将您的商业代码getPagination 放在一个地方.

    【讨论】: