Рефактор полиморфизма с использованием Java 8 - PullRequest
8 голосов
/ 21 января 2020

У меня есть старая кодовая база, которую мне нужно реорганизовать с использованием Java 8, поэтому у меня есть интерфейс, который сообщает, поддерживает ли мой текущий сайт платформу.

public interface PlatformSupportHandler {   
  public abstract boolean isPaltformSupported(String platform);
}

и у меня есть несколько классов реализуя его, и каждый класс поддерживает свою платформу.

Несколько реализующих классов:

@Component("bsafePlatformSupportHandler")
public class BsafePlatoformSupportHandler implements PlatformSupportHandler {

  String[] supportedPlatform = {"iPad", "Android", "iPhone"};
  Set<String> supportedPlatformSet = new HashSet<>(Arrays.asList(supportedPlatform)); 

  @Override
  public boolean isPaltformSupported(String platform) {

    return supportedPlatformSet.contains(platform);
  }

}

Другая реализация:

@Component("discountPlatformSupportHandler")
public class DiscountPlatoformSupportHandler implements PlatformSupportHandler{

  String[] supportedPlatform = {"Android", "iPhone"};
  Set<String> supportedPlatformSet = new HashSet<>(Arrays.asList(supportedPlatform)); 

  @Override
  public boolean isPaltformSupported(String platform) {

  return supportedPlatformSet.contains(platform);
  }
}

Во время выполнения в моем фильтре Я получаю требуемый компонент:

platformSupportHandler = (PlatformSupportHandler) ApplicationContextUtil
  .getBean(subProductType + Constants.PLATFORM_SUPPORT_HANDLER_APPEND);

и вызываю isPlatformSupported, чтобы узнать, поддерживает ли мой текущий сайт следующую платформу или нет.

Я новичок в Java 8, есть ли способ реорганизовать этот код без создания нескольких классов? Поскольку интерфейс содержит только один метод, могу ли я как-то использовать лямбду для его рефакторинга?

Ответы [ 3 ]

5 голосов
/ 21 января 2020

Если вы хотите придерживаться текущего дизайна, вы можете сделать что-то вроде этого:

public class MyGeneralPurposeSupportHandler implements PlatformSupportHandler {
   private final Set<String> supportedPlatforms;
   public MyGeneralPurposeSupportHandler(Set<String> supportedPlatforms) {
      this.supportedPlatforms = supportedPlatforms;
   } 

   public boolean isPlatformSupported(String platform) {
     return supportedPlatforms.contains(platform);
   }
}

// now in configuration:

@Configuration
class MySpringConfig {

    @Bean
    @Qualifier("discountPlatformSupportHandler") 
    public PlatformSupportHandler discountPlatformSupportHandler() {
       return new MyGeneralPurposeSupportHandler(new HashSefOf({"Android", "iPhone"})); // yeah its not a java syntax, but you get the idea
    }

    @Bean
    @Qualifier("bsafePlatformSupportHandler") 
    public PlatformSupportHandler bsafePlatformSupportHandler() {
       return new MyGeneralPurposeSupportHandler(new HashSefOf({"Android", "iPhone", "iPad"}));
    }   

}

Этот метод имеет преимущество в том, что не создает класс для типа (discount, bsafe, et c) , так что это отвечает на вопрос.

Идем дальше, что произойдет, если не будет запрошен тип, в настоящее время он потерпит неудачу, потому что бин не существует в контексте приложения - не очень хороший подход.

Таким образом, вы можете создать карту типа для набора поддерживаемых платформ, сохранить карту в конфигурации или что-то еще, чтобы пружина сделала свое волшебство c. В итоге вы получите что-то вроде этого:

public class SupportHandler {

   private final Map<String, Set<String>> platformTypeToSuportedPlatforms;

   public SupportHandler(Map<String, Set<String>> map) {
       this.platformTypeToSupportedPlatforms = map; 
   }

   public boolean isPaltformSupported(String type) {
        Set<String> supportedPlatforms = platformTypeToSupportedPlatforms.get(type);
        if(supportedPlatforms == null) {
          return false; // or maybe throw an exception, the point is that you don't deal with spring here which is good since spring shouldn't interfere with your business code
        }
        return supportedPlatforms.contains(type);

   }
}

@Configuration 
public class MyConfiguration {

    // Configuration conf is supposed to be your own way to read configurations in the project - so you'll have to implement it somehow
    @Bean
    public SupportHandler supportHandler(Configuration conf) {
      return new SupportHandler(conf.getRequiredMap());
    }
}

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

Однако в обоих методах отсутствуют функции java 8;)

4 голосов
/ 21 января 2020

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

@Configuration 
public class AppConfiguration {

    @Bean(name = "discountPlatformSupportHandler")
    public PlatformSupportHandler discountPlatformSupportHandler() {
        String[] supportedPlatforms = {"Android", "iPhone"};
        return getPlatformSupportHandler(supportedPlatforms);
    }

    @Bean(name = "bsafePlatformSupportHandler")
    public PlatformSupportHandler bsafePlatformSupportHandler() {
        String[] supportedPlatforms = {"iPad", "Android", "iPhone"};
        return getPlatformSupportHandler(supportedPlatforms);
    }

    private PlatformSupportHandler getPlatformSupportHandler(String[] supportedPlatforms) {
        return platform -> Arrays.asList(supportedPlatforms).contains(platform);
    }
}

Кроме того, когда вы хотите использовать бин, это снова очень просто:

@Component
class PlatformSupport {

    // map of bean name vs bean, automatically created by Spring for you
    private final Map<String, PlatformSupportHandler> platformSupportHandlers;

    @Autowired // Constructor injection
    public PlatformSupport(Map<String, PlatformSupportHandler> platformSupportHandlers) {
        this.platformSupportHandlers = platformSupportHandlers;
    }

    public void method1(String subProductType) {
        PlatformSupportHandler platformSupportHandler = platformSupportHandlers.get(subProductType + Constants.PLATFORM_SUPPORT_HANDLER_APPEND);
    }
}

3 голосов
/ 21 января 2020

Как было написано в Марк Брамник ответ, вы можете переместить это в конфигурацию.

Предположим, что это будет в yaml таким образом:

platforms:
    bsafePlatformSupportHandler: ["iPad", "Android", "iPhone"]
    discountPlatformSupportHandler: ["Android", "iPhone"]

Затем вы можете создать класс конфигурации, чтобы прочитать это:

@Configuration
@EnableConfigurationProperties
@ConfigurationProperties
public class Config {

    private Map<String, List<String>> platforms = new HashMap<String, List<String>>();

    // getters and setters

Вы можете создать обработчик с проверкой кода. Или поместите его в свой фильтр, как показано ниже:

@Autowired
private Config config;

...

public boolean isPlatformSupported(String subProductType, String platform) {
    String key = subProductType + Constants.PLATFORM_SUPPORT_HANDLER_APPEND;
    return config.getPlatforms()
        .getOrDefault(key, Collections.emptyList())
        .contains(platform);
}
...