Динамически приводить к соответствующему подклассу из родительского класса в методе - PullRequest
1 голос
/ 29 мая 2020

У меня Java interface PlatformConfigurable. У меня также есть два класса PlatformProducerConfig и PlatformConsumerConfig.

Позже мне нужно добавить общую конфигурацию к обоим, которая устанавливает свойство в пустую строку:

    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 я передаю конфигурацию производителя или потребителя, потому что интерфейс 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();

Я думал об использовании лямбда-выражений, но не уверен на 100%, как это сделать.

Ответы [ 3 ]

6 голосов
/ 29 мая 2020

Вы можете просто реорганизовать существующий код следующим образом:

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

Другой подход может заключаться в извлечении функциональности, связанной с построителем, для разделения интерфейсов и использования их таким образом:

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

5 голосов
/ 29 мая 2020

Взяв ответ Алекса Руденко немного дальше, используя дженерики:

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

Это предполагает, что это безопасно сделать для любого подтипа PlatformConfigurablePlatformConfigurable сам).

Затем вызовите, например:

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

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

Если хотите, создайте вспомогательные методы, чтобы скрыть BiFunction s:

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

Фактически , Я думаю, что лучший способ сделать это было бы без дженериков или лямбда-выражений: напишите метод, который создает обновленную карту:

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();
} 
1 голос
/ 29 мая 2020

Добавление одной вещи в ответ Алекса Руденко и создание другой функции для добавления разных реализаций интерфейсов.

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

Таким образом, вы можете обрабатывать все экземпляры другим методом, и всякий раз, когда классы PlatfromXCofing добавляются позже, вы нужно только изменить этот метод. Принцип единой ответственности.

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

...