Автоматическое упрощение кода с помощью рефакторинга

Есть ли инструмент для автоматического упрощения кода Java с помощью рефакторинга?

Есть такие инструменты для Python и C:

но я не знаю ни одной такой программы для Java.

Есть много случаев, когда инструмент может автоматически упростить код, например:

  • петли: for (String s: a) { b.add(s); }b.addAll(a);
  • если/возврат: if (x) return true; else return false;return x;
  • тернарный оператор: if (x) {result = a;} else {result = b;}result = x ? a : b;
  • автоматический рефакторинг для алмазного оператора / исключение multi catch / лямбда-выражения
  • еще больше...

Преимуществом такого автоматического рефакторинга является удаление множества шаблонных строк кода, что делает его более чистым и лаконичным.


person Marcin    schedule 23.01.2015    source источник
comment
Вопрос не по теме: Вопросы, в которых нас просят порекомендовать или найти книгу, инструмент, библиотеку программного обеспечения, учебник или другой сторонний ресурс, не относятся к теме Stack Overflow   -  person meskobalazs    schedule 24.01.2015
comment
если (х) вернуть истину; → вернуть х; ???   -  person Ira Baxter    schedule 24.01.2015
comment
@ Ира хорошая мысль, это ошибка :) отредактировано   -  person Marcin    schedule 24.01.2015
comment
Если вам нужны другие конкретные предложения, вам следует отправить запрос на дочерний сайт Stack Exchange с рекомендациями по программному обеспечению. –   -  person Ira Baxter    schedule 29.01.2015
comment
Кроме ответа Айры Бакстер, я ничего не слышал. Ваша IDE может что-то делать, но обычно не автоматически. Существуют инструменты статического анализа, которые могут помочь вам выявить фрагменты кода, которые необходимо изменить. Может быть, есть какие-то плагины для IDE? В противном случае вам понадобится какой-то парсер + много правил. Я слышал, что Refaster будет хорош для таких вещей, когда он наконец будет выпущен. Кроме того, сайт autorefactor.org выглядит хитом.   -  person Petr Janeček    schedule 29.01.2015


Ответы (2)


Конечно. Ознакомьтесь с Инструментами преобразования программы (PTS). Такие инструменты предоставляют средства для определения грамматики, использования этой грамматики для синтаксического анализа исходного текста в AST и точной печати AST обратно в текст. Что еще более важно, одна и та же грамматика используется для чтения правил преобразования от источника к источнику, что позволяет напрямую выражать преобразования (или выражать составные преобразования в терминах наборов перезаписи). Такие преобразования используют выраженные шаблоны для сопоставления с AST и модифицируют AST; эти преобразования, основанные на структуре, не путаются из-за макета или пробелов.

Например, наш набор инструментов для реинжиниринга программного обеспечения DMS позволяет вам писать правила в форме если вы видите это, замените это на это, принимая общую форму:

 rule rule_name(pattern_variables)
   : pattern_syntax_category -> replacement_syntax_category
 = metaquote  pattern_text_in_specified_language metaquote
 => metaquote replacement_text_in_specified_language metaquote
 if condition_over_bound_pattern_variables;

куда

  • правило – это синтаксис, вводящий правило.
  • rule_name дает правилу имя, отличающее его от других правил,
  • pattern_variables — это список пар n:c имени переменной шаблона n и синтаксической категории c, которые n должно удовлетворять,
  • pattern_syntax_category – это синтаксическая категория, которой должен удовлетворять pattern_text.
  • replacement_syntax_category – это синтаксическая категория, которой должен удовлетворять replacement_text. Обычно и во всех приведенных здесь примерах совпадает с pattern_syntax_category ; при использовании правил перезаписи для перевода между языками они часто отличаются, потому что категории синтаксиса в языках различаются.
  • метакавычка – это символ ", который отделяет синтаксис языка правил от синтаксиса шаблона или замещающего языка.
  • text_pattern_in_specified_language – это правильно сформированный фрагмент исходного языка, содержащий переменные шаблона n, записанные как \n; любое совпадение шаблона связывает переменные шаблона с поддеревьями. Если \n встречается в шаблоне дважды, оба экземпляра должны быть идентичными.
  • replacement_text_in_specified_language — правильно сформированный фрагмент целевого языка. Найденный шаблон заменяется заменой, при этом любые переменные шаблона в замене заменяются значением связанной переменной шаблона.
  • if — это синтаксис, вводящий условие
  • condition_over_bound_pattern_variables – это ограничение, которому должны удовлетворять переменные шаблона после того, как будет найдено совпадение с шаблоном. Это часто используется для проверки условий контекста, которые должны быть удовлетворены.

DMS позволит вам написать и применить следующие примеры преобразований (включая некоторые примеры OP):

 default domain Java~v8; -- specify v8 dialect of Java to manipulate

 rule reduce_strength_squared(e: term):
     :product -> product
 = "\e ^ 2 " ==> "\e * \e "
 if no_side_effects(e);

 rule optimize_divide_by_self(e: term):
     :product -> product
 = " \e / \e " => " 1 " if is_not_zero(e);

 rule accumulate_string(n: IDENTIFIER, a: expression, b: left_hand_side)
   : statement -> statement
 = "for (String \s: \a) { \b.add(\s); }"
 => "\b.addAll(\a);";

 rule eliminate_useless_if(x: expression, s: statement)
    : statement -> statement
 = "if (\x) \s; else \s; " -- generalizes OP's example
 => "\s;";

 rule left_factor_ternary(x: expression; t: left_hand_side; a: expression; b:expression)
      : statement -> statement
 =  "if (\x) { \t = \a;} else {\t = \b;} "
 =>  "\t = \x ? \a : \b ";

 rule convert_to_diamond( T1: qualified_path, T2: qualified_path,
                           C: qualified_path, 
                           i: IDENTIFIER, a: arglist)
       :statement -> statement
 =  "\T1<\T2> \i = new \C<\T2>(\a);"
 => "\T1<\T2> \i = new \C<>(\a);"

 rule merge_multi_catch( b:body, i1: IDENTIFIER, e1: qualified_path,
                                  i2: IDENTIFIER,  eh: body  )
       :statement -> statement
  = "try { \b }
     catch ( \i1: \e1 ) { \eh }
     catch ( \i2: \e1 ) { \eh }";
  => "try { \b }
     catch ( \i1, \i2: \e1 ) { \eh }";

Возможны более сложные преобразования, включая те, которые затрагивают части программы, находящиеся далеко друг от друга и даже за границами исходного файла. Обычно это требует некоторого дополнительного метапрограммирования (здесь не обсуждается) и часто дополнительных проверок контекста, например, правильности типа идентификаторов и т. д. [Для поддержки этого в DMS есть полные таблицы символов для Java].

Другие PTS могут выражать правила аналогичным образом. Большинство из них не обеспечивают поддержку более глубоких семантических фактов, таких как таблицы символов или типы, хотя утверждается, что вы можете запрограммировать их самостоятельно. Опыт показывает, что это много работы, и вы действительно хотите, чтобы все было правильно. (Кто хочет, чтобы их преобразование повредило код из-за того, что он работает с неверной информацией?).

person Ira Baxter    schedule 29.01.2015

Я автор AutoRefactor, упомянутого Slanec. Все первые показанные вами рефакторинги уже реализованы, а следующие еще нет:

  • автоматический рефакторинг для алмазного оператора / исключение multi catch / лямбда-выражения
  • еще больше...

См. нерешенные проблемы, включая рефакторинг Java 7. На данный момент я реализовал только рефакторинг, совместимый с Java 6.

person JnRouvignac    schedule 16.02.2015
comment
Теперь реализовано следующее: автоматический рефакторинг для алмазного оператора / исключение multi catch - person JnRouvignac; 01.09.2015