Генерация кода шаблона строителя в 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 ...», чтобы открыть диалоговое окно для генерации кода.


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вариант и выберите свой внутренний класс. Работает как шарм! Хотя было бы проще сделать эту опцию настраиваемой.


12

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

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


Интересно, что эти плагины забывают важную часть паттерна построителя: вы все равно должны определять необходимые параметры в конструкторе. В противном случае вы перешли от статического обнаружения ошибок отсутствия параметров к обнаружению только во время выполнения.
EJ Campbell

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

5

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

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

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


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


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

2

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

  1. Если у вас еще нет конструктора, создайте его с помощью диалогового окна «Создание конструктора».
  2. Затем используйте Replace Constructor with Builder
  3. Переместите вновь созданный класс обратно к исходному с помощью рефакторинга Move (горячая клавиша: F6)

1
Этот плагин, кажется, достигает именно этого всего за один шаг: plugins.jetbrains.com/plugin/7354-innerbuilder
jivimberg

0

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


0

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


0

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


Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.