Phân lớp một lớp Trình tạo Java


133

Cung cấp bài viết này cho Tiến sĩ Dobbs và Mô hình Trình tạo cụ thể, làm thế nào để chúng tôi xử lý trường hợp phân lớp Trình tạo? Lấy một phiên bản rút gọn của ví dụ mà chúng tôi muốn phân lớp để thêm nhãn GMO, một triển khai ngây thơ sẽ là:

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

Phân lớp:

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

Bây giờ, chúng ta có thể viết mã như thế này:

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

Nhưng, nếu chúng tôi nhận được lệnh sai, tất cả đều thất bại:

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

Vấn đề tất nhiên là NutritionFacts.Buildertrả về a NutritionFacts.Builder, không phải a GMOFacts.Builder, vậy làm thế nào để chúng ta giải quyết vấn đề này, hoặc có một Mô hình tốt hơn để sử dụng?

Lưu ý: câu trả lời này cho một câu hỏi tương tự cung cấp các lớp tôi có ở trên; câu hỏi của tôi liên quan đến vấn đề đảm bảo các cuộc gọi của người xây dựng theo đúng thứ tự.


1
Tôi nghĩ rằng liên kết sau đây mô tả một cách tiếp cận tốt: egalluzzo.blogspot.co.at/2010/06/ trên
stuXnet

1
Nhưng làm thế nào để bạn build()đầu ra của b.GMO(true).calories(100)?
Sridhar Sarnobat

Câu trả lời:


170

Bạn có thể giải quyết nó bằng cách sử dụng thuốc generic. Tôi nghĩ rằng điều này được gọi là "mô hình chung định kỳ tò mò"

Làm cho kiểu trả về của các phương thức xây dựng lớp cơ sở thành một đối số chung.

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

Bây giờ khởi tạo trình xây dựng cơ sở với trình xây dựng lớp dẫn xuất làm đối số chung.

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, tôi nghĩ rằng tôi sẽ phải (a) đăng một câu hỏi mới, (b) thiết kế lại implementsthay vì extends, hoặc (c) vứt bỏ mọi thứ. Bây giờ tôi có một lỗi biên dịch kỳ lạ ở đâu leafBuilder.leaf().leaf()leafBuilder.mid().leaf()vẫn ổn, nhưng leafBuilder.leaf().mid().leaf()không thành công ...
Ken YN

11
@gkamal return (T) this;dẫn đến một unchecked or unsafe operationscảnh báo. Điều này là không thể tránh được, phải không?
Dmitry Minkovsky

5
Để giải quyết unchecked castcảnh báo, hãy xem giải pháp được đề xuất dưới đây trong số các câu trả lời khác: stackoverflow.com/a/34741836/3114959
Stepan Vavra

8
Lưu ý rằng đó Builder<T extends Builder>thực sự là một rawtype - điều này nên được Builder<T extends Builder<T>>.
Boris the Spider

2
@ user2957378 sự Buildercho GMOFactscũng cần phải chung Builder<B extends Builder<B>> extends NutritionFacts.Builder<Builder>- và mô hình này có thể tiếp tục xuống như nhiều cấp độ theo yêu cầu. Nếu bạn khai báo một trình xây dựng không chung chung thì bạn không thể mở rộng mẫu.
Boris the Spider

44

Chỉ để ghi lại, để thoát khỏi

unchecked or unsafe operations cảnh báo

đối với return (T) this;tuyên bố như @dimadima và @Thomas N. nói về, giải pháp sau đây được áp dụng trong một số trường hợp nhất định.

Tạo abstracttrình xây dựng khai báo kiểu chung ( T extends Buildertrong trường hợp này) và khai báo protected abstract T getThis()phương thức trừu tượng như sau:

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

Tham khảo http://www.angelikalanger.com/GenericsFAQ/FAQSections/ProgrammingIdioms.html#FAQ205 để biết thêm chi tiết.


Tại sao build()phương thức trả về NutrestionFacts ở đây?
mvd

@mvd Vì đây là câu trả lời cho câu hỏi? Trong các kiểu con, bạn sẽ ghi đè lên nó, chẳng hạn nhưpublic GMOFacts build() { return new GMOFacts(this); }
Stepan Vavra

Vấn đề xảy ra khi chúng tôi muốn thêm đứa con thứ 2 BuilderC extends BuilderBBuilderB extends BuilderAkhi BuilderBkhôngabstract
nằm trong

1
Đây không phải là một câu trả lời cho câu hỏi, bởi vì lớp cơ sở có thể không trừu tượng!
Roland

"Tạo trừu tượng trình xây dựng khai báo loại chung" - nếu tôi muốn sử dụng trình xây dựng đó trực tiếp thì sao?
cúc

21

Dựa trên một bài đăng trên blog , cách tiếp cận này yêu cầu tất cả các lớp không lá phải trừu tượng và tất cả các lớp lá phải là cuối cùng.

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

Sau đó, bạn có một số lớp trung gian mở rộng lớp này và trình xây dựng của nó, và nhiều hơn nữa bạn cần:

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

Và, cuối cùng, một lớp lá bê tông có thể gọi tất cả các phương thức xây dựng trên bất kỳ cha mẹ nào của nó theo bất kỳ thứ tự nào:

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

Sau đó, bạn có thể gọi các phương thức theo bất kỳ thứ tự nào, từ bất kỳ lớp nào trong hệ thống phân cấp:

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

Bạn có biết tại sao các lớp lá cần phải là cuối cùng? Tôi muốn các lớp cụ thể của mình có thể được phân lớp, nhưng chưa tìm được cách làm cho trình biên dịch hiểu loại B, nó luôn luôn là lớp cơ sở.
David Ganster

Lưu ý cách lớp Builder trong LeafClass không tuân theo cùng một <T extends SomeClass, B extends SomeClass.Builder<T,B>> extends SomeClassParent.Builder<T,B>mẫu mà lớp SecondLevel trung gian thực hiện, thay vào đó nó khai báo các loại cụ thể. Bạn không thể kích hoạt một lớp cho đến khi bạn nhận được lá bằng cách sử dụng các loại cụ thể, nhưng một khi bạn làm như vậy, bạn không thể mở rộng thêm nữa vì bạn đang sử dụng các loại cụ thể và đã từ bỏ Mẫu Mẫu lặp lại tò mò. Liên kết này có thể giúp: angelikalanger.com/GenericsFAQ/FAQSections/ cấp
Q23

7

Bạn cũng có thể ghi đè calories()phương thức và để nó trả về trình xây dựng mở rộng. Điều này biên dịch bởi vì Java hỗ trợ các kiểu trả về covariant .

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);
        }
    }
    [...]
}

À, tôi không biết điều đó, vì tôi đến từ nền tảng C ++. Đó là một cách tiếp cận hữu ích cho ví dụ nhỏ này, nhưng với một lớp đầy đủ lặp đi lặp lại, tất cả các phương thức sẽ trở thành một nỗi đau và một nỗi đau dễ bị lỗi ở đó. +1 để dạy tôi một cái gì đó mới, tuy nhiên!
Ken YN

Dường như với tôi rằng điều này không giải quyết được gì. Lý do (IMO) để phân lớp phụ huynh là để sử dụng lại các phương thức cha mẹ mà không ghi đè chúng. Nếu các lớp chỉ đơn giản là các đối tượng giá trị không có logic thực trong các phương thức của trình xây dựng ngoại trừ để đặt một giá trị đơn giản, thì việc gọi phương thức cha trong phương thức ghi đè có ít hoặc không có giá trị.
Nhà phát triển Dude

Câu trả lời giải quyết vấn đề được mô tả trong câu hỏi: mã sử dụng trình xây dựng biên dịch với cả hai thứ tự. Vì một cách biên dịch và cách khác thì không, tôi đoán rằng rốt cuộc phải có một số giá trị.
Flavio

3

Ngoài ra còn có một cách khác để tạo các lớp theo Buildermẫu, phù hợp với "Ưu tiên thành phần hơn kế thừa".

Xác định một giao diện, lớp cha đó Buildersẽ kế thừa:

public interface FactsBuilder<T> {

    public T calories(int val);
}

Việc triển khai NutritionFactsgần như giống nhau (ngoại trừ việc Buildertriển khai giao diện 'FactBuilder'):

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

Lớp Buildercon nên mở rộng cùng một giao diện (ngoại trừ việc triển khai chung khác nhau):

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

Lưu ý, đó NutritionFacts.Builderlà một trường bên trong GMOFacts.Builder(được gọi baseBuilder). Phương thức được thực hiện từ phương thức gọi cùng tên của FactsBuildergiao diện baseBuilder:

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

Ngoài ra còn có một sự thay đổi lớn trong các nhà xây dựng của GMOFacts(Builder builder). Cuộc gọi đầu tiên trong hàm tạo đến hàm tạo của lớp cha nên truyền thích hợp NutritionFacts.Builder:

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

Việc thực hiện đầy đủ của GMOFactslớp:

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

Một ví dụ đầy đủ 3 cấp về thừa kế nhiều trình xây dựng sẽ như thế này :

(Đối với phiên bản có hàm tạo sao chép cho trình tạo, hãy xem ví dụ thứ hai bên dưới)

Cấp độ đầu tiên - cha mẹ (có khả năng trừu tượng)

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

Cấp độ thứ hai

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

Cấp độ thứ ba

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

Và một ví dụ về việc sử dụng

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


Phiên bản dài hơn một chút có tính năng xây dựng bản sao cho trình tạo:

Cấp độ đầu tiên - cha mẹ (có khả năng trừu tượng)

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

Cấp độ thứ hai

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

Cấp độ thứ ba

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

Và một ví dụ về việc sử dụng

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

Nếu bạn không muốn thò mắt ra một khung góc hoặc ba, hoặc có lẽ không cảm thấy bạn ... ừm ... ý tôi là ... ho ... những người còn lại trong nhóm của bạn sẽ nhanh chóng hiểu được một cách tò mò mô hình chung định kỳ, bạn có thể làm điều này:

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

được hỗ trợ bởi

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

và kiểu cha mẹ:

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 + '\'' +
        '}';
  }
}

Những điểm chính:

  • Đóng gói đối tượng trong trình tạo để kế thừa ngăn bạn thiết lập trường trên đối tượng được giữ trong kiểu cha
  • Các cuộc gọi đến siêu đảm bảo rằng logic (nếu có) được thêm vào các phương thức xây dựng siêu kiểu được giữ lại trong các kiểu phụ.
  • Mặt trái là tạo đối tượng giả trong lớp cha mẹ ... Nhưng hãy xem bên dưới để biết cách làm sạch nó
  • Nhìn lên dễ hiểu hơn nhiều trong nháy mắt, và không có thuộc tính xây dựng dài dòng chuyển thuộc tính.
  • Nếu bạn có nhiều luồng truy cập vào các đối tượng trình tạo của mình ... Tôi đoán tôi rất vui vì tôi không phải là bạn :).

BIÊN TẬP:

Tôi tìm thấy một cách xung quanh việc tạo đối tượng giả. Đầu tiên thêm phần này vào mỗi trình xây dựng:

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

Sau đó, trong hàm tạo cho mỗi trình xây dựng:

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

Chi phí là một tệp lớp bổ sung cho new Object(){}lớp bên trong ẩn danh


1

Một điều bạn có thể làm là tạo một phương thức nhà máy tĩnh trong mỗi lớp của bạn:

NutritionFacts.newBuilder()
GMOFacts.newBuilder()

Phương pháp nhà máy tĩnh này sau đó sẽ trả về trình xây dựng thích hợp. Bạn có thể GMOFacts.Buildermở rộng một NutritionFacts.Builder, đó không phải là một vấn đề. Vấn đề ở đây sẽ là đối phó với tầm nhìn ...


0

Trình đóng góp tinh chỉnh của IEEE sau đây trong Java cung cấp một giải pháp toàn diện cho vấn đề này.

Nó phân tích câu hỏi ban đầu thành hai vấn đề phụ về thiếu hụt thừa kếbất biến gần đúng và chỉ ra cách giải pháp cho hai vấn đề phụ này mở ra để hỗ trợ thừa kế với việc sử dụng lại mã trong mẫu xây dựng cổ điển trong Java.


Câu trả lời này không chứa bất kỳ thông tin nào hữu ích, không chứa ít nhất một bản tóm tắt câu trả lời được đưa ra trong liên kết và dẫn đến một liên kết yêu cầu đăng nhập.
Sonata

Câu trả lời này liên kết đến một ấn phẩm hội nghị được đánh giá ngang hàng với cơ quan xuất bản chính thức và quy trình xuất bản và chia sẻ chính thức.
mc00x1

0

Tôi đã tạo một lớp cha, trình xây dựng chung trừu tượng chấp nhận hai tham số kiểu chính thức. Đầu tiên là cho loại đối tượng được trả về bởi build (), thứ hai là loại được trả về bởi mỗi bộ thiết lập tham số tùy chọn. Dưới đây là các lớp cha mẹ và con cho mục đích minh họa:

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

Điều này đã đáp ứng nhu cầu của tôi để hài lòng.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.