IntelliJでのビルダーパターンコードの生成


83

IntelliJでBuilderパターンの記述を自動化する方法はありますか?

たとえば、次の単純なクラスがあるとします。

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

コンストラクターをビルダーリファクタリングに置き換えるを使用します

この関数を使用するには、コード内のコンストラクターの署名をクリックし、右クリックして[リファクタリング]メニューを選択し、[コンストラクターをビルダーに置き換える...]をクリックしてダイアログボックスを表示し、コードを生成します。


12
「setX()」を「withX()」に変更する方法はありますか、Serge?
ae6rt 2013

5
または、setX()をx()だけに変更できますか?
Kurru 2014年

いいね!存在した新しいことはありません。ありがとう
vikingsteve 2014

16
セッターメソッドの名前を変更するには、ビルダーの作成ウィンドウの右上隅にある歯車をクリックし、[セッタープレフィックスの名前を変更]を選択します。次に、「with」に変更するか、プレフィックスを完全に削除します。
クリスB

3
IntelliJにBuilderクラスを内部クラスとして自動的に作成させることは可能ですか?
キット

23

IntelliJに組み込まれているビルダーパターンの生成は、いくつかの理由で少し扱いに​​くいことがわかりました。

  1. 既存のコンストラクターを参照として使用する必要があります。
  2. command+NOS Xの)「生成」メニューからすぐにアクセスすることはできません。
  3. 外部のBuilderクラスのみを生成します。他の人が述べているように、ビルダーパターンを実装するときに静的内部クラスを使用することは非常に一般的です。

InnerBuilderプラグインのすべてのこれらの欠点のアドレスを、そして何のセットアップや設定は必要ありません。プラグインによって生成されたサンプルビルダーは次のとおりです。

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 2016

1
@jFreneticにはいくつかの良い点がありますが、それらの回避策があっても、最終的には組み込みのアプローチが面倒すぎると感じています。プラグインを使用すると、それは文字通りだalt+shift+BenterとBAM、あなたのビルダーを持っています。:D
Mansoor Siddiqui 2016年

1
OMGこれは夢の実現です。組み込みのビルダーは私のためにそれをしませんでした-私はビルダーをPoJoの内部クラスとして持つことを好みます。素敵なヒントをありがとう!
SomaiahKumbera18年1

16

MansoorSiddiquiが言及した欠点を克服する方法は次のとおりです

1)既存のコンストラクターを参照として使用する必要があります。

これは非常に簡単に生成できます。ただ、ヒットAlt+をInsメニューを生成し、選択起動するには、Windows上でConstructor

2)[生成]メニューからすばやくアクセスすることはできません(OSXではcommand + N)

に移動しSettings -> KeymapBuilder選択したショートカットを検索して割り当てます(この機能を頻繁に使用する場合は、めったにありません)。たとえば、Alt+Bを割り当てることができます。

別の方法としては、Ctrl+ Shift+ A(アクションを見つけます。)。入力Builderを開始すると、すぐにコマンドにアクセスできるようになります。

アクションの検索ダイアログ

このショートカットを使用すると、IntelliJ IDEA機能にすばやくアクセスできます(これは、コマンドの名前と場所を正確に覚えていない場合に非常に役立ちます)。

3)外部のBuilderクラスのみを生成します。他の人が述べているように、ビルダーパターンを実装するときに静的内部クラスを使用することは非常に一般的です。

また、静的内部クラスとしてビルダーを好みます。残念ながら、それを行う簡単な方法はありませんが、それでも実行可能です。ネストされた内部クラスを自分で定義する(空のままにする)必要があります。Replace Constructor with Builderダイアログを呼び出すときに、Use existingオプションを選択して内部クラスを選択します。チャームのように機能します!ただし、このオプションを構成可能にする方が簡単でした。


12

Joshua Blockによって説明されているように、これを使用して内部ビルダークラスを持つクラスを作成できるかどうか疑問に思っている場合は、最初に空の内部クラスを定義してから、[既存のものを使用]をオンにして、新しく作成した(内部クラス)を検索する必要があります。 )そして「リファクタリング」を押します。

PS!「コンストラクターをビルダーに置き換える」リファクタリング機能を使用するには、カーソルがコンストラクター内に存在する必要があります(事前に作成されています)。


これらのプラグインがビルダーパターンの重要な部分を忘れているのは興味深いことです。コンストラクターで必要なパラメーターを定義する必要があります。それ以外の場合は、欠落しているparamエラーを静的にキャッチすることから、実行時にのみキャッチすることへと移行しました。
EJキャンベル

@EJCampbellこれは、ビルダーを使用する目的の1つを無効にします。これは、パラメーターに名前を付ける方法を提供することで、必要な場合でもコードを読みやすくすることです。これは、コンストラクターがかなり多数のパラメーターを受け取る場合に役立ちます。C#やSwiftのように、Javaで呼び出しの実際のパラメーターに名前を付けることができれば、それは必要ありません。
ajb 2016

5

これに対するIntelliJの方法は、私見ですが、複雑です。目的をはるかによく果たす2つのプラグインがあります(私はこれを好みますhttps//plugins.jetbrains.com/plugin/7354)。

たとえば、PoJoの内部クラスとしてBuilderクラスを使用することを好みます。IntelliJでそれを達成するには、いくつかの追加のストロークが必要です。

プラグインのもう1つの利点は、機能の場所(Generate...コンテキストメニュー内)です。


4

ロンボクはそれを行う最も簡単な方法です!(構文サポート用のIntellijプラグイン https://plugins.jetbrains.com/plugin/6317-lombok-pluginがあります

追加するだけです @Builder注釈を、壁の後ろにオブジェクト内にビルダー実装が追加されます。

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

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

ソース:https//projectlombok.org/features/Builder


lombokは、追加の手順を伴うコード生成です。これが原因で、多くの静的分析ツールが混乱します。これは、読み取る最終的なコードがJavaのように見えないためです。
ThisCompSciGuy

2

私の意見では、個別の代わりに内部静的クラスを取得する最も簡単な方法は次のとおりです。

  1. コンストラクターをまだ持っていない場合は、[コンストラクターの生成]ダイアログを使用してコンストラクター生成します
  2. 次に、コンストラクタをビルダーに置き換えるを使用します
  3. 移動リファクタリング(ホットキー:F6)を使用して、新しく作成したクラスを最初のクラスに戻します。

1
このプラグインは、たった1つのステップでこれを正確に達成しているようです:plugins.jetbrains.com/plugin/7354-innerbuilder
jivimberg


0

「パラメーターが多すぎます」をビルダーパターンに置き換えたい場合は、「パラメーターオブジェクトの抽出」を実行してから、コンストラクターをビルダーに変換します。


0

クラスGenerate> Constructorを右クリックして、クラスのコンストラクターを生成します。次に、Windowsでは、Ctrl + Shift + Aを押してアクションを検索し、「builder」と入力して、[コンストラクターをビルダーに置き換える]を選択します。


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.