web-dev-qa-db-ja.com

セッターに「this」を返すのは悪い習慣ですか?

Javaのセッターに「this」を返すのは良い考えですか、悪い考えですか?

public Employee setName(String name){
   this.name = name;
   return this;
}

このパターンは、次のようなセッターをチェーン化できるため便利です。

list.add(new Employee().setName("Jack Sparrow").setId(1).setFoo("bacon!"));

これの代わりに:

Employee e = new Employee();
e.setName("Jack Sparrow");
...and so on...
list.add(e);

...しかし、それは一種の標準的な慣習に反します。そのセッターに何か他の有用なことをさせることができるという理由だけで価値があると思います。このパターンはいくつかの場所(JMock、JPAなど)を使用するのを見てきましたが、一般的ではないようで、このパターンがどこでも使用される非常によく定義されたAPIにのみ一般的に使用されます。

更新:

私が説明したことは明らかに有効ですが、私が本当に探しているのは、これが一般に受け入れられるかどうか、そして落とし穴または関連するベストプラクティスがあるかどうかについてのいくつかの考えです。 Builderパターンについては知っていますが、私が説明しているものよりも少し複雑です-Josh Blochが説明しているように、オブジェクト作成用の静的Builderクラスが関連付けられています。

230
Ken Liu

私はそれについて特に悪いことはないと思います、それはただのスタイルの問題です。次の場合に便利です。

  • 一度に多くのフィールドを設定する必要があります(構築時を含む)
  • コードを書いているときに設定する必要があるフィールドを知っている
  • 設定するフィールドにはさまざまな組み合わせがあります。

この方法の代替方法は次のとおりです。

  1. 1つのメガコンストラクター
  2. オーバーロードされたいくつかのコンストラクター(欠点:少数を超えると扱いにくくなります)
  3. ファクトリー/静的メソッド(欠点:オーバーロードされたコンストラクターと同じ-数が多くなると扱いにくくなります)

一度にいくつかのプロパティのみを設定する場合は、「this」を返す価値はないと言います。後でステータス/成功インジケータ/メッセージなどの何かを返すことにした場合、それは確かに落ちます。

73
Tom Clift

それは悪い習慣ではありません。それはますます一般的な慣行です。ほとんどの言語では、返されたオブジェクトを処理する必要はありません。そうすることで、「通常の」セッターの使用構文を変更せずに、セッターを連結できます。

これは通常、ビルダーパターンまたは fluent interface と呼ばれます。

Java AP​​Iでも一般的です:

String s = new StringBuilder().append("testing ").append(1)
  .append(" 2 ").append(3).toString();
99
cletus

要約する:

  • 「流 "なインターフェイス」または「メソッドチェーン」と呼ばれます。
  • これは「標準」のJavaではありませんが、最近ではもっと見やすくなっています(jQueryでうまく機能します)
  • javaBean仕様に違反しているため、さまざまなツールやライブラリ、特にJSPビルダーやSpringで機能しなくなります。
  • jVMが通常行ういくつかの最適化を妨げる可能性があります
  • コードをクリーンアップすると考える人もいれば、「恐ろしい」と思う人もいます

言及されていない他のいくつかのポイント:

  • これは、各関数が1つだけのことを行うという原則に違反します。あなたはこれを信じるか信じないかもしれませんが、Javaで私はそれがうまくいくと信じています。

  • IDEはこれらを自動的に生成しません(デフォルト)。

  • 最後に、実際のデータポイントを示します。このようにビルドされたライブラリを使用すると問題が発生しました。 Hibernateのクエリビルダーは、既存のライブラリでのこの例です。 Queryのset *メソッドはクエリを返すため、シグネチャの使用方法を確認するだけでは判断できません。例えば:

    Query setWhatever(String what);
    
  • メソッドは現在のオブジェクト(パターン)を変更するか、Queryが本当に不変(非常に一般的で価値のあるパターン)であり、メソッドは新しいパターンを返します。ライブラリが使いにくくなるだけで、多くのプログラマーはこの機能を活用しません。セッターがセッターである場合、それを使用する方法がより明確になります。

82
ndp

私はこれに「with」メソッドを使用することを好みます:

public String getFoo() { return foo; }
public void setFoo(String foo) { this.foo = foo; }
public Employee withFoo(String foo) {
  setFoo(foo);
  return this;
}

副<文>この[前述の事実の]結果として、それ故に、従って、だから◆【同】consequently; therefore <文>このような方法で、このようにして、こんなふうに、上に述べたように◆【同】in this manner <文>そのような程度まで<文> AひいてはB◆【用法】A and thus B <文>例えば◆【同】for example; as an example:

list.add(new Employee().withName("Jack Sparrow")
                       .withId(1)
                       .withFoo("bacon!"));

警告:このwithX構文は、一般に不変オブジェクトに「セッター」を提供するために使用されるため、これらのメソッドの呼び出し元は、既存のインスタンスを変更するのではなく、新しいオブジェクトを作成することを合理的に期待する場合があります。

74
qualidafial

セッターから'this'を返したくないが、2番目のオプションを使用したくない場合は、次の構文を使用してプロパティを設定できます。

list.add(new Employee()
{{
    setName("Jack Sparrow");
    setId(1);
    setFoo("bacon!");
}});

余談ですが、C#の方が少しすっきりしています。

list.Add(new Employee() {
    Name = "Jack Sparrow",
    Id = 1,
    Foo = "bacon!"
});
25
Luke Quinane

これは、ゲッター/セッターの規則を破るだけでなく、Java 8メソッド参照フレームワークも破ります。 MyClass::setMyValueBiConsumer<MyClass,MyValue>であり、myInstance::setMyValueConsumer<MyValue>です。セッターがthisを返す場合、それはConsumer<MyValue>の有効なインスタンスではなく、Function<MyValue,MyClass>であり、それらのセッターへのメソッド参照を使用して何かを引き起こします(それらが無効であると仮定します)メソッド)を破る。

10
Steve K

Javaはわかりませんが、C++でこれを実行しました。他の人は、それが行を本当に長くし、本当に読みにくくすると言っていますが、私はこのように何度もやりました:

list.add(new Employee()
    .setName("Jack Sparrow")
    .setId(1)
    .setFoo("bacon!"));

これはさらに優れています:

list.add(
    new Employee("Jack Sparrow")
    .Id(1)
    .foo("bacon!"));

少なくとも、私は思う。しかし、あなたは私に賛成票を投じて、あなたが望むなら私をひどいプログラマーと呼ぶことを歓迎します。また、Javaでこれを行うことを許可されているかどうかもわかりません。

7
Carson Myers

Voidを返さないため、有効なJavaBeanプロパティセッターではなくなりました。あなたが視覚的な「Bean Builder」ツールを使用している世界の7人のうちの1人、またはJSP-bean-setProperty要素を使用している17人のうちの1人である場合、それは重要かもしれません。

6
Ken

「流fluentなインターフェイス」と呼ばれるこのスキーム(しゃれた意図)は、現在非常に人気が高まっています。それは許容できますが、実際には私のお茶ではありません。

6
Noon Silk

少なくとも理論上では、呼び出し間に誤った依存関係を設定することにより、JVMの最適化メカニズムを損傷する可能性があります。

これは構文糖衣であると想定されていますが、実際には超インテリジェントJava 43の仮想マシンで副作用を引き起こす可能性があります。

だから投票しない、使わないで。

5
Marian

それは悪い習慣ではありません。ただし、 JavaBeans Spec との互換性はありません。

そして、それらの標準アクセサに依存する仕様がたくさんあります。

いつでも共存させることができます。

public class Some {
    public String getValue() { // JavaBeans
        return value;
    }
    public void setValue(final String value) { // JavaBeans
        this.value = value;
    }
    public String value() { // simple
        return getValue();
    }
    public Some value(final String value) { // fluent/chaining
        setValue(value);
        return this;
    }
    private String value;
}

これで、それらを一緒に使用できます。

new Some().value("some").getValue();

不変オブジェクトの別のバージョンが登場します。

public class Some {

    public static class Builder {

        public Some build() { return new Some(value); }

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

        private String value;
    }

    private Some(final String value) {
        super();
        this.value = value;
    }

    public String getValue() { return value; }

    public String value() { return getValue();}

    private final String value;
}

これでできます。

new Some.Builder().value("value").build().getValue();
5
Jin Kwon

セッターが「これ」を返すことに賛成です。 Beanに準拠していないかどうかは気にしません。私にとって、「=」式/文を使用しても問題ない場合、値を返すセッターは問題ありません。

3
Monty Hall

Paulo Abrantes は、JavaBeanセッターを流makeにする別の方法を提供します。各JavaBeanの内部ビルダークラスを定義します。値を返すセッターによって混乱するツールを使用している場合、Pauloのパターンが役立ちます。

3
Jim Ferrans

アプリケーション全体で同じ規則を使用すると、問題ないように見えます。

一方、アプリケーションの既存の部分が標準的な規則を使用している場合は、それに固執し、より複雑なクラスにビルダーを追加します

public class NutritionalFacts {
    private final int sodium;
    private final int fat;
    private final int carbo;

    public int getSodium(){
        return sodium;
    }

    public int getfat(){
        return fat;
    }

    public int getCarbo(){
        return carbo;
    }

    public static class Builder {
        private int sodium;
        private int fat;
        private int carbo;

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

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

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

        public NutritionalFacts build() {
            return new NutritionalFacts(this);
        }
    }

    private NutritionalFacts(Builder b) {
        this.sodium = b.sodium;
        this.fat = b.fat;
        this.carbo = b.carbo;
    }
}
2
Marcin Szymczak

この特定のパターンは、メソッド連鎖と呼ばれます。 ウィキペディアのリンク 、これには、さまざまなプログラミング言語でどのように行われるかについての詳細な説明と例があります。

追伸:特定の名前を探していたので、ここに残すことを考えました。

2
capt.swag

以前はこのアプローチを好んでいましたが、私はそれに反対することを決めました。

理由:

  • 読みやすさ。これにより、コードが読みやすくなり、各setFoo()が個別の行になります。通常、コードを書くのは、コードを1回書くよりも何度も何度も読むことになります。
  • 副作用:setFoo()はフィールドfooのみを設定し、それ以外は設定しないでください。これを返すことは、余分な「それは何でしたか」です。

私が見たBuilderパターンは、setFoo(foo).setBar(bar)規則を使用せず、さらにfoo(foo).bar(bar)を使用します。おそらくそれらの理由のためでしょう。

それは、常に好みの問題です。 「最小限の驚き」アプローチが好きです。

一見すると:「Ghastly!」。

さらに考えて

list.add(new Employee().setName("Jack Sparrow").setId(1).setFoo("bacon!"));

実際にはエラーが発生しにくい

Employee anEmployee = new Employee();
anEmployee.setName("xxx");
...
list.add(anEmployee);

とても興味深い。ツールバッグにアイデアを追加...

1
djna

一般的には良い習慣ですが、セット型の関数では、ブール型を使用して、操作が正常に完了したかどうかを判断する必要がある場合があります。一般的に、これが良いことだとか、ベッドだとかという教義はありません。それはもちろん状況に由来します。

1
Narek

はい、良いアイデアだと思います。

私が何かを追加できるなら、この問題はどうですか:

class People
{
    private String name;
    public People setName(String name)
    {
        this.name = name;
        return this;
    }
}

class Friend extends People
{
    private String nickName;
    public Friend setNickName(String nickName)
    {
        this.nickName = nickName;
        return this;
    }
}

これは動作します:

new Friend().setNickName("Bart").setName("Barthelemy");

これはEclipseでは受け入れられません! :

new Friend().setName("Barthelemy").setNickName("Bart");

これは、setName()がFriendではなくPeopleを返し、PeoplesetNickNameがないためです。

クラスの名前の代わりにSELFクラスを返すセッターを作成するにはどうすればよいですか?

このようなものは問題ありません(SELFキーワードが存在する場合)。とにかくこれは存在しますか?

class People
{
    private String name;
    public SELF setName(String name)
    {
        this.name = name;
        return this;
    }
}
1
Baptiste

これがJavaBeansの仕様に違反すると主張するすべてのポスターに同意します。それを保持する理由はありますが、このBuilderパターン(暗示された)の使用にはその場所があると感じています。それがどこでも使われない限り、それは受け入れられるべきです。私にとって、「It's Place」は、エンドポイントが「build()」メソッドの呼び出しであるところです。

もちろん、これらすべてを設定する方法は他にもありますが、ここでの利点は、1)多くのパラメーターを持つパブリックコンストラクター、2)部分的に指定されたオブジェクトを回避できることです。ここでは、ビルダーに必要なものを収集させ、最後に「build()」を呼び出します。これにより、部分的に指定されたオブジェクトが構築されないことを確認できます。代替手段は「パラメータオブジェクト」ですが、そのIMHOは問題を1レベルだけプッシュします。

多くのパラメーターのコンストラクターは、同じタイプの引数が多数渡される可能性が高くなるため、パラメーターに間違った引数を渡しやすくなるため、私は嫌いです。オブジェクトを完全に構成する前に使用できるため、多くのセッターを使用するのは嫌です。さらに、以前の選択に基づいたデフォルト値を持つという概念は、「build()」メソッドを使用する方が適切です。

要するに、適切に使用すれば、それは良い習慣だと思います。

0
Javaneer

これは読みにくいかもしれません

list.add(new Employee().setName("Jack Sparrow").setId(1).setFoo("bacon!")); 

またはこれ

list.add(new Employee()
          .setName("Jack Sparrow")
          .setId(1)
          .setFoo("bacon!")); 

これは以下よりも読みやすいです:

Employee employee = new Employee();
employee.setName("Jack Sparrow")
employee.setId(1)
employee.setFoo("bacon!")); 
list.add(employee); 
0
Scott

APIを作成している場合、「return this」を使用して、一度だけ設定される値を設定します。ユーザーが変更できる他の値がある場合は、代わりに標準のvoidセッターを使用します。

しかし、それは本当に好みの問題であり、セッターの連鎖は私の意見では非常にクールに見えます。

0
Kaiser Keister

声明から

list.add(new Employee().setName("Jack Sparrow").setId(1).setFoo("bacon!"));

私は二つのことを見ています

1)無意味な声明。 2)読みやすさの欠如。

0
Madhu

私はかなり長い間セッターを作っていますが、唯一の本当の問題は、厳密なgetPropertyDescriptorsに固執してBeanリーダー/ライターBeanアクセサーを取得するライブラリーです。これらの場合、Java "bean"には期待したとおりの書き込み機能がありません。

たとえば、確かにテストしていませんが、json/mapsからJavaオブジェクトを作成するときに、Jacksonがそれらをセッターとして認識しないことは驚くことではありません。私はこれについて間違っていることを願っています(すぐにテストします)。

実際、私は軽量のSQL中心のORMを開発しており、getPropertyDescriptorsの間にあるコードを、これを返す認識されたセッターに追加する必要があります。

0
Jeremy Chone

ずっと前に答え​​ましたが、私の2セントは...結構です。この流interfaceなインターフェイスがもっと頻繁に使用されることを望みます。

「factory」変数を繰り返しても、以下の情報は追加されません。

ProxyFactory factory = new ProxyFactory();
factory.setSuperclass(Foo.class);
factory.setFilter(new MethodFilter() { ...

これはすっきりしています。

ProxyFactory factory = new ProxyFactory()
.setSuperclass(Properties.class);
.setFilter(new MethodFilter() { ...

もちろん、既に述べた答えの1つとして、継承やツールなどのいくつかの状況でこれを正しく行うには、Java AP​​Iを微調整する必要があります。

0
Josef.B

可能であれば、他の言語構成を使用することをお勧めします。たとえば、Kotlinでは、withapply、またはletを使用します。このアプローチを使用する場合、セッターからインスタンスを返すために実際にはneedにはなりません。

このアプローチにより、クライアントコードは次のようになります。

  • 戻り型に無関心
  • メンテナンスが簡単
  • コンパイラの副作用を避ける

下記は用例です。

val employee = Employee().apply {
   name = "Jack Sparrow"
   id = 1
   foo = "bacon"
}


val employee = Employee()
with(employee) {
   name = "Jack Sparrow"
   id = 1
   foo = "bacon"
}


val employee = Employee()
employee.let {
   it.name = "Jack Sparrow"
   it.id = 1
   it.foo = "bacon"
}
0
Steven Spungin