2012-03-05 12 views
12

のは、私は不変のデータを持ってJavaでFooクラスがあるとしましょう:のJava:不変クラスのための擬似setterメソッド

class Foo { 
    final private int x; 
    public int getX() { return this.x; } 
    final private OtherStuff otherstuff; 
    public Foo(int x, OtherStuff otherstuff) { 
     this.x = x; 
     this.otherstuff = otherstuff; 
    } 
    // lots of other stuff... 
} 

今私は「兄弟」の値を作成するユーティリティメソッドを追加したいです同じ状態でxの新しい値を持ちます。

class Foo 
{ 
    ... 
    Foo setX(int newX) { return new Foo(newX, this.otherstuff); } 
    ... 
} 
が、 setX()のセマンティクスが変更可能なBeanオブジェクトの標準的なセッターの規則とは異なりますので、何とかこれは正しい感じていない:私は setX()それを呼び出すことができます。

このメソッドにはどのような名前が最適ですか?

withX()またはnewX()などとしますか?


編集:私の場合は、追加の優先順位:私は(JSR-223を通って、私はエクスポートするオブジェクトモデル)スクリプトクライアントを持って簡単にFooオブジェクトを取得することができます。しかし、コンストラクタの呼び出しやビルダーの作成などは面倒です。ですから、このメソッドをクライアントのスクリプト作成の便宜のために提供することが望ましいです。

+0

関連する質問:http://stackoverflow.com/questions/521893/whats-the-best-name-for-a-non-mutating-add-method-on-だから、次のようになります不変のコレクション。 (これはこれと同じですが、 'setX'の代わりに' add'のためです。) – ruakh

答えて

12

withX()がOKに聞こえるコピーコンストラクタの変種であるかもしれない

public Foo newFooWith(int x) { 
    return new Foo(x, other); 
} 

この場合は、より適切なオプションだと思います。

あなたはjava.lang.String(も不変)を見ればこれはベースの新しい文字列を返すメソッドのすべての種類があります...

「部分クローン」または「セッター」よりも「ビルダー」の詳細です古いもの(ストリング、toLowerCaseメソッド()、など)...

更新に:もaioobeから答えを参照してください、私は好き [deriveFoo()] - それは特にBuilderパターンに慣れていない人には、おそらく明確です。

1

ではなく、という設定者です。実際にはという新しいオブジェクトを作成して返します。私はそれはいくつかのビルダーのパターンのために使用される規則ですので、工場出荷時のセマンティクスは代替が

class Foo { 
    public Foo(Foo foo, int x) { 
     return new Foo(x, foo.getOtherStuff()); 
    } 
} 
+0

この場合、 "otherstuff"はどのようにコピーされますか? – DNA

+0

私は同意しましたが、私の問題はスクリプトクライアントから発生します。クライアントは 'Foo'オブジェクトを取得するのは簡単ですが、' FooFactory'オブジェクトを取得するのは面倒です。 –

+0

これは他のインスタンスデータをコピーしません –

12

標準APIのFont classのスタイルに従って、deriveFoo(int x)とします。

別のオプションは、新しいオブジェクトのプロトタイプとしてFooオブジェクトを受け入れることができるBuilderクラスを提供することです。そのような場合、通常setBar()という名前はbar()となります。これにより、

Foo newFoo = new Foo.Builder(foo).x(123).build(); 
+2

私はこのアプローチが好きです。+1 –

+0

最初のアプローチでは、 'x'と' y''int'フィールドの両方があったら 'Foo'をどのように派生させますか? –

+3

'java.awt.Font'はJDK 1.0クラスであり、そのリリースの多くの命名決定は良くありませんでした。 JDK 8では、 'java.time.YearMonth.withMonth()'など、新しい 'java.time' APIで' withFoo'という規約を使用しています(http://download.java.net/jdk8/docs/api/java /time/YearMonth.html#withMonth-int-)。 – leventov

2

のようになります。これはwithX(value)となります。それはx = valueと何かになると言います。クラスはフィールドの多くを持っていた場合

、私が恐れて次のようになります。だから私は多分ビルダー使用する

obj.withX(1).withY(2).withZ(3).withU(1)... 

データのみと作成するためのメソッドを持つ指定されたクラスの変更可能なバリアントをパターンを、ご紹介現在の状態の元のクラス。そして、私はこれらのメソッドを、y()z()と呼んで、thisを返します。

Immutable im2 = new Mutable(im1).x(1).y(2).z(3).build(); 
+0

基礎となるデータ項目が大きくて高価な場合でも、 'with'スタイルのメソッドを効率的にするのに役立つアプローチは、基礎となる抽象オブジェクト型に余分なレベルの間接参照を追加し、その抽象型仮想の "平坦化"方法が含まれています。 'WithXX'メソッドは、オブジェクトの「連鎖」が過度に増えていないことを検出しない限り、元のオブジェクトへの参照と新しいプロパティの値を一般に保持する' WrapWithXX'オブジェクトを指す新しいラッパーを返します長い場合は... – supercat

+0

... 'Flatten()'を呼び出します。 'Flatten'メソッドは、それが解析できる' WithXX'メソッドを考慮に入れて、新しい基本オブジェクトインスタンスを生成します。オブジェクトに対して「Flatten」を呼び出すと、observable *状態が変更されることはありませんが、その実現をあまり「複雑な」形式に置き換えます。間接参照の余分なレベルはコストなしではありませんが、利点がないわけではありません。例えば、2つのラッパーが同じオブジェクトを持つことが判明した場合、どちらか一方または両方のラッパーを変更して、「標準的な」インスタンスを指すようにして、比較を迅速化することができます。 – supercat

関連する問題