Генерация кода шаблона строителя в IntelliJ

83

Есть ли способ автоматизировать написание шаблонов Builder в IntelliJ?

Например, учитывая этот простой класс:

class Film {
   private String title;
   private int length;

   public void setTitle(String title) {
       this.title = title;
   }

   public String getTitle() {
       return this.title;
   }

   public void setLength(int length) {
       this.length = length;
   }

   public int getLength() {
       return this.length;
   }
}

есть ли способ заставить IDE сгенерировать это или подобное:

public class FilmBuilder {

    Film film;

    public FilmBuilder() {
        film = new Film();
    }

    public FilmBuilder withTitle(String title) {
        film.setTitle(title);
        return this;
    }

    public FilmBuilder withLength(int length) {
        film.setLength(length);
        return this;
    }

    public Film build() {
        return film;
    }
}
Мартын
источник

Ответы:

108

Используйте Replace Constructor with Builder refactoring.

Чтобы использовать эту функцию, щелкните подпись конструктора в коде, затем щелкните правой кнопкой мыши и выберите меню «Рефакторинг», затем щелкните «Заменить конструктор на Builder ...», чтобы открыть диалоговое окно для генерации кода.

CrazyCoder
источник
12
Есть ли способ изменить "setX ()" на "withX ()", Серж?
ae6rt
5
Или вы можете изменить setX () только на x ()?
Kurru
Ницца! Никогда ничего нового в этом нет. Спасибо
vikingsteve
16
Вы можете изменить имя методов установки, нажав на шестеренку в верхнем правом углу окна создания конструктора и выбрав «Переименовать префикс установщика». Затем вы можете изменить его на «с» или просто полностью удалить префикс.
Chris B
3
Можно ли заставить IntelliJ автоматически создавать класс Builder как внутренний класс?
комплект
23

Я обнаружил, что генерация встроенного шаблона компоновщика в IntelliJ немного неуклюжа по нескольким причинам:

  1. В качестве ссылки необходимо использовать существующий конструктор.
  2. Это не быстро доступно через меню «Создать» ( command+Nв OS X).
  3. Он только генерирует внешние классы Builder. Как уже упоминалось, при реализации шаблона построителя очень часто используются статические внутренние классы.

InnerBuilder плагин адреса всех этих недостатков, и не требует установки или настройки. Вот пример Builder, созданный плагином:

public class Person {
    private String firstName;
    private String lastName;
    private int age;

    private Person(Builder builder) {
        firstName = builder.firstName;
        lastName = builder.lastName;
        age = builder.age;
    }

    public static final class Builder {
        private String firstName;
        private String lastName;
        private int age;

        public Builder() {
        }

        public Builder firstName(String firstName) {
            this.firstName = firstName;
            return this;
        }

        public Builder lastName(String lastName) {
            this.lastName = lastName;
            return this;
        }

        public Builder age(int age) {
            this.age = age;
            return this;
        }

        public Person build() {
            return new Person(this);
        }
    }
}
Мансур Сиддики
источник
3
Пожалуйста, посмотрите мой ответ о встроенном решении всех упомянутых вами проблем.
jFrenetic
1
Вы делаете несколько хороших замечаний в @jFrenetic, но я все же считаю встроенный подход слишком громоздким, даже с этими обходными путями. С помощью плагина, это буквально alt+shift+B, enterи БАМ, у вас есть свой строитель. : D
Mansoor Siddiqui
1
OMG, это мечта. Встроенный конструктор не сделал этого для меня - я предпочитаю иметь конструктор как внутренний класс PoJo. Спасибо прекрасный отзыв!
Сомаиа Кумбера 03
16

Вот как преодолеть недостатки, упомянутые Мансуром Сиддики :

1) В качестве ссылки необходимо использовать существующий конструктор.

Что очень легко сгенерировать. Просто нажмите Alt+ Insв Windows, чтобы вызвать меню «Создать», и выберите Constructor.

2) Это не быстро доступно через меню «Создать» (команда + N в OS X)

Просто перейдите по адресу Settings -> Keymap, найдите Builderи назначьте ему ярлык по вашему выбору (если вы используете эту функцию очень часто, что бывает редко). Например, можно присвоить Alt+ B.

Другой альтернативой является Ctrl+ Shift+ A(Найти действие). Начните печатать, Builderи вы сразу получите доступ к команде:

Диалог поиска действия

Вы можете использовать этот ярлык для быстрого доступа к любой функции IntelliJ IDEA (это очень помогает, когда вы точно не помните, что вызывается команда и где ее найти).

3) Он генерирует только внешние классы Builder. Как уже упоминалось, при реализации шаблона построителя очень часто используются статические внутренние классы .

Я также предпочитаю свои конструкторы как статические внутренние классы. К сожалению, простого способа сделать это нет, но это все же возможно. Вам просто нужно самостоятельно определить вложенный внутренний класс (оставьте его пустым), а когда вы вызываете Replace Constructor with Builderдиалог, выберите Use existingвариант и выберите свой внутренний класс. Работает как шарм! Хотя было бы проще сделать эту опцию настраиваемой.

jFrenetic
источник
12

Если вам интересно, можно ли это использовать для создания класса с внутренним классом построителя, как описано Джошуа Блоком - вам просто нужно сначала определить пустой внутренний класс, затем установить флажок «Использовать существующий» и выполнить поиск вашего вновь созданного (внутреннего класса ) и нажмите "Рефакторинг".

PS! Курсор должен находиться внутри конструктора (предварительно написанного) для использования функции рефакторинга «Заменить конструктор на построитель».

ПолОливер
источник
Интересно, что эти плагины забывают важную часть паттерна построителя: вы все равно должны определять необходимые параметры в конструкторе. В противном случае вы перешли от статического обнаружения ошибок отсутствия параметров к обнаружению только во время выполнения.
EJ Campbell
@EJCampbell Это нарушило бы одну из целей использования Builder, которая состоит в том, чтобы сделать код более читабельным, предоставив способ прикрепления имен к параметрам, даже если они необходимы. Это помогает, когда конструктор принимает довольно большое количество параметров. В этом не было бы необходимости, если бы Java позволяла нам указывать фактические параметры в вызове, как это делают C # и Swift.
ajb 07
5

Способ IntelliJ к этому, ИМХО, запутан. Есть два плагина (я предпочитаю этот: https://plugins.jetbrains.com/plugin/7354 ), которые намного лучше служат этой цели.

Например, я предпочитаю иметь класс Builder как внутренний класс PoJo. Чтобы добиться этого с помощью IntelliJ, вам понадобится несколько дополнительных штрихов.

Еще один плюс плагина - расположение функционала (в Generate...контекстном меню).

Nucatus
источник
4

Ломбок - самый простой способ это сделать! (Он имеет плагин Intellij для поддержки синтаксиса https://plugins.jetbrains.com/plugin/6317-lombok-plugin )

Просто добавьте @Builderаннотацию, и за стенами он добавит реализацию конструктора внутри объекта.

С Ломбоком:

import lombok.Builder;
import lombok.Singular;
import java.util.Set;

@Builder
public class BuilderExample {
  @Builder.Default private long created = System.currentTimeMillis();
  private String name;
  private int age;
  @Singular private Set<String> occupations;
}

Без Ломбока:

import java.util.Set;

public class BuilderExample {
  private long created;
  private String name;
  private int age;
  private Set<String> occupations;
  
  BuilderExample(String name, int age, Set<String> occupations) {
    this.name = name;
    this.age = age;
    this.occupations = occupations;
  }
  
  private static long $default$created() {
    return System.currentTimeMillis();
  }
  
  public static BuilderExampleBuilder builder() {
    return new BuilderExampleBuilder();
  }
  
  public static class BuilderExampleBuilder {
    private long created;
    private boolean created$set;
    private String name;
    private int age;
    private java.util.ArrayList<String> occupations;
    
    BuilderExampleBuilder() {
    }
    
    public BuilderExampleBuilder created(long created) {
      this.created = created;
      this.created$set = true;
      return this;
    }
    
    public BuilderExampleBuilder name(String name) {
      this.name = name;
      return this;
    }
    
    public BuilderExampleBuilder age(int age) {
      this.age = age;
      return this;
    }
    
    public BuilderExampleBuilder occupation(String occupation) {
      if (this.occupations == null) {
        this.occupations = new java.util.ArrayList<String>();
      }
      
      this.occupations.add(occupation);
      return this;
    }
    
    public BuilderExampleBuilder occupations(Collection<? extends String> occupations) {
      if (this.occupations == null) {
        this.occupations = new java.util.ArrayList<String>();
      }

      this.occupations.addAll(occupations);
      return this;
    }
    
    public BuilderExampleBuilder clearOccupations() {
      if (this.occupations != null) {
        this.occupations.clear();
      }
      
      return this;
    }

    public BuilderExample build() {
      // complicated switch statement to produce a compact properly sized immutable set omitted.
      Set<String> occupations = ...;
      return new BuilderExample(created$set ? created : BuilderExample.$default$created(), name, age, occupations);
    }
    
    @java.lang.Override
    public String toString() {
      return "BuilderExample.BuilderExampleBuilder(created = " + this.created + ", name = " + this.name + ", age = " + this.age + ", occupations = " + this.occupations + ")";
    }
  }
}

Источник: https://projectlombok.org/features/Builder

Veysiertekin
источник
lombok - это генерация кода с дополнительными шагами. из-за этого многие инструменты статического анализа сбивают с толку, потому что окончательный код, который они читают, не похож на java.
ThisCompSciGuy
2

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

  1. Если у вас еще нет конструктора, создайте его с помощью диалогового окна «Создание конструктора».
  2. Затем используйте Replace Constructor with Builder
  3. Переместите вновь созданный класс обратно к исходному с помощью рефакторинга Move (горячая клавиша: F6)
Gaket
источник
1
Этот плагин, кажется, достигает именно этого всего за один шаг: plugins.jetbrains.com/plugin/7354-innerbuilder
jivimberg
0

В дополнение к ответу @ CrazyCoder, я думаю, очень полезно знать, что в верхней правой части диалогового окна Replace Constructor with Builder есть кнопка конфигурации, которую вы можете использовать для переименования префикса сеттеров.

тибтоф
источник
0

Для тех, кто хочет заменить «слишком много параметров» шаблоном построителя, выполните «извлечь объект параметра», а затем преобразовать конструктор в построитель, упомянутый в других ответах здесь.

Роджердпак
источник
0

Сгенерируйте конструктор класса, щелкнув правой кнопкой мыши на class Generate> Constructor. Затем в Windows нажмите Ctrl + Shift + A, чтобы найти действие, и введите «строитель», выберите «Заменить конструктор на построитель ..»

Виджай Нандвана
источник