2016-12-23 2 views
2

Предположим, у меня есть приложение, которому необходимо применить несколько настраиваемых преобразований для строк. Потребности будут возрастать по времени. Следующие два подхода делают то же самое, но мне интересно, какой из них более выгоден в долгосрочной перспективе. Они одинаковы? Или, вы предлагаете больше преимуществ, чем другие, поскольку количество преобразований увеличивается и меняется?Функциональные интерфейсы Java 8 функциональных интерфейсов

Предположим, мы имеем следующие:

public static final String PL = "("; 
public static final String PR = ")"; 
public static final String Q1 = "'"; 

Вот настройки каждого подхода и использования.

Подход 1:

@FunctionalInterface 
public interface StringFunction { 
    String applyFunction(String s); 
} 

public class StrUtils { 

    public static String transform(String s, StringFunction f) { 
     return f.applyFunction(s); 
    } 

    public static String putInQ1(String s) { 
     return Q1.concat(s).concat(Q1); 
    } 

    public static String putInParens(String s) { 
     return PL.concat(s).concat(PR); 
    } 

    // and so on... 
} 

Что я хотел бы использовать так:

System.out.println(StrUtils.transform("anSqlStr", StrUtils::putInQ1)); 
System.out.println(StrUtils.transform("sqlParams", StrUtils::putInParens)); 

подхода 2:

Здесь я использую простую функцию:

Function<String, String> putInQ1 = n -> Q1.concat(n).concat(Q1); 
Function<String, String> putInParens = n -> PL.concat(n).concat(PR); 
// and so on... 

Что я хотел бы использовать так:

System.out.println(putInQ1.apply("anSqlStr"); 
System.out.println(putInParens.apply("sqlParams"); 
+4

В чем смысл? Почему бы просто не использовать 'StrUtils.putInQ1 (« anSqlStr ») и' StrUtils.putInParens («sqlParams») '? И почему бы просто не использовать «return» («+ s +») «', что гораздо читаемо, а также более эффективно? –

+0

Метод '.concat()' намного медленнее, чем использование '+', которое оптимизирует компилятор. – 4castle

+1

Вам вообще не нужно определять 'StringFunction'. Вы можете просто использовать 'UnaryOperator '. (Не то, чтобы это было очень важно, потому что подход, предложенный JB Nizet, будет намного лучше в долгосрочной перспективе). – 4castle

ответ

-1

Почему бы не просто определить метод «putInWhatever (строка s, String влево, String справа) { возвращение левый + s + вправо; }

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

+1

Это совершенно не соответствует действительности. Вопрос задает вопрос о различии между пользовательскими функциональными интерфейсами и просто функциями библиотеки Java. Пример со строками является чисто гипотетическим. – HTNW

+0

Спасибо @HTNW! Наконец кто-то получает мой пост. – mohsenmadi

1

Вы набросали два способа предлагая определенную функциональность

  1. Первой явно предложить его как метод

    public static String putInQ1(String s) { 
        return Q1.concat(s).concat(Q1); 
    } 
    

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

  2. Второй, чтобы предложить его в качестве Function объекта:.

    Function<String, String> putInQ1 = n -> Q1.concat(n).concat(Q1); 
    

    (Здесь, вы не сказали, где эти случаи должны быть расположены, я предполагаю, что вы бы также создать класс, который содержал все эти Function случаи как (возможно public static final полей)


JBNizet упоминается третий вариант: могут использовать методы напрямую, а не через ссылки на методы. Действительно, цель функции transform не совсем ясна. Единственным оправданием для этого было бы то, что вы захотите передать там произвольные ссылки на методы, но эти ссылки на методы будут всего лишь Function объектов - как во втором подходе ...


Однако в техническом смысле разница не такая большая. Чтобы проиллюстрировать суть: оба подхода могут быть тривиально преобразованы друг в друга! Этот метод может быть реализован на основе функционального объекта

public static String putInQ1(String s) { 
    return putInQ1.apply(s); 
} 

И функциональный объект может быть создана из ссылки методы:

Function<String, String> putInQ1 = StringUtils::putInQ1; 

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

Для этого рассмотрит случай использования у вас есть входная строка, и хочет, чтобы положить его в ( скобки ), и результат в ' одинарных кавычек ':

String doItWithMethodReferences(String input) { 
    String result = input; 
    result = StrUtils.transform(result, StrUtils::putInParens); 
    result = StrUtils.transform(result, StrUtils::putInQ1); 
    return result; 
} 

String doItWithFunctionObjects(String input) { 
    String result = input; 
    result = StringFunctions.putInParens.apply(result); 
    result = StringFunctions.putInQ1.apply(result) 
    return result; 
} 

String doItWithMethods(String input) { 
    String result = input; 
    result = StrUtils.putInParens(result); 
    result = StrUtils.putInQ1(result); 
    return result; 
} 

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

Конечно, каждый из этих методов можно было бы записать «более компактно» в одной строке. Но в зависимости от количества и структуры операций это могло бы строго уменьшить читаемость, и на самом деле это приводит к другому моменту: я мог представить себе, что расширяемость может что-то рассмотреть. Представьте себе, что вы хотели создать сингл, который помещал строку в '( одинарные кавычки и круглые скобки )' сразу.

С методами:

public static String putInPandQ1(String s) { 
    return putInQ1(putInParens(s)); 
} 

С функциями:

Function<String, String> putInPandQ1 = putInParens.andThen(putInQ1); 

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

(Но, принимая, что сколь угодно далеко, надо спросить, есть ли вы на самом деле не пытается реализовать шаблон двигатель или новый предметно-ориентированный язык программирования ...)


Короткая заметка: Все это кажется довольно несвязанным с производительностью. Будут ли вы делать return s0 + s1; или return s0.concat(s1) часто, и в тех немногих случаях, когда это имеет значение, вы можете изменить реализацию позже - поскольку, учитывая функциональность, очерченную в вопросе, решение об использовании + или concat или некоторых StringBuilder обман - это точно: деталь реализации.

И еще одна записки, как указано в комментариях: Вместо определения своего собственного StringFunction интерфейса, вы можете использовать UnaryOperator<String>. Оба являются «структурно равными», но первая является частью стандартного API.Представьте, что там уже много библиотек, с методами, которые ожидают в качестве аргумента UnaryOperator<String>. Если у вас есть только ваши собственные StringFunction, вам, возможно, придется преобразовать эти экземпляры, чтобы ваш код мог взаимодействовать с другим кодом. Это, конечно, тривиально, но интерфейсы в пакете functional тщательно подобраны для охвата большого числа приложений, и я думаю, что взаимодействие между библиотеками может быть значительно увеличено, когда программисты не напрасно создают новые интерфейсы, которые уже существуют в стандартном API. Один может утверждать, что введение StringFunction упрощает код, поскольку для него не требуется общий параметр <String>. Но если вы хотите это, тогда вы должны просто объявить iterface как interface StringFunction extends UnaryOperator<String> { }, который просто является дополнительной специализацией и будет поддерживать совместимость с другим кодом. Кроме того, вы тогда удобно наследуете все методы default от Function, например, andThen, которые я описал выше.

+0

Спасибо, Марко, и спасибо за обработку проблемы '+' x 'concat(). Я знал, что это может быть вопросом предпочтения больше, чем что-либо еще, и это с точки зрения производительности, они похожи. Престижность для 'putInParens.andThen (putInQ1)', это плюс для функций Java 8 по сравнению со стандартным использованием метода. Я надеюсь получить больше ударов по обе стороны, когда мы пойдем вперед. И да, должны быть классификаторы 'public static final', если такой список функций будет расти долго, чтобы они лучше оказались в одном месте. – mohsenmadi

Смежные вопросы