微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Java - 通用工厂类

如何解决Java - 通用工厂类

我有一个使用泛型的 HTTP 客户端类。我想创建一个带有 (key,value) => (String type,HttpClient>) 映射的工厂类。

HttpClient 类有一个函数,它只根据泛型类类型将数据发送到服务器。基本上我想要的是一个发送数据的通用类,就是这样。

问题是我有多个类,我想使用工厂类以便:

  1. 简化 HttpClient 对象的创建。
  2. 避免一直使用“new”关键字,因为我可以在整个应用程序中使用 HttpClient<Some Object> 类的相同实例。

请看下面的代码

工厂

public class SystemPreferencesFactory {

    private static SystemPreferencesFactory factory = null;
    private  Map<String,PreferencesHTTPClient<? extends ISystemPreferences>> preferencesMap;

    private SystemPreferencesFactory(){
        this.preferencesMap = Map.of
                (PreferencesHTTPType.DUT_PREFERENCES.getName(),new PreferencesHTTPClient<DutPreferencesDTO>(PreferencesHTTPType.DUT_PREFERENCES.getUrl()),PreferencesHTTPType.MACHINE_PROPERTIES.getName(),new PreferencesHTTPClient<MachineProperties>(PreferencesHTTPType.MACHINE_PROPERTIES.getUrl())
                );
    }

    public static PreferencesHTTPClient<? extends ISystemPreferences> getPreferencesHTTPClient(String type) {
        if (factory == null) {
            factory = new SystemPreferencesFactory();
        }
        return factory.preferencesMap.get(type);
    }

}

HttpClient

public class PreferencesHTTPClient<T> extends HTTPClient {
    private static final Logger logger = LoggerManager.getLogger();
    private static String route = "";

    public PreferencesHTTPClient(String route){
        this.route = route;
    }

    public Future<HttpResponse> put(T dto) {
        try {
            System.err.println(dto.getClass());
            HttpPut request = new HttpPut(URIAffix + route + "/" + SystemPreferences.systemPreferences().getSetupName());
            request.addHeader("Authorization",authorizationPassword);
            request.addHeader("Content-Type","application/json");
            request.setEntity(new StringEntity(objectMapper.writeValueAsstring(dto)));

            return getClient().execute(request,new FutureCallback<>() {
                        @Override
                        public void completed(final HttpResponse response) {
                            logger.info("update request succeeded");
                        }

                        @Override
                        public void Failed(Exception ex) {
                            logger.error("update request Failed: {}",ex.getMessage());
                        }

                        @Override
                        public void cancelled() {
                            logger.error("update request canceled");
                        }
                    });
        } catch (JsonProcessingException | UnsupportedEncodingException e) {
            logger.error("update request Failed: {}",e.getMessage());
        }

        return CompletableFuture.completedFuture(null);
    }
}

当我尝试在 main 中调用它时出现编译错误

SystemPreferencesFactory.getPreferencesHTTPClient(PreferencesHTTPType.DUT_PREFERENCES.getName())
                 .put(converterUtil.DutFromEntityToDTO(dp));

编译错误是:

required type:
capture of ? extends ISystemPreferences
Provided:
DutPreferencesDTO

我的 DutPreferencesDTO 类声明如下:

public class DutPreferencesDTO implements ISystemPreferences

我的语法有什么问题?

解决方法

我们可以修改getPreferencesHTTPClient如下:

public class SystemPreferencesFactory {

...
                  // Change to generic method
    public static <T extends ISystemPreferences> PreferencesHTTPClient<T> getPreferencesHTTPClient(String type) {
        if (factory == null) {
            factory = new SystemPreferencesFactory();
        }
        // cast result explicitly
        return (PreferencesHTTPClient<T>) factory.preferencesMap.get(type);
    }

    public static void main(String[] args) {
        SystemPreferencesFactory.getPreferencesHTTPClient(PreferencesHTTPType.DUT_PREFERENCES.name())
                .put(new DutPreferencesDTO());
        // Oops wrong dto still compiles
        SystemPreferencesFactory.getPreferencesHTTPClient(PreferencesHTTPType.DUT_PREFERENCES.name())
                .put(new MachineProperties());
        // Assign to a local variable to infer
        PreferencesHTTPClient<DutPreferencesDTO> preferencesHTTPClient = SystemPreferencesFactory.getPreferencesHTTPClient(PreferencesHTTPType.DUT_PREFERENCES.name());
        preferencesHTTPClient.put(new DutPreferencesDTO());
        // Compile error;
        preferencesHTTPClient.put(new MachineProperties());
        // Use type witness to guard
        SystemPreferencesFactory
                .<DutPreferencesDTO>getPreferencesHTTPClient(PreferencesHTTPType.DUT_PREFERENCES.name())
                // Compile error;
                .put(new MachineProperties());
    }
}

看起来还不错。 但是正如 main 方法中所展示的那样,有一个缺点。

我们需要在调用 getPreferencesHTTPClient显式推断类型,以获得所需的类型检查。但是其他使用这种方法的人可能很容易忘记这样做,并且在调用 put 方法时可能会输入错误的 DTO 类型。

所以为了避免这样的问题,我们使用DTO的类,而不是String中的getPreferencesHTTPClient,如下所示:

public class SystemPreferencesFactorySafe {
    private static SystemPreferencesFactorySafe factory = null;
    private Map<Class<?>,PreferencesHTTPClient<? extends ISystemPreferences>> preferencesMap;

    private SystemPreferencesFactorySafe() {
        this.preferencesMap = Map.of
                (DutPreferencesDTO.class,new PreferencesHTTPClient<DutPreferencesDTO>(PreferencesHTTPType.DUT_PREFERENCES.getUrl()),MachineProperties.class,new PreferencesHTTPClient<MachineProperties>(PreferencesHTTPType.MACHINE_PROPERTIES.getUrl())
                );
    }
                  // T is inferred by type this time
    public static <T extends ISystemPreferences> PreferencesHTTPClient<T> getPreferencesHTTPClient(Class<T> type) {
        if (factory == null) {
            factory = new SystemPreferencesFactorySafe();
        }
        return (PreferencesHTTPClient<T>) factory.preferencesMap.get(type);
    }

    public static void main(String[] args) {
        SystemPreferencesFactorySafe.getPreferencesHTTPClient(DutPreferencesDTO.class)
                .put(new DutPreferencesDTO());
        // Good we see compile error.
        SystemPreferencesFactorySafe.getPreferencesHTTPClient(DutPreferencesDTO.class)
                .put(new MachineProperties());
    }
}

由于类型是使用参数推断的,当有人输入错误的 DTO 类型时,我们不需要显式推断来得到编译错误。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。