Java Builder sınıfını alt sınıflandırma


133

Ver ben Doktor Dobbs makaleye biz Builder sınıflara eden olayda nasıl ve özellikle Oluşturucu Desen? GDO etiketleme eklemek için alt sınıfa eklemek istediğimiz örneğin kısaltılmış bir versiyonunu ele alırsak, saf bir uygulama şöyle olacaktır:

public class NutritionFacts {                                                                                                    

    private final int calories;                                                                                                  

    public static class Builder {                                                                                                
        private int calories = 0;                                                                                                

        public Builder() {}                                                                                                      

        public Builder calories(int val) { calories = val; return this; }                                                                                                                        

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

    protected NutritionFacts(Builder builder) {                                                                                  
        calories = builder.calories;                                                                                             
    }                                                                                                                            
}

Alt sınıf:

public class GMOFacts extends NutritionFacts {                                                                                   

    private final boolean hasGMO;                                                                                                

    public static class Builder extends NutritionFacts.Builder {                                                                 

        private boolean hasGMO = false;                                                                                          

        public Builder() {}                                                                                                      

        public Builder GMO(boolean val) { hasGMO = val; return this; }                                                           

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

    protected GMOFacts(Builder builder) {                                                                                        
        super(builder);                                                                                                          
        hasGMO = builder.hasGMO;                                                                                                 
    }                                                                                                                            
}

Şimdi, şu şekilde kod yazabiliriz:

GMOFacts.Builder b = new GMOFacts.Builder();
b.GMO(true).calories(100);

Ancak siparişi yanlış yaparsak, hepsi başarısız olur:

GMOFacts.Builder b = new GMOFacts.Builder();
b.calories(100).GMO(true);

Sorun elbette NutritionFacts.Buildera NutritionFacts.Builderdeğil a döndürmesidir , GMOFacts.Builderpeki bu problemi nasıl çözeceğiz yoksa kullanılacak daha iyi bir Kalıp var mı?

Not: Benzer bir soruya verdiğim bu cevap , yukarıdaki derslerimi sunuyor; Sorum, inşaatçı çağrılarının doğru sırada olmasını sağlama sorunuyla ilgilidir.


1
Aşağıdaki bağlantının iyi bir yaklaşımı açıkladığını düşünüyorum: egalluzzo.blogspot.co.at/2010/06/…
stuXnet

1
Ama build()çıktısını nasıl elde edersiniz b.GMO(true).calories(100)?
Sridhar Sarnobat

Yanıtlar:


170

Jenerik kullanarak çözebilirsiniz. Sanırım buna "Merakla yinelenen genel kalıplar" deniyor

Temel sınıf oluşturucu yöntemlerinin dönüş türünü genel bir bağımsız değişken yapın.

public class NutritionFacts {

    private final int calories;

    public static class Builder<T extends Builder<T>> {

        private int calories = 0;

        public Builder() {}

        public T calories(int val) {
            calories = val;
            return (T) this;
        }

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

    protected NutritionFacts(Builder<?> builder) {
        calories = builder.calories;
    }
}

Şimdi temel oluşturucuyu, genel bağımsız değişken olarak türetilmiş sınıf oluşturucuyla somutlaştırın.

public class GMOFacts extends NutritionFacts {

    private final boolean hasGMO;

    public static class Builder extends NutritionFacts.Builder<Builder> {

        private boolean hasGMO = false;

        public Builder() {}

        public Builder GMO(boolean val) {
            hasGMO = val;
            return this;
        }

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

    protected GMOFacts(Builder builder) {
        super(builder);
        hasGMO = builder.hasGMO;
    }
}

2
Hmm, sanırım ya (a) yeni bir soru göndermem, (b) implementsyerine ile yeniden tasarlamam extendsya da (c) her şeyi çöpe atmam gerekecek . Şimdi garip bir derleme hatası var nerede leafBuilder.leaf().leaf()ve leafBuilder.mid().leaf()Tamam, ama leafBuilder.leaf().mid().leaf()... başarısız
Ken yn

11
@gkamal return (T) this;bir unchecked or unsafe operationsuyarı ile sonuçlanır . Bundan kaçınmak imkansız, değil mi?
Dmitry Minkovsky

5
unchecked castUyarıyı çözmek için , diğer yanıtlar arasında önerilen çözüme bakın: stackoverflow.com/a/34741836/3114959
Stepan Vavra

8
Bunun Builder<T extends Builder>aslında bir ham tür olduğunu unutmayın - bu olmalıdır Builder<T extends Builder<T>>.
Örümcek Boris

2
@ user2957378 Builderfor GMOFactsaynı zamanda jenerik olmalıdır Builder<B extends Builder<B>> extends NutritionFacts.Builder<Builder>- ve bu model gerektiği kadar çok seviyede devam edebilir. Genel olmayan bir oluşturucu bildirirseniz, modeli genişletemezsiniz.
Örümcek Boris

44

Sadece kayıt için, kurtulmak için

unchecked or unsafe operations uyarı

return (T) this;@dimadima ve @Thomas N.'nin bahsettiği ifade için, bazı durumlarda aşağıdaki çözüm geçerlidir.

abstractJenerik türü ( T extends Builderbu durumda) bildiren oluşturucuyu yapın ve protected abstract T getThis()aşağıdaki gibi soyut yöntemi bildirin:

public abstract static class Builder<T extends Builder<T>> {

    private int calories = 0;

    public Builder() {}

    /** The solution for the unchecked cast warning. */
    public abstract T getThis();

    public T calories(int val) {
        calories = val;

        // no cast needed
        return getThis();
    }

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

Daha fazla ayrıntı için http://www.angelikalanger.com/GenericsFAQ/FAQSections/ProgrammingIdioms.html#FAQ205 adresine bakın .


Neden build()NutrutionFacts'ı iade eden yöntem burada?
mvd

@mvd Bu sorunun cevabı olduğu için mi? Alt türlerde, örneğinpublic GMOFacts build() { return new GMOFacts(this); }
Stepan Vavra

Sorun şu 2 çocuğu eklemek istediğinizde ortaya BuilderC extends BuilderBve BuilderB extends BuilderAzaman BuilderBdeğilabstract
sosite

1
Bu sorunun cevabı değil, çünkü temel sınıf soyut olmayabilir!
Roland

"Genel türü bildiren oluşturucuyu soyut yapın" - ya bu oluşturucuyu doğrudan kullanmak istersem?
daisy

21

Bir blog gönderisine dayanan bu yaklaşım, yaprak olmayan tüm sınıfların soyut olmasını ve tüm yaprak sınıflarının nihai olmasını gerektirir.

public abstract class TopLevel {
    protected int foo;
    protected TopLevel() {
    }
    protected static abstract class Builder
        <T extends TopLevel, B extends Builder<T, B>> {
        protected T object;
        protected B thisObject;
        protected abstract T createObject();
        protected abstract B thisObject();
        public Builder() {
            object = createObject();
            thisObject = thisObject();
        }
        public B foo(int foo) {
            object.foo = foo;
            return thisObject;
        }
        public T build() {
            return object;
        }
    }
}

Ardından, bu sınıfı ve kurucusunu genişleten bir ara sınıfa ve ihtiyacınız olduğu kadar fazlasına sahipsiniz:

public abstract class SecondLevel extends TopLevel {
    protected int bar;
    protected static abstract class Builder
        <T extends SecondLevel, B extends Builder<T, B>> extends TopLevel.Builder<T, B> {
        public B bar(int bar) {
            object.bar = bar;
            return thisObject;
        }
    }
}

Ve son olarak, tüm oluşturucu yöntemlerini herhangi bir ebeveyni üzerinde herhangi bir sırayla çağırabilen somut bir yaprak sınıfı:

public final class LeafClass extends SecondLevel {
    private int baz;
    public static final class Builder extends SecondLevel.Builder<LeafClass,Builder> {
        protected LeafClass createObject() {
            return new LeafClass();
        }
        protected Builder thisObject() {
            return this;
        }
        public Builder baz(int baz) {
            object.baz = baz;
            return thisObject;
        }
    }
}

Ardından, hiyerarşideki sınıfların herhangi birinden yöntemleri istediğiniz sırada çağırabilirsiniz:

public class Demo {
    LeafClass leaf = new LeafClass.Builder().baz(2).foo(1).bar(3).build();
}

Yaprak sınıflarının neden son olması gerektiğini biliyor musunuz? Somut sınıflarımın alt sınıflandırılabilir olmasını istiyorum, ancak derleyicinin türünü anlamasını sağlamanın bir yolunu bulamadım B, her zaman temel sınıf olarak çıkıyor.
David Ganster

LeafClass'taki Builder sınıfının, ara SecondLevel sınıfının yaptığı aynı <T extends SomeClass, B extends SomeClass.Builder<T,B>> extends SomeClassParent.Builder<T,B>kalıbı nasıl takip etmediğini, bunun yerine belirli türleri bildirdiğini unutmayın. Belirli türleri kullanarak yaprağa ulaşıncaya kadar bir sınıfı başlatamazsınız, ancak bir kez yaptıktan sonra, belirli türleri kullandığınız ve Tuhaf Yinelenen Şablon Kalıbını terk ettiğiniz için onu daha fazla genişletemezsiniz. Bu bağlantı yardımcı olabilir: angelikalanger.com/GenericsFAQ/FAQSections/…
Q23

7

Ayrıca calories()yöntemi geçersiz kılabilir ve genişleyen oluşturucuyu döndürmesine izin verebilirsiniz . Bu derlenir çünkü Java ortak değişken dönüş türlerini destekler .

public class GMOFacts extends NutritionFacts {
    private final boolean hasGMO;
    public static class Builder extends NutritionFacts.Builder {
        private boolean hasGMO = false;
        public Builder() {
        }
        public Builder GMO(boolean val)
        { hasGMO = val; return this; }
        public Builder calories(int val)
        { super.calories(val); return this; }
        public GMOFacts build() {
            return new GMOFacts(this);
        }
    }
    [...]
}

Ah, C ++ geçmişinden geldiğim için bunu bilmiyordum. Bu, bu küçük örnek için yararlı bir yaklaşımdır, ancak tüm yöntemleri tekrarlayan tam anlamıyla bir sınıfla bir acı ve bunda hataya açık bir acı haline gelir. Ancak bana yeni bir şey öğrettiği için +1!
Ken YN

Bana öyle geliyor ki bu hiçbir şeyi çözmüyor. Ebeveyni alt sınıflandırma nedeni (IMO), ebeveyn yöntemlerini geçersiz kılmadan yeniden kullanmaktır. Sınıflar, basit bir değer ayarlamak dışında oluşturucu yöntemlerinde gerçek mantığı olmayan basit değer nesneleriyse, geçersiz kılma yönteminde üst yöntemi çağırmanın çok az değeri vardır veya hiç değeri yoktur.
Developer Dude

Cevap, soruda açıklanan sorunu çözer: oluşturucuyu kullanan kod, her iki sıralamayla da derlenir. Bir yol derlenirken diğeri olmadığından, sanırım sonuçta bir değer olmalı.
Flavio

3

Modele göre sınıflar yaratmanın Builder"Kalıtım yerine kompozisyonu tercih et" e uyan başka bir yolu da vardır .

Ana sınıfın Buildermiras alacağı bir arayüz tanımlayın :

public interface FactsBuilder<T> {

    public T calories(int val);
}

Uygulaması NutritionFactshemen hemen aynıdır ( Builder'FactsBuilder' arayüzünün uygulanması dışında ):

public class NutritionFacts {

    private final int calories;

    public static class Builder implements FactsBuilder<Builder> {
        private int calories = 0;

        public Builder() {
        }

        @Override
        public Builder calories(int val) {
            return this;
        }

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

    protected NutritionFacts(Builder builder) {
        calories = builder.calories;
    }
}

BuilderBir çocuk sınıfının (farklı jenerik uygulanması hariç) aynı arayüz uzatmak olmalıdır:

public static class Builder implements FactsBuilder<Builder> {
    NutritionFacts.Builder baseBuilder;

    private boolean hasGMO = false;

    public Builder() {
        baseBuilder = new NutritionFacts.Builder();
    }

    public Builder GMO(boolean val) {
        hasGMO = val;
        return this;
    }

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

    @Override
    public Builder calories(int val) {
        baseBuilder.calories(val);
        return this;
    }
}

Dikkat, bu NutritionFacts.Builderiçerideki bir alandır GMOFacts.Builder(çağrılır baseBuilder). FactsBuilderArabirimden uygulanan baseBuilderyöntem aynı ada sahip yöntemini çağırır :

@Override
public Builder calories(int val) {
    baseBuilder.calories(val);
    return this;
}

Yapıcısında da büyük bir değişiklik var GMOFacts(Builder builder). Yapıcıda ana sınıf yapıcısına yapılan ilk çağrı uygun şekilde geçmelidir NutritionFacts.Builder:

protected GMOFacts(Builder builder) {
    super(builder.baseBuilder);
    hasGMO = builder.hasGMO;
}

GMOFactsSınıfın tam uygulaması :

public class GMOFacts extends NutritionFacts {

    private final boolean hasGMO;

    public static class Builder implements FactsBuilder<Builder> {
        NutritionFacts.Builder baseBuilder;

        private boolean hasGMO = false;

        public Builder() {
        }

        public Builder GMO(boolean val) {
            hasGMO = val;
            return this;
        }

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

        @Override
        public Builder calories(int val) {
            baseBuilder.calories(val);
            return this;
        }
    }

    protected GMOFacts(Builder builder) {
        super(builder.baseBuilder);
        hasGMO = builder.hasGMO;
    }
}

3

Çoklu oluşturucu mirasının tam 3 seviyeli bir örneği şu şekilde görünecektir :

(Oluşturucu için kopya oluşturuculu sürüm için aşağıdaki ikinci örneğe bakın)

Birinci seviye - ebeveyn (potansiyel olarak soyut)

import lombok.ToString;

@ToString
@SuppressWarnings("unchecked")
public abstract class Class1 {
    protected int f1;

    public static class Builder<C extends Class1, B extends Builder<C, B>> {
        C obj;

        protected Builder(C constructedObj) {
            this.obj = constructedObj;
        }

        B f1(int f1) {
            obj.f1 = f1;
            return (B)this;
        }

        C build() {
            return obj;
        }
    }
}

İkinci seviye

import lombok.ToString;

@ToString(callSuper=true)
@SuppressWarnings("unchecked")
public class Class2 extends Class1 {
    protected int f2;

    public static class Builder<C extends Class2, B extends Builder<C, B>> extends Class1.Builder<C, B> {
        public Builder() {
            this((C) new Class2());
        }

        protected Builder(C obj) {
            super(obj);
        }

        B f2(int f2) {
            obj.f2 = f2;
            return (B)this;
        }
    }
}

Üçüncü seviye

import lombok.ToString;

@ToString(callSuper=true)
@SuppressWarnings("unchecked")
public class Class3 extends Class2 {
    protected int f3;

    public static class Builder<C extends Class3, B extends Builder<C, B>> extends Class2.Builder<C, B> {
        public Builder() {
            this((C) new Class3());
        }

        protected Builder(C obj) {
            super(obj);
        }

        B f3(int f3) {
            obj.f3 = f3;
            return (B)this;
        }
    }
}

Ve bir kullanım örneği

public class Test {
    public static void main(String[] args) {
        Class2 b1 = new Class2.Builder<>().f1(1).f2(2).build();
        System.out.println(b1);
        Class2 b2 = new Class2.Builder<>().f2(2).f1(1).build();
        System.out.println(b2);

        Class3 c1 = new Class3.Builder<>().f1(1).f2(2).f3(3).build();
        System.out.println(c1);
        Class3 c2 = new Class3.Builder<>().f3(3).f1(1).f2(2).build();
        System.out.println(c2);
        Class3 c3 = new Class3.Builder<>().f3(3).f2(2).f1(1).build();
        System.out.println(c3);
        Class3 c4 = new Class3.Builder<>().f2(2).f3(3).f1(1).build();
        System.out.println(c4);
    }
}


Oluşturucu için bir kopya oluşturucu içeren biraz daha uzun bir sürüm:

Birinci seviye - ebeveyn (potansiyel olarak soyut)

import lombok.ToString;

@ToString
@SuppressWarnings("unchecked")
public abstract class Class1 {
    protected int f1;

    public static class Builder<C extends Class1, B extends Builder<C, B>> {
        C obj;

        protected void setObj(C obj) {
            this.obj = obj;
        }

        protected void copy(C obj) {
            this.f1(obj.f1);
        }

        B f1(int f1) {
            obj.f1 = f1;
            return (B)this;
        }

        C build() {
            return obj;
        }
    }
}

İkinci seviye

import lombok.ToString;

@ToString(callSuper=true)
@SuppressWarnings("unchecked")
public class Class2 extends Class1 {
    protected int f2;

    public static class Builder<C extends Class2, B extends Builder<C, B>> extends Class1.Builder<C, B> {
        public Builder() {
            setObj((C) new Class2());
        }

        public Builder(C obj) {
            this();
            copy(obj);
        }

        @Override
        protected void copy(C obj) {
            super.copy(obj);
            this.f2(obj.f2);
        }

        B f2(int f2) {
            obj.f2 = f2;
            return (B)this;
        }
    }
}

Üçüncü seviye

import lombok.ToString;

@ToString(callSuper=true)
@SuppressWarnings("unchecked")
public class Class3 extends Class2 {
    protected int f3;

    public static class Builder<C extends Class3, B extends Builder<C, B>> extends Class2.Builder<C, B> {
        public Builder() {
            setObj((C) new Class3());
        }

        public Builder(C obj) {
            this();
            copy(obj);
        }

        @Override
        protected void copy(C obj) {
            super.copy(obj);
            this.f3(obj.f3);
        }

        B f3(int f3) {
            obj.f3 = f3;
            return (B)this;
        }
    }
}

Ve bir kullanım örneği

public class Test {
    public static void main(String[] args) {
        Class3 c4 = new Class3.Builder<>().f2(2).f3(3).f1(1).build();
        System.out.println(c4);

        // Class3 builder copy
        Class3 c42 = new Class3.Builder<>(c4).f2(12).build();
        System.out.println(c42);
        Class3 c43 = new Class3.Builder<>(c42).f2(22).f1(11).build();
        System.out.println(c43);
        Class3 c44 = new Class3.Builder<>(c43).f3(13).f1(21).build();
        System.out.println(c44);
    }
}

2

Bir ya da üç açılı parantez üzerinde gözünüzü oymak istemiyorsanız ya da belki de kendinizi hissetmiyorsanız ... umm ... Yani ... öksürmek ... ekibinizin geri kalanı çabucak anlayacaktır. yinelenen jenerik model, şunu yapabilirsiniz:

public class TestInheritanceBuilder {
  public static void main(String[] args) {
    SubType.Builder builder = new SubType.Builder();
    builder.withFoo("FOO").withBar("BAR").withBaz("BAZ");
    SubType st = builder.build();
    System.out.println(st.toString());
    builder.withFoo("BOOM!").withBar("not getting here").withBaz("or here");
  }
}

Tarafından desteklenen

public class SubType extends ParentType {
  String baz;
  protected SubType() {}

  public static class Builder extends ParentType.Builder {
    private SubType object = new SubType();

    public Builder withBaz(String baz) {
      getObject().baz = baz;
      return this;
    }

    public Builder withBar(String bar) {
      super.withBar(bar);
      return this;
    }

    public Builder withFoo(String foo) {
      super.withFoo(foo);
      return this;
    }

    public SubType build() {
      // or clone or copy constructor if you want to stamp out multiple instances...
      SubType tmp = getObject();
      setObject(new SubType());
      return tmp;
    }

    protected SubType getObject() {
      return object;
    }

    private void setObject(SubType object) {
      this.object = object;
    }
  }

  public String toString() {
    return "SubType2{" +
        "baz='" + baz + '\'' +
        "} " + super.toString();
  }
}

ve ana tür:

public class ParentType {
  String foo;
  String bar;

  protected ParentType() {}

  public static class Builder {
    private ParentType object = new ParentType();

    public ParentType object() {
      return getObject();
    }

    public Builder withFoo(String foo) {
      if (!"foo".equalsIgnoreCase(foo)) throw new IllegalArgumentException();
      getObject().foo = foo;
      return this;
    }

    public Builder withBar(String bar) {
      getObject().bar = bar;
      return this;
    }

    protected ParentType getObject() {
      return object;
    }

    private void setObject(ParentType object) {
      this.object = object;
    }

    public ParentType build() {
      // or clone or copy constructor if you want to stamp out multiple instances...
      ParentType tmp = getObject();
      setObject(new ParentType());
      return tmp;
    }
  }

  public String toString() {
    return "ParentType2{" +
        "foo='" + foo + '\'' +
        ", bar='" + bar + '\'' +
        '}';
  }
}

Anahtar noktaları:

  • Nesneyi oluşturucuda kapsülleyin, böylece devralma, ana türde tutulan nesne üzerindeki alanı ayarlamanızı önler
  • Süper çağrılar, süper tür oluşturucu yöntemlerine eklenen mantığın (varsa) alt türlerde korunmasını sağlar.
  • Aşağı tarafı, üst sınıf (lar) da sahte nesne oluşturma ... Ancak bunu temizlemenin bir yolu için aşağıya bakın
  • Yukarı tarafı bir bakışta anlamak çok daha kolaydır ve özellikleri aktaran ayrıntılı bir yapıcı yoktur.
  • Oluşturucu nesnelerinize erişen birden fazla iş parçacığınız varsa ... Sanırım sen olmadığıma memnunum :).

DÜZENLE:

Sahte nesne yaratmanın etrafından bir yol buldum. Önce bunu her kurucuya ekleyin:

private Class whoAmI() {
  return new Object(){}.getClass().getEnclosingMethod().getDeclaringClass();
}

Daha sonra her kurucu için kurucuda:

  if (whoAmI() == this.getClass()) {
    this.obj = new ObjectToBuild();
  }

Maliyet, new Object(){}anonim iç sınıf için ekstra bir sınıf dosyasıdır


1

Yapabileceğiniz bir şey, sınıflarınızın her birinde statik bir fabrika yöntemi oluşturmaktır:

NutritionFacts.newBuilder()
GMOFacts.newBuilder()

Bu statik fabrika yöntemi daha sonra uygun kurucuyu döndürür. Bir GMOFacts.Builderuzatmaya sahip olabilirsiniz NutritionFacts.Builder, bu bir problem değildir. Buradaki sorun görünürlükle uğraşmak olacak ...


0

Aşağıdaki IEEE katkısı Refined Fluent Builder in Java , soruna kapsamlı bir çözüm sunar.

Orijinal soruyu, kalıtım eksikliği ve yarı değişmezlik olmak üzere iki alt soruna böler ve bu iki alt soruna çözümün, Java'da klasik oluşturucu modelinde kod yeniden kullanımıyla kalıtım desteğine nasıl açıldığını gösterir.


Bu cevap yardımcı olacak herhangi bir bilgi içermiyor, bağlantıda verilen cevabın en azından bir özetini içermiyor ve giriş yapılmasını gerektiren bir bağlantıya götürüyor.
Sonata

Bu cevap, resmi bir yayın otoritesine ve resmi yayınlama ve paylaşım prosedürüne sahip hakemli bir konferans yayınına bağlanır.
mc00x1

0

İki biçimsel tür parametresini kabul eden bir ana, soyut genel oluşturucu sınıfı oluşturdum. Birincisi, build () tarafından döndürülen nesne türü için, ikincisi ise her bir isteğe bağlı parametre ayarlayıcı tarafından döndürülen türdür. Aşağıda örnek amaçlı ebeveyn ve çocuk sınıfları bulunmaktadır:

// **Parent**
public abstract static class Builder<T, U extends Builder<T, U>> {
    // Required parameters
    private final String name;

    // Optional parameters
    private List<String> outputFields = null;


    public Builder(String pName) {
        name = pName;
    }

    public U outputFields(List<String> pOutFlds) {
        outputFields = new ArrayList<>(pOutFlds);
        return getThis();
    }


    /**
     * This helps avoid "unchecked warning", which would forces to cast to "T" in each of the optional
     * parameter setters..
     * @return
     */
    abstract U getThis();

    public abstract T build();



    /*
     * Getters
     */
    public String getName() {
        return name;
    }
}

 // **Child**
 public static class Builder extends AbstractRule.Builder<ContextAugmentingRule, ContextAugmentingRule.Builder> {
    // Required parameters
    private final Map<String, Object> nameValuePairsToAdd;

    // Optional parameters
    private String fooBar;


    Builder(String pName, Map<String, String> pNameValPairs) {
        super(pName);
        /**
         * Must do this, in case client code (I.e. JavaScript) is re-using
         * the passed in for multiple purposes. Doing {@link Collections#unmodifiableMap(Map)}
         * won't caught it, because the backing Map passed by client prior to wrapping in
         * unmodifiable Map can still be modified.
         */
        nameValuePairsToAdd = new HashMap<>(pNameValPairs);
    }

    public Builder fooBar(String pStr) {
        fooBar = pStr;
        return this;
    }


    @Override
    public ContextAugmentingRule build() {
        try {
            Rule r = new ContextAugmentingRule(this);
            storeInRuleByNameCache(r);
            return (ContextAugmentingRule) r;
        } catch (RuleException e) {
            throw new IllegalArgumentException(e);
        }
    }

    @Override
    Builder getThis() {
        return this;
    }
}

Bu benim ihtiyaçlarımı tatmin edecek şekilde karşıladı.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.