动态地从父类转换为合适的子类在一个方法中。

huangapple go评论70阅读模式
英文:

Dynamically cast to a proper subclass from parent class in a method

问题

我有一个Java interface 叫做 PlatformConfigurable。我还有两个类 PlatformProducerConfigPlatformConsumerConfig

后来,我需要为这两个类添加一个共同的配置,将一个属性设置为空字符串:

    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;
    }

我进行了对生产者或消费者配置的强制类型转换,因为 PlatformConfigurable 接口中没有声明 .toBuilder().build() 方法,而且我无法访问修改该接口,我只能实现它。

我希望摆脱重复的代码:

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();

我在考虑使用Lambda表达式,但我不确定如何做到这一点。

英文:

I have a Java interface PlatformConfigurable. I also have two classes PlatformProducerConfig and PlatformConsumerConfig.

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&lt;String, String&gt; additionalConfig = oldConfig.additionalProperties();
            Map&lt;String, String&gt; newConfig = new HashMap&lt;&gt;(Optional.ofNullable(additionalConfig).orElseGet(ImmutableMap::of));
            newConfig.put(SslConfigs.SSL_ENDPOINT_IDENTIFICATION_ALGORITHM_CONFIG, &quot;&quot;);
            return oldConfig.toBuilder().additionalProperties(newConfig).build();
        }
        else if (platformConfig instanceof PlatformConsumerConfig) {
            PlatformConsumerConfig oldConfig = (PlatformConsumerConfig) platformConfig;
            Map&lt;String, String&gt; additionalConfig = platformConfig.additionalProperties();
            Map&lt;String, String&gt; newConfig = new HashMap&lt;&gt;(Optional.ofNullable(additionalConfig).orElseGet(ImmutableMap::of));
            newConfig.put(SslConfigs.SSL_ENDPOINT_IDENTIFICATION_ALGORITHM_CONFIG, &quot;&quot;);
            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.

I would want to get rid of the duplicate code:

Map&lt;String, String&gt; additionalConfig = platformConfig.additionalProperties();
            Map&lt;String, String&gt; newConfig = new HashMap&lt;&gt;(Optional.ofNullable(additionalConfig).orElseGet(ImmutableMap::of));
            newConfig.put(SslConfigs.SSL_ENDPOINT_IDENTIFICATION_ALGORITHM_CONFIG, &quot;&quot;);
            return oldConfig.toBuilder().additionalProperties(newConfig).build();

I was thinking of using lambdas, but I am not 100% sure how to do it.

答案1

得分: 5

你可以像这样重构现有的代码:

private PlatformConfigurable disableHostNameVerificationConfig(PlatformConfigurable platformConfig) {
    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(Collections::emptyMap));
    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();
}

更新

另一种方法是将与构建器相关的功能提取到单独的接口中,并在代码中使用它们:

// 1. 扩展现有的 `PlatformConfigurable`
public interface BuilderedPlatformConfigurable extends PlatformConfigurable {
    ConfigPlatformBuilder toBuilder();
}

// 2. 提供具有共同实现的构建器接口
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. 更新 PlatformConsumerConfig 类(类似地,更新 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.additionalProperties.putAll(this.additionalProperties);
            return config;
        }
    }
}

// 4. 提供重载的方法
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(Collections::emptyMap));
    newConfig.put(SslConfigs.SSL_ENDPOINT_IDENTIFICATION_ALGORITHM_CONFIG, "");

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

注意:这只是您提供的代码的翻译部分,没有其他附加内容。

英文:

You could just refactor existing code like this:

private PlatfromConfigurable disableHostNameVerificationConfig(Platfromonfigurable platfromConfig) {
if (!(platformConfig instanceof PlatformProducerConfig) &amp;&amp; !(platformConfig instanceof PlatformConsumerConfig)) {
return platformConfig;
}
Map&lt;String, String&gt; additionalConfig = platformConfig.additionalProperties();
Map&lt;String, String&gt; newConfig = new HashMap&lt;&gt;(Optional.ofNullable(additionalConfig).orElseGet(ImmutableMap::of));
newConfig.put(SslConfigs.SSL_ENDPOINT_IDENTIFICATION_ALGORITHM_CONFIG, &quot;&quot;);
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&lt;String, String&gt; additionalProperties = new HashMap&lt;&gt;();
BuilderedPlatformConfigurable build();
default ConfigPlatformBuilder additionalProperties(Map&lt;String, String&gt; properties) {
this.additionalProperties.clear();
this.additionalProperties.putAll(properties);
return this;
}
}
// 3. update PlatformConsumerConfig class (similarly, PlatformProducerConfig)
public class PlatformConsumerConfig implements BuilderedPlatformConfigurable {
private Map&lt;String, String&gt; additionalProperties = new HashMap&lt;&gt;();
@Override
public Map&lt;String, String&gt; 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&lt;String, String&gt; additionalConfig = platformConfig.additionalProperties();
Map&lt;String, String&gt; newConfig = new HashMap&lt;&gt;(Optional.ofNullable(additionalConfig).orElseGet(Map::of));
newConfig.put(SslConfigs.SSL_ENDPOINT_IDENTIFICATION_ALGORITHM_CONFIG, &quot;&quot;);
return platformConfig.toBuilder().additionalProperties(newConfig).build();
}

答案2

得分: 4

使用泛型,对 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);
}

假设对于任何 PlatformConfigurable 的子类型(包括 PlatformConfigurable 本身),这样做是安全的。

然后可以这样调用:

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

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

如果愿意,可以创建辅助方法来隐藏 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());
}

实际上,我认为更好的方法是不使用泛型或 lambda 表达式:编写一个方法来创建更新后的映射:

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;
}

然后只需编写两个重载方法:

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

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

Taking Alex Rudenko's answer a bit further, using generics:

private &lt;P extends PlatformConfigurable&gt; P disableHostNameVerificationConfig(P platformConfig, BiFunction&lt;P, Map&lt;String, String&gt;, P&gt; appender) {
Map&lt;String, String&gt; additionalConfig = platformConfig.additionalProperties();
Map&lt;String, String&gt; newConfig = new HashMap&lt;&gt;(Optional.ofNullable(additionalConfig).orElseGet(ImmutableMap::of));
newConfig.put(SslConfigs.SSL_ENDPOINT_IDENTIFICATION_ALGORITHM_CONFIG, &quot;&quot;);
return appender.apply(platformConfig, newConfig);
}

This assumes that it is safe to do this for any subtype of PlatformConfigurable (and PlatformConfigurable itself).

Then invoke like:

disableHostNameVerificationConfig(
platformProducerConfig,
(p, config) -&gt; p.toBuilder().setAdditionalConfig(config).build());
disableHostNameVerificationConfig(
platformConsumerConfig,
(p, config) -&gt; p.toBuilder().setAdditionalConfig(config).build());

If you like, create helper methods to hide the BiFunctions:

private PlatformProducerConfig disableHostNameVerificationConfig(PlatformProducerConfig config) {
return disableHostNameVerificationConfig(
platformConfigurable,
(p, config) -&gt; p.toBuilder().setAdditionalConfig(config).build());
}
private PlatformConsumerConfig disableHostNameVerificationConfig(PlatformConsumerConfig config) {
return disableHostNameVerificationConfig(
platformConfigurable,
(p, config) -&gt; 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:

private static Map&lt;String, String&gt; newConfig(PlatformConfigurable platformConfig) {
Map&lt;String, String&gt; additionalConfig = platformConfig.additionalProperties();
Map&lt;String, String&gt; newConfig = additionalConfig != null ? new HashMap&lt;&gt;(additionalConfig) : new HashMap&lt;&gt;();
newConfig.put(SslConfigs.SSL_ENDPOINT_IDENTIFICATION_ALGORITHM_CONFIG, &quot;&quot;);
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();
} 

答案3

得分: 1

// 添加在Alex Rudenko的回答中的一点,并创建不同函数以添加不同接口的实现。

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);
}

// 因此,您可以在不同的方法中处理所有实例,以后只需更改此方法即可添加后续的PlatformXCofing类。单一责任原则。

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;
    }
}
英文:

Adding one thing in Alex Rudenko's answer, and making different function to add different implementations of interfaces.

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

    Map&lt;String, String&gt; additionalConfig = platformConfig.additionalProperties();
    Map&lt;String, String&gt; newConfig = new HashMap&lt;&gt;(Optional.ofNullable(additionalConfig).orElseGet(ImmutableMap::of));
    newConfig.put(SslConfigs.SSL_ENDPOINT_IDENTIFICATION_ALGORITHM_CONFIG, &quot;&quot;);
    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. Single Responsibility Principle.

private PlatformConfigurable PlatformConfigurableObject(PlatformConfigurable platformConfig, Map&lt;String, String&gt; 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;
    }
}

huangapple
  • 本文由 发表于 2020年5月29日 23:57:21
  • 转载请务必保留本文链接:https://go.coder-hub.com/62089984.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定