Generowanie kodu wzorca konstruktora w IntelliJ

83

Czy istnieje sposób na zautomatyzowanie pisania wzorców Builder w IntelliJ?

Na przykład, biorąc pod uwagę tę prostą klasę:

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;
   }
}

czy jest sposób, w jaki mógłbym uzyskać IDE, aby wygenerować to lub podobne:

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;
    }
}
Martyn
źródło

Odpowiedzi:

108

Użyj narzędzia Replace Constructor z refaktoryzacją Builder .

Aby skorzystać z tej funkcji, kliknij podpis konstruktora w swoim kodzie, a następnie kliknij prawym przyciskiem myszy i wybierz menu „Refaktoryzacja”, a następnie kliknij opcję „Zastąp konstruktor konstruktorem ...”, aby wyświetlić okno dialogowe generowania kodu.

CrazyCoder
źródło
12
Czy istnieje sposób na zmianę „setX ()” na „withX ()”, Serge?
ae6rt
5
Czy możesz zmienić setX () na po prostu x ()?
Kurru
Miły! Nigdy nie było nowego, co istniało. Dzięki
vikingsteve
16
Możesz zmienić nazwę metod ustawiających, klikając koło zębate w prawym górnym rogu okna kreatora tworzenia i wybierając „Zmień nazwę prefiksu ustawiającego”. Następnie możesz zmienić to na „z” lub całkowicie usunąć prefiks.
Chris B
3
Czy jest możliwe, aby IntelliJ automatycznie tworzył klasę Builder jako klasę wewnętrzną?
zestaw
23

Zauważyłem, że wbudowane generowanie wzorca konstruktora w IntelliJ jest nieco niezgrabne z kilku powodów:

  1. Musi użyć istniejącego konstruktora jako odniesienia.
  2. Nie jest szybko dostępny za pośrednictwem menu „Generuj” ( command+Nw systemie OS X).
  3. Generuje tylko zewnętrzne klasy Builder. Jak wspominali inni, podczas implementowania wzorca konstruktora bardzo często używa się statycznych klas wewnętrznych.

InnerBuilder plugin adresy wszystkich tych wad, i nie wymaga instalacji ani konfiguracji. Oto przykładowy Konstruktor wygenerowany przez wtyczkę:

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);
        }
    }
}
Mansoor Siddiqui
źródło
3
Proszę zapoznać się z moją odpowiedzią na wbudowane rozwiązanie wszystkich wymienionych problemów.
jFrenetic
1
Poruszasz kilka dobrych punktów @jFrenetic, ale ostatecznie uważam, że wbudowane podejście jest zbyt uciążliwe, nawet przy takich obejściach. Z wtyczką, to dosłownie alt+shift+B, enteri BAM, masz Builder. : D
Mansoor Siddiqui
1
OMG to spełnienie marzeń. Wbudowany kreator nie zrobił tego za mnie - wolę mieć budowniczego jako wewnętrzną klasę PoJo. Dzięki za cudowną wskazówkę!
Somaiah Kumbera,
16

Oto, jak przezwyciężyć niedociągnięcia, o których wspomniał Mansoor Siddiqui :

1) Musi użyć istniejącego konstruktora jako odniesienia.

Który jest bardzo łatwy do wygenerowania. Po prostu naciśnij Alt+ Insw systemie Windows, aby wywołać menu Generuj i wybierz Constructor.

2) Nie jest szybko dostępny za pośrednictwem menu „Generuj” (polecenie + N w systemie OS X)

Po prostu przejdź do Settings -> Keymap, wyszukaj Builderi przypisz mu wybrany skrót (jeśli korzystasz z tej funkcji bardzo często, co rzadko się zdarza). Możesz na przykład przypisać Alt+ B.

Inną alternatywą jest Ctrl+ Shift+ A(Find Action). Zacznij pisać, Buildera natychmiast uzyskasz dostęp do polecenia:

Okno dialogowe Znajdź akcję

Możesz użyć tego skrótu, aby szybko uzyskać dostęp do dowolnej funkcji IntelliJ IDEA (jest to bardzo pomocne, gdy nie pamiętasz dokładnie, jak nazywa się polecenie i gdzie je znaleźć).

3) Generuje tylko zewnętrzne klasy Builder. Jak wspominali inni, podczas implementowania wzorca konstruktora bardzo często używa się statycznych klas wewnętrznych .

Wolę też moich konstruktorów jako statyczne klasy wewnętrzne. Niestety nie ma prostego sposobu, aby to zrobić, ale nadal jest to wykonalne. Musisz tylko samodzielnie zdefiniować zagnieżdżoną klasę wewnętrzną (pozostaw ją pustą), a po wywołaniu Replace Constructor with Builderokna dialogowego wybierz Use existingopcję i wybierz swoją klasę wewnętrzną. Działa jak marzenie! Chociaż byłoby łatwiej skonfigurować tę opcję.

jFrenetic
źródło
12

Jeśli zastanawiasz się, czy można to wykorzystać do stworzenia klasy z wewnętrzną klasą budującą, jak opisano przez Joshua Blocka - musisz najpierw zdefiniować pustą klasę wewnętrzną, a następnie zaznaczyć "Użyj istniejącej" i wyszukać nowo utworzoną (wewnętrzną klasę ) i kliknij „Refaktoryzuj”.

PS! Kursor musi znajdować się w konstruktorze (wstępnie napisanym), aby można było użyć funkcji refaktoryzacji „Zamień konstruktor na Builder”.

PålOliver
źródło
Ciekawe, że te wtyczki zapominają o ważnej części wzorca konstruktora: nadal powinieneś zdefiniować wymagane parametry w konstruktorze. W przeciwnym razie przeszedłeś od statycznego przechwytywania błędów brakujących parametrów do przechwytywania tylko w czasie wykonywania.
EJ Campbell
@EJCampbell To zniweczyłoby jeden z celów używania Konstruktora, którym jest uczynienie kodu bardziej czytelnym poprzez zapewnienie możliwości dołączania nazw do parametrów, nawet jeśli są one wymagane. Jest to pomocne, gdy konstruktor przyjmuje dość dużą liczbę parametrów. Nie byłoby konieczne, gdyby Java pozwoliła nam nazwać rzeczywiste parametry w wywołaniu, tak jak robią to C # i Swift.
ajb,
5

IntelliJną drogą do tego jest IMHO, jest skomplikowana. Istnieją dwie wtyczki (wolę tę: https://plugins.jetbrains.com/plugin/7354 ), które znacznie lepiej służą temu celowi.

Na przykład wolę mieć klasę Builder jako wewnętrzną klasę PoJo. Aby to osiągnąć z IntelliJ, potrzebujesz kilku dodatkowych ruchów.

Kolejnym plusem wtyczki jest lokalizacja funkcjonalności (w Generate...menu kontekstowym).

nucatus
źródło
4

Lombok to najłatwiejszy sposób! (Posiada wtyczkę Intellij do obsługi składni https://plugins.jetbrains.com/plugin/6317-lombok-plugin )

Wystarczy dodać @Builderadnotację, a za ścianami doda implementację budowniczego wewnątrz obiektu.

Z Lombok:

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;
}

Bez Lombok:

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 + ")";
    }
  }
}

Źródło: https://projectlombok.org/features/Builder

veysiertekin
źródło
lombok to generowanie kodu z dodatkowymi krokami. z tego powodu wiele narzędzi do analizy statycznej myli się, ponieważ końcowy kod, który czytają, nie wygląda jak java.
ThisCompSciGuy
2

Moim zdaniem najprostszym sposobem na uzyskanie wewnętrznej klasy statycznej zamiast osobnej są:

  1. Jeśli nie masz jeszcze konstruktora, wygeneruj go za pomocą okna dialogowego Generuj konstruktor
  2. Następnie użyj opcji Replace Constructor with Builder
  3. Przenieś nowo utworzoną klasę z powrotem do początkowej za pomocą funkcji Move refactoring (Hotkey: F6)
Gaket
źródło
1
Wydaje się, że ta wtyczka osiąga dokładnie to w jednym kroku: plugins.jetbrains.com/plugin/7354-innerbuilder
jivimberg
0

Jako uzupełnienie odpowiedzi @ CrazyCodera, myślę, że bardzo przydatne jest wiedzieć, że w prawym górnym rogu okna dialogowego Zamień konstruktora na Konstruktor znajduje się przycisk konfiguracyjny, którego można użyć do zmiany nazwy prefiksu ustawiającego.

tibtof
źródło
0

Dla tych, którzy chcą zamienić „zbyt wiele parametrów” na wzorzec konstruktora, wykonaj „wyodrębnij obiekt parametru”, a następnie konwertuj konstruktor do konstruktora wspomnianego w innych odpowiedziach tutaj.

rogerdpack
źródło
0

Wygeneruj konstruktora klasy, klikając prawym przyciskiem myszy pozycję Generuj> Konstruktor. Następnie w systemie Windows naciśnij kombinację klawiszy Ctrl + Shift + A, aby znaleźć akcję i wpisz „budowniczy”, wybierz „Zastąp konstruktora przez konstruktora”.

Vijay Nandwana
źródło
0

Uważam, że Effective Inner Builder jest najlepszą opcją, ponieważ dodaje kontrole null i może zaczynać się od zdefiniowanych elementów członkowskich.

ThisCompSciGuy
źródło