2017-08-28 16 views
3

静的内部クラスを持つビルダーパターンを実装して、クラスA(フィールドa1、a2、a3)、フィールドB(b1、b2)、フィールドC(フィールドc1)一方、スーパークラススーパークラスからすべての共有フィールド(S1、S2):継承を持つJava Builderパターン

public class A extends SuperClass { 
    private final String a1; 
    ... 

    private A(ABuilder builder) { 
     super(builder); 
     this.a1 = builder.a1; 
     ... 
    } 

    public static class ABuilder extends SuperClassBuilder implements ABuilderInterface { 
     private String a1; 
     ... 

     @Override 
     public ABuilder withA1(String a1) { 
      this.a1 = a1; 
      return this; 
     } 
     ... 

     @Override 
     public SuperClass build() { 
      return new A(this); 
     } 
    } 
} 

は、従ってBおよびCのためのビルダーは単にこれらのインタフェースに対し、彼らが自分のフィールドを持っており、自分のインターフェース(BBuilderInterfaceとCBuilderInterface)を実装することが異なりますどのメソッドを実装するかを定義するだけです:

public interface ABuilderInterface extends SuperClassBuilderInterface { 
    ABuilderInterface withA1(String a1); 
    ... 
} 
...<interfaces for B and C> 

public interface SuperClassBuilderInterface { 
    SuperClassBuilderInterface withS1(String s1); 
    ... 
    SuperClass build(); 
} 

// Usage of the builders: 
public SuperClass foo() { 
    return new A.ABuilder() 
     .withA1(...) // returns ABuilderInterface 
     ... 
     .withS1(...) // returns SuperClassBuilderInterface 
     ... 
     .build(); 
} 

public abstract class SuperClass { 
private final String s1; 
... 

protected SuperClass(SuperClassBuilder builder) { 
    this.s1 = builder.s1; 
    ... 
} 

protected static abstract class SuperClassBuilder implements SuperClassBuilderInterface { 
    private String s1; 
    ... 

    @Override 
    public SuperClassBuilder withS1(String s1) { 
     this.s1 = s1; 
     return this; 
    } 
    ... 

    @Override 
    public abstract SuperClass build(); 
} 
} 

これで、ビルダーを使用するときには、まず子クラスに関連するwith ...メソッドを呼び出してから、スーパークラスのものをチェーン化することに注意しなければならないという制限があります、しかし、まだ良い実践かどうかは分かりません。 反対側では、子クラスのwith ...メソッドをすべてスーパークラスインターフェイスに追加してから制限がなくなりましたが、別の子クラスの...メソッドと混在したインターフェイスがあります。

どちらをお好みですか/お勧めしますか?

+0

'ABuilder'で' withS1() 'が返されますか? – alayor

+0

@alayorまたはABuilderでは、親のものを使用しているため、withS1()はありません。アイデアは、A、B、Cビルダーには固有の...メソッドしかなく、共有ビルダーはスーパービルダーに残ります – radio

+2

これは非常に一般的な問題です。 1つのパターンは、スーパークラスに汎用の「自己タイプ」を定義し、そのメソッドによって返されるパターンです。 – shmosel

答えて

5

Fバウンド(奇妙に反復するテンプレートパターンと呼ばれます)を使用するようにスーパークラスのビルダーを変更します。

public interface SuperClassBuilderInterface<SELF extends SuperClassBuilderInterface<SELF>> { 
    SELF withS1(String s1); 
    // etc. 
    Superclass build(); 
} 

次に、あなたが持っている:SuperClassBuilderの実装は、フォームreturn (SELF)this;の未チェックのキャストを含まなければならないことを

class SuperClassBuilder<SELF extends SuperClassBuilder<SELF>> implements SuperClassBuilderInterface<SELF> 

interface ABuilderInterface<SELF extends ABuilderInterface<SELF>> extends SuperClassBuilderInterface<SELF> 

class ABuilder extends SuperClassBuilder<ABuilder> implements ABuilderInterface<ABuilder> 

注意を。型システムは理論的にはこれを必要としないほど強力ですが、結果として生じるエンコーディングはおそらく非常に醜いでしょう(this参照)、それは価値がない可能性があります。

編集:これは@shmoselの意味です

+0

私はそれを試してみたところ、うまくいった。最初に私はそれが非常に多くのジェネリックスを追加し、一見して2度考える必要があるので、それが好きではなかった:)しかし、彼は制限がないので呼び出し元にとっては簡単だ。 – radio

+0

btw。これは正しいのですか?: 'SuperClassBuilder'の実装は、前述のようにSELFを' return(SELF)this; 'として返します。 ' ABuilderInterface'のメソッドはSELFを返し、その実装はABuilder – radio

+0

を返します。私は実際にチェックされていないキャストを使用しないように答えを編集します。 – HTNW