简体   繁体   English

在方法中从父 class 动态转换为适当的子类

[英]Dynamically cast to a proper subclass from parent class in a method

I have a Java interface PlatformConfigurable .我有一个 Java interface PlatformConfigurable I also have two classes PlatformProducerConfig and PlatformConsumerConfig .我也有两个类PlatformProducerConfigPlatformConsumerConfig

Later on, I need to add a common config to both that sets a property to an empty string:稍后,我需要向两者添加一个通用配置,将属性设置为空字符串:

    private PlatformConfigurable disableHostNameVerificationConfig(PlatformConfigurable platformConfig) {
        if (platformConfig instanceof PlatformProducerConfig) {
            PlatformProducerConfig oldConfig = (PlatformProducerConfig) platformConfig;
            Map<String, String> additionalConfig = oldConfig.additionalProperties();
            Map<String, String> newConfig = new HashMap<>(Optional.ofNullable(additionalConfig).orElseGet(ImmutableMap::of));
            newConfig.put(SslConfigs.SSL_ENDPOINT_IDENTIFICATION_ALGORITHM_CONFIG, "");
            return oldConfig.toBuilder().additionalProperties(newConfig).build();
        }
        else if (platformConfig instanceof PlatformConsumerConfig) {
            PlatformConsumerConfig oldConfig = (PlatformConsumerConfig) platformConfig;
            Map<String, String> additionalConfig = platformConfig.additionalProperties();
            Map<String, String> newConfig = new HashMap<>(Optional.ofNullable(additionalConfig).orElseGet(ImmutableMap::of));
            newConfig.put(SslConfigs.SSL_ENDPOINT_IDENTIFICATION_ALGORITHM_CONFIG, "");
            return oldConfig.toBuilder().additionalProperties(newConfig).build();
        }
        return platformConfig;
    }

I am casting to producer or consumer config because the PlatformConfigurable interface doesn't have .toBuilder() or .build() methods declared in it, and I don't have access to modify the interface, as I can only implement it.我正在转换为生产者或消费者配置,因为PlatformConfigurable接口没有在其中声明.toBuilder()或 .build( .build()方法,并且我无权修改接口,因为我只能实现它。

I would want to get rid of the duplicate code:我想摆脱重复的代码:

Map<String, String> additionalConfig = platformConfig.additionalProperties();
            Map<String, String> newConfig = new HashMap<>(Optional.ofNullable(additionalConfig).orElseGet(ImmutableMap::of));
            newConfig.put(SslConfigs.SSL_ENDPOINT_IDENTIFICATION_ALGORITHM_CONFIG, "");
            return oldConfig.toBuilder().additionalProperties(newConfig).build();

I was thinking of using lambdas, but I am not 100% sure how to do it.我正在考虑使用 lambdas,但我不是 100% 确定该怎么做。

You could just refactor existing code like this:您可以像这样重构现有代码:

private PlatfromConfigurable disableHostNameVerificationConfig(Platfromonfigurable platfromConfig) {
    if (!(platformConfig instanceof PlatformProducerConfig) && !(platformConfig instanceof PlatformConsumerConfig)) {
        return platformConfig;
    }

    Map<String, String> additionalConfig = platformConfig.additionalProperties();
    Map<String, String> newConfig = new HashMap<>(Optional.ofNullable(additionalConfig).orElseGet(ImmutableMap::of));
    newConfig.put(SslConfigs.SSL_ENDPOINT_IDENTIFICATION_ALGORITHM_CONFIG, "");

    if (platformConfig instanceof PlatformProducerConfig) {
        return ((PlatformProducerConfig)platformConfig).toBuilder().additionalProperties(newConfig).build();
    }
    return ((PlatformConsumerConfig)platformConfig).toBuilder().additionalProperties(newConfig).build();
}

Update更新

Another approach could be to extract functionality related to the builder to separate interfaces and use them in this way:另一种方法可能是提取与构建器相关的功能以分离接口并以这种方式使用它们:

// 1. extend existing `PlatformConfigurable`
public interface BuilderedPlatformConfigurable extends PlatformConfigurable {
    ConfigPlatformBuilder toBuilder();
}

// 2. provide builder interface with common implementation
public interface ConfigPlatformBuilder {
    Map<String, String> additionalProperties = new HashMap<>();

    BuilderedPlatformConfigurable build();

    default ConfigPlatformBuilder additionalProperties(Map<String, String> properties) {
        this.additionalProperties.clear();
        this.additionalProperties.putAll(properties);
        return this;
    }
}

// 3. update PlatformConsumerConfig class (similarly, PlatformProducerConfig)
public class PlatformConsumerConfig implements BuilderedPlatformConfigurable {
    private Map<String, String> additionalProperties = new HashMap<>();

    @Override
    public Map<String, String> additionalProperties() {
        return additionalProperties;
    }

    public ConfigPlatformBuilder toBuilder() {
        return new Builder();
    }

    public static class Builder implements ConfigPlatformBuilder {
        public PlatformConsumerConfig build() {
            PlatformConsumerConfig config = new PlatformConsumerConfig();
            config.additionalPropertie.putAll(this.additionalProperties);
            return config;
        }
    }
}

// 4. provide overloaded method
private PlatformConfigurable disableHostNameVerificationConfig(PlatformConfigurable platformConfig) {
    return platformConfig;
}

private PlatformConfigurable disableHostNameVerificationConfig(BuilderedPlatformConfigurable platformConfig) {
    Map<String, String> additionalConfig = platformConfig.additionalProperties();
    Map<String, String> newConfig = new HashMap<>(Optional.ofNullable(additionalConfig).orElseGet(Map::of));
    newConfig.put(SslConfigs.SSL_ENDPOINT_IDENTIFICATION_ALGORITHM_CONFIG, "");

    return platformConfig.toBuilder().additionalProperties(newConfig).build();
}

Taking Alex Rudenko's answer a bit further, using generics:使用 generics 进一步了解Alex Rudenko 的答案

private <P extends PlatformConfigurable> P disableHostNameVerificationConfig(P platformConfig, BiFunction<P, Map<String, String>, P> appender) {
    Map<String, String> additionalConfig = platformConfig.additionalProperties();
    Map<String, String> newConfig = new HashMap<>(Optional.ofNullable(additionalConfig).orElseGet(ImmutableMap::of));
    newConfig.put(SslConfigs.SSL_ENDPOINT_IDENTIFICATION_ALGORITHM_CONFIG, "");

    return appender.apply(platformConfig, newConfig);
}

This assumes that it is safe to do this for any subtype of PlatformConfigurable (and PlatformConfigurable itself).这假定对PlatformConfigurable的任何子类型(以及PlatformConfigurable本身)执行此操作是安全的。

Then invoke like:然后像这样调用:

disableHostNameVerificationConfig(
    platformProducerConfig,
    (p, config) -> p.toBuilder().setAdditionalConfig(config).build());

disableHostNameVerificationConfig(
    platformConsumerConfig,
    (p, config) -> p.toBuilder().setAdditionalConfig(config).build());

If you like, create helper methods to hide the BiFunction s:如果您愿意,可以创建帮助方法来隐藏BiFunction

private PlatformProducerConfig disableHostNameVerificationConfig(PlatformProducerConfig config) {
  return disableHostNameVerificationConfig(
      platformConfigurable,
      (p, config) -> p.toBuilder().setAdditionalConfig(config).build());
}

private PlatformConsumerConfig disableHostNameVerificationConfig(PlatformConsumerConfig config) {
  return disableHostNameVerificationConfig(
      platformConfigurable,
      (p, config) -> p.toBuilder().setAdditionalConfig(config).build());
}

Actually, I think a better way to do it would be without generics or lambdas: write a method which creates an updated map:实际上,我认为更好的方法是没有 generics 或 lambda:编写一个创建更新的 map 的方法:

private static Map<String, String> newConfig(PlatformConfigurable platformConfig) {
  Map<String, String> additionalConfig = platformConfig.additionalProperties();
  Map<String, String> newConfig = additionalConfig != null ? new HashMap<>(additionalConfig) : new HashMap<>();

  newConfig.put(SslConfigs.SSL_ENDPOINT_IDENTIFICATION_ALGORITHM_CONFIG, "");
  return newConfig;
}

and then just have two overloads:然后只有两个重载:

private PlatformProducerConfig disableHostNameVerificationConfig(PlatformProducerConfig config) {
  return config.toBuilder().setAdditionalConfig(newConfig(config)).build();
} 

private PlatformConsumerConfig disableHostNameVerificationConfig(PlatformConsumerConfig config) {
  return config.toBuilder().setAdditionalConfig(newConfig(config)).build();
} 

Adding one thing in Alex Rudenko's answer, and making different function to add different implementations of interfaces.在 Alex Rudenko 的回答中添加一件事,并制作不同的 function 以添加不同的接口实现。

private PlatformConfigurable disableHostNameVerificationConfig(PlatformConfigurable platformConfig) {
    if ((platformConfig == null)) {
        return platformConfig;
    }

    Map<String, String> additionalConfig = platformConfig.additionalProperties();
    Map<String, String> newConfig = new HashMap<>(Optional.ofNullable(additionalConfig).orElseGet(ImmutableMap::of));
    newConfig.put(SslConfigs.SSL_ENDPOINT_IDENTIFICATION_ALGORITHM_CONFIG, "");
    return PlatformConfigurableObject(platformConfig, newConfig);
}

So you can handle all instances in a different method, and whenever PlatfromXCofing classes are added later you only have to change this method.因此,您可以使用不同的方法处理所有实例,并且以后添加 PlatfromXCofing 类时,您只需更改此方法。 Single Responsibility Principle.单一职责原则。

private PlatformConfigurable PlatformConfigurableObject(PlatformConfigurable platformConfig, Map<String, String> newConfig){
    if (platformConfig instanceof PlatformProducerConfig) {
        return ((PlatformProducerConfig)platformConfig).toBuilder().additionalProperties(newConfig).build();
    } else if (platformConfig instanceof PlatformConsumerConfig){
        return ((PlatformConsumerConfig)platformConfig).toBuilder().additionalProperties(newConfig).build();
    } else{
    return platformConfig;
    }
}

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

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