แก้ไข:ฉันต้องการจะชี้ให้เห็นว่าคำถามนี้อธิบายถึงปัญหาทางทฤษฎีและฉันรู้ว่าฉันสามารถใช้อาร์กิวเมนต์ตัวสร้างสำหรับพารามิเตอร์บังคับหรือส่งข้อยกเว้น runtime ถ้าใช้ API ไม่ถูกต้อง อย่างไรก็ตามฉันกำลังมองหาวิธีแก้ปัญหาที่ไม่ต้องการตัวสร้างอาร์กิวเมนต์หรือการตรวจสอบรันไทม์
ลองนึกภาพคุณมีCarอินเทอร์เฟซเช่นนี้:
public interface Car {
    public Engine getEngine(); // required
    public Transmission getTransmission(); // required
    public Stereo getStereo(); // optional
}
ตามความคิดเห็นที่จะแนะนำCarต้องมีEngineและTransmissionแต่Stereoเป็นตัวเลือก นั่นหมายความว่าสร้างที่สามารถเพียงตัวอย่างที่เคยควรจะมีวิธีการถ้าและมีทั้งรับแล้วมอบให้กับผู้สร้างอินสแตนซ์ ด้วยวิธีการตรวจสอบชนิดจะปฏิเสธที่จะรวบรวมรหัสใด ๆ ที่พยายามที่จะสร้างอินสแตนซ์โดยไม่ต้องหรือbuild()Carbuild()EngineTransmissionCarEngineTransmission
สายนี้สำหรับสร้างขั้นตอน โดยทั่วไปแล้วคุณจะใช้สิ่งนี้:
public interface Car {
    public Engine getEngine(); // required
    public Transmission getTransmission(); // required
    public Stereo getStereo(); // optional
    public class Builder {
        public BuilderWithEngine engine(Engine engine) {
            return new BuilderWithEngine(engine);
        }
    }
    public class BuilderWithEngine {
        private Engine engine;
        private BuilderWithEngine(Engine engine) {
            this.engine = engine;
        }
        public BuilderWithEngine engine(Engine engine) {
            this.engine = engine;
            return this;
        }
        public CompleteBuilder transmission(Transmission transmission) {
            return new CompleteBuilder(engine, transmission);
        }
    }
    public class CompleteBuilder {
        private Engine engine;
        private Transmission transmission;
        private Stereo stereo = null;
        private CompleteBuilder(Engine engine, Transmission transmission) {
            this.engine = engine;
            this.transmission = transmission;
        }
        public CompleteBuilder engine(Engine engine) {
            this.engine = engine;
            return this;
        }
        public CompleteBuilder transmission(Transmission transmission) {
            this.transmission = transmission;
            return this;
        }
        public CompleteBuilder stereo(Stereo stereo) {
            this.stereo = stereo;
            return this;
        }
        public Car build() {
            return new Car() {
                @Override
                public Engine getEngine() {
                    return engine;
                }
                @Override
                public Transmission getTransmission() {
                    return transmission;
                }
                @Override
                public Stereo getStereo() {
                    return stereo;
                }
            };
        }
    }
}
มีห่วงโซ่ของการเรียนการสร้างที่แตกต่างกัน (เป็นBuilder, BuilderWithEngine, CompleteBuilder) ที่เพิ่มวิธีการตั้งค่าที่จำเป็นหนึ่งหลังจากที่อื่นกับการเรียนที่ผ่านมามีทั้งหมดวิธีการตั้งค่าตัวเลือกเช่นกัน 
ซึ่งหมายความว่าผู้ใช้งานของผู้สร้างขั้นตอนนี้จะถูก จำกัด อยู่ในลำดับที่ผู้เขียนทำให้ผู้เซ็ตอัพสามารถใช้งานได้ นี่คือตัวอย่างของการใช้งานที่เป็นไปได้ (โปรดทราบว่าทั้งหมดนั้นมีคำสั่งอย่างเคร่งครัด: engine(e)ก่อนตามด้วยtransmission(t)และในที่สุดก็เป็นทางเลือกstereo(s))
new Builder().engine(e).transmission(t).build();
new Builder().engine(e).transmission(t).stereo(s).build();
new Builder().engine(e).engine(e).transmission(t).stereo(s).build();
new Builder().engine(e).transmission(t).engine(e).stereo(s).build();
new Builder().engine(e).transmission(t).stereo(s).engine(e).build();
new Builder().engine(e).transmission(t).transmission(t).stereo(s).build();
new Builder().engine(e).transmission(t).stereo(s).transmission(t).build();
new Builder().engine(e).transmission(t).stereo(s).stereo(s).build();
อย่างไรก็ตามมีสถานการณ์มากมายที่ไม่เหมาะสำหรับผู้ใช้ของผู้สร้างโดยเฉพาะอย่างยิ่งหากผู้สร้างไม่ได้เป็นเพียงผู้ตั้งค่า แต่ยังเป็นผู้เพิ่มหรือหากผู้ใช้ไม่สามารถควบคุมลำดับที่คุณสมบัติบางอย่างสำหรับผู้สร้างจะสามารถใช้ได้
ทางออกเดียวที่ฉันคิดได้ว่ามีความซับซ้อนมาก:สำหรับการรวมกันของคุณสมบัติบังคับที่ได้รับการตั้งค่าหรือยังไม่ได้ตั้งค่าฉันสร้างคลาสตัวสร้างเฉพาะที่รู้ว่าตัวบังคับที่จำเป็นอื่น ๆ สถานะที่build()ควรมีวิธีการและแต่ละตัวตั้งค่าเหล่านั้นส่งคืนชนิดของตัวสร้างที่สมบูรณ์มากขึ้นซึ่งเป็นขั้นตอนเดียวที่มีbuild()วิธีการบรรจุมากกว่า 
ฉันเพิ่มรหัสด้านล่าง แต่คุณอาจบอกว่าฉันใช้ระบบประเภทเพื่อสร้าง FSM ที่ให้คุณสร้าง a Builderซึ่งสามารถเปลี่ยนเป็น a BuilderWithEngineหรือBuilderWithTransmissionซึ่งทั้งสองสามารถกลายเป็น a CompleteBuilderซึ่งใช้build()วิธี. setters ที่เป็นตัวเลือกสามารถถูกเรียกใช้บนอินสแตนซ์ตัวสร้างใด ๆ เหล่านี้

public interface Car {
    public Engine getEngine(); // required
    public Transmission getTransmission(); // required
    public Stereo getStereo(); // optional
    public class Builder extends OptionalBuilder {
        public BuilderWithEngine engine(Engine engine) {
            return new BuilderWithEngine(engine, stereo);
        }
        public BuilderWithTransmission transmission(Transmission transmission) {
            return new BuilderWithTransmission(transmission, stereo);
        }
        @Override
        public Builder stereo(Stereo stereo) {
            super.stereo(stereo);
            return this;
        }
    }
    public class OptionalBuilder {
        protected Stereo stereo = null;
        private OptionalBuilder() {}
        public OptionalBuilder stereo(Stereo stereo) {
            this.stereo = stereo;
            return this;
        }
    }
    public class BuilderWithEngine extends OptionalBuilder {
        private Engine engine;
        private BuilderWithEngine(Engine engine, Stereo stereo) {
            this.engine = engine;
            this.stereo = stereo;
        }
        public CompleteBuilder transmission(Transmission transmission) {
            return new CompleteBuilder(engine, transmission, stereo);
        }
        public BuilderWithEngine engine(Engine engine) {
            this.engine = engine;
            return this;
        }
        @Override
        public BuilderWithEngine stereo(Stereo stereo) {
            super.stereo(stereo);
            return this;
        }
    }
    public class BuilderWithTransmission extends OptionalBuilder {
        private Transmission transmission;
        private BuilderWithTransmission(Transmission transmission, Stereo stereo) {
            this.transmission = transmission;
            this.stereo = stereo;
        }
        public CompleteBuilder engine(Engine engine) {
            return new CompleteBuilder(engine, transmission, stereo);
        }
        public BuilderWithTransmission transmission(Transmission transmission) {
            this.transmission = transmission;
            return this;
        }
        @Override
        public BuilderWithTransmission stereo(Stereo stereo) {
            super.stereo(stereo);
            return this;
        }
    }
    public class CompleteBuilder extends OptionalBuilder {
        private Engine engine;
        private Transmission transmission;
        private CompleteBuilder(Engine engine, Transmission transmission, Stereo stereo) {
            this.engine = engine;
            this.transmission = transmission;
            this.stereo = stereo;
        }
        public CompleteBuilder engine(Engine engine) {
            this.engine = engine;
            return this;
        }
        public CompleteBuilder transmission(Transmission transmission) {
            this.transmission = transmission;
            return this;
        }
        @Override
        public CompleteBuilder stereo(Stereo stereo) {
            super.stereo(stereo);
            return this;
        }
        public Car build() {
            return new Car() {
                @Override
                public Engine getEngine() {
                    return engine;
                }
                @Override
                public Transmission getTransmission() {
                    return transmission;
                }
                @Override
                public Stereo getStereo() {
                    return stereo;
                }
            };
        }
    }
}
อย่างที่คุณสามารถบอกได้ว่าสิ่งนี้ไม่ได้ปรับขนาดได้อย่างดีเนื่องจากจำนวนคลาสตัวสร้างที่แตกต่างกันที่ต้องการคือO (2 ^ n)โดยที่nคือจำนวนเซ็ตเตอร์บังคับ
ดังนั้นคำถามของฉัน: สิ่งนี้สามารถทำได้อย่างสวยงามยิ่งขึ้น?
(ฉันกำลังมองหาคำตอบที่ใช้งานได้กับ Java แม้ว่า Scala จะเป็นที่ยอมรับเช่นกัน)
.engine(e)สองครั้งสำหรับผู้สร้างหนึ่งหมายความว่าอย่างไร
                build()ถ้าคุณยังไม่ได้เรียกengine(e)และtransmission(t)ก่อนที่จะ
                Engineใช้งานเริ่มต้นและเขียนทับมันในภายหลังด้วยการใช้งานที่เฉพาะเจาะจงมากขึ้น แต่ส่วนใหญ่นี้จะทำให้ความรู้สึกมากขึ้นถ้าengine(e)ไม่ได้เป็นหมา addEngine(e)แต่เป็นงูพิษที่: สิ่งนี้จะเป็นประโยชน์สำหรับCarผู้สร้างที่สามารถผลิตรถยนต์ไฮบริดที่มีมากกว่าหนึ่งเครื่องยนต์ / มอเตอร์ เนื่องจากนี่เป็นตัวอย่างที่วางแผนไว้ฉันไม่ได้ลงรายละเอียดว่าทำไมคุณถึงอยากทำเช่นนี้ - เพื่อความกระชับ
                
this?