Какой наилучший способ предоставить интерфейс для этих библиотечных функций?

#java #design-patterns #method-reference #functional-interface

#java #шаблоны проектирования #метод-ссылка #функциональный интерфейс

Вопрос:

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

 public interface PropTransformer<T, R> {
    R transform(T t);
    T reverseTransform(R r);
}
  
 public class ShapeTransformer implements PropTransformer<List<String>, Set<String>> {
    
    @Override
    public Set<String> transform(final List<String> list) {
        // transform code
    }

    @Override
    public List<String> reverseTransform(final Set<String> set) {
        // reverseTransform code
    }
}
  

Очевидно, что методы классов этой библиотеки могут быть вызваны с помощью new operator, но поскольку на стороне пользователя может быть выполнено множество преобразований, многие экземпляры будут казаться раздутыми. Что-то вроде:

 new ShapeTransformer().transform(...); 
new TemperatureTransformer().transform(...);
...
  

Я мог бы подумать о создании служебного класса Transformer , который обеспечивал бы прямой вызов для transformers, например

 public class Transformer<T, R> {

    private static ShapeTransformer shapeTransformer = new CompatibleDevicesTransformer();

    public static Set<String> shapeTransform(final List<String> list) {
        return shapeTransformer.transform(list);
    }

    public static List<String> shapeReverseTransform(final Set<String> set) {
        return shapeTransformer.reverseTransform(set);
    }
}
  

Пользователь библиотеки может затем использовать его как

 Transformer.shapeTransform(...)
  

Проблемы с этим подходом:

  • Он не будет масштабироваться, поскольку класс Transformer будет продолжать расти с увеличением числа реализуемых transformer PropTransformer .
  • Своего рода повторная работа выполняется путем добавления статических методов и переменных в Transformer класс

Есть ли более чистый / лучший способ добиться этого? По возможности, через ссылки на методы и / или функциональный интерфейс?

Что-то вроде

 Transformer t = new Transformer();
t.transform(ShapeTransformer::transform);
t.transform(TemperatureTransformer::transform);
  

(^ это просто для того, чтобы дать представление о том, что я ищу. Я не обязательно предлагаю сделать метод transformers статичным)

Комментарии:

1. Может ли клиентский код свободно использовать любую из PropTransformer реализаций или это зависит от какого-либо атрибута?

2. @Smile Все реализации независимы друг от друга. Клиент свободен и может использовать любое количество реализаций. Единственная причина, по которой реализации реализуют один и тот же интерфейс, заключается в поддержании контракта на роль преобразователя. Отвечает ли это на ваш вопрос?

Ответ №1:

PropTransformer не является функциональным интерфейсом, поскольку содержит более 1 абстрактного метода. Итак, вы не можете реализовать и вызвать эти методы, используя выражение lamda.

Вы можете разделить PropTransformer на функциональные интерфейсы PropTransformer и PropReverseTransformer. , но решение должно основываться на том, как PropTransformer вписаться в общий дизайн.

Чтобы еще больше сократить количество шаблонного кода на стороне клиента, вы можете создать PropTransformerFactory — утилиту для создания PropTransformer реализаций. Псевдокод:

 public class PropTransformerFactory {
    public static PropTransformer createShapeTransformer() {
        return new ShapeTransformer();
    }

    public static PropTransformer createTemperatureTransformer() {
        return new TemperatureTransformer();
    }
}