ปรับปรุงรูปแบบการออกแบบตัวสร้างของ Joshua Bloch หรือไม่


12

ย้อนกลับไปในปีพ. ศ. 2550 ฉันอ่านบทความเกี่ยวกับ Joshua Blochs เกี่ยวกับ "รูปแบบการสร้าง" และวิธีที่จะสามารถแก้ไขเพื่อปรับปรุงการใช้ตัวสร้างและตัวตั้งค่ามากเกินไปโดยเฉพาะอย่างยิ่งเมื่อวัตถุมีคุณสมบัติจำนวนมากซึ่งส่วนใหญ่เป็นทางเลือก สรุปโดยย่อของรูปแบบการออกแบบนี้จะฝึกหัดที่นี่

ฉันชอบแนวคิดนี้และใช้มันตั้งแต่ ปัญหากับมันในขณะที่มันสะอาดและดีที่จะใช้จากมุมมองของลูกค้าการใช้มันอาจเป็นความเจ็บปวดในก้น! มีสถานที่แตกต่างกันมากมายในวัตถุที่มีการอ้างอิงคุณสมบัติเดียวและทำให้การสร้างวัตถุและการเพิ่มคุณสมบัติใหม่ใช้เวลานาน

ดังนั้น ... ฉันมีความคิด ก่อนวัตถุตัวอย่างในสไตล์ของ Joshua Bloch:

สไตล์ Josh Bloch:

public class OptionsJoshBlochStyle {

    private final String option1;
    private final int option2;
    // ...other options here  <<<<

    public String getOption1() {
        return option1;
    }

    public int getOption2() {
        return option2;
    }

    public static class Builder {

        private String option1;
        private int option2;
        // other options here <<<<<

        public Builder option1(String option1) {
            this.option1 = option1;
            return this;
        }

        public Builder option2(int option2) {
            this.option2 = option2;
            return this;
        }

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

    private OptionsJoshBlochStyle(Builder builder) {
        this.option1 = builder.option1;
        this.option2 = builder.option2;
        // other options here <<<<<<
    }

    public static void main(String[] args) {
        OptionsJoshBlochStyle optionsVariation1 = new OptionsJoshBlochStyle.Builder().option1("firefox").option2(1).build();
        OptionsJoshBlochStyle optionsVariation2 = new OptionsJoshBlochStyle.Builder().option1("chrome").option2(2).build();
    }
}

ตอนนี้รุ่น "ปรับปรุง" ของฉัน:

public class Options {

    // note that these are not final
    private String option1;
    private int option2;
    // ...other options here

    public String getOption1() {
        return option1;
    }

    public int getOption2() {
        return option2;
    }

    public static class Builder {

        private final Options options = new Options();

        public Builder option1(String option1) {
            this.options.option1 = option1;
            return this;
        }

        public Builder option2(int option2) {
            this.options.option2 = option2;
            return this;
        }

        public Options build() {
            return options;
        }
    }

    private Options() {
    }

    public static void main(String[] args) {
        Options optionsVariation1 = new Options.Builder().option1("firefox").option2(1).build();
        Options optionsVariation2 = new Options.Builder().option1("chrome").option2(2).build();

    }
}

ดังที่คุณเห็นใน "รุ่นปรับปรุง" ของฉันมีสถานที่น้อยกว่า 2 แห่งที่เราต้องเพิ่มรหัสเกี่ยวกับคุณสมบัติเพิ่มเติม (หรือตัวเลือกในกรณีนี้)! ข้อเสียอย่างเดียวที่ฉันเห็นได้คือตัวแปรอินสแตนซ์ของคลาสภายนอกไม่สามารถสุดท้ายได้ แต่ชั้นเรียนยังคงไม่เปลี่ยนรูปหากปราศจากสิ่งนี้

มีข้อเสียใด ๆ ในการปรับปรุงนี้ในการบำรุงรักษา? จะต้องมีเหตุผลที่เขาซ้ำคุณสมบัติภายในชั้นซ้อนที่ฉันไม่เห็น?


นี้ดูเหมือนคล้ายกับเวลาของฉันของรูปแบบการสร้างใน C # ที่นี่
MattDavey

คำตอบ:


12

รูปแบบของคุณค่อนข้างดี แต่มันช่วยให้ผู้ใช้ทำสิ่งนี้:

Options.Builder builder = new Options.Builder().option1("firefox").option2(1);
Options optionsVariation1 = builder.build();
assert optionsVariation1.getOption1().equals("firefox");
builder.option1("chrome");
assert optionsVariation1.getOption1().equals("firefox"); // FAILURE!

ซึ่งค่อนข้างเอาชนะวัตถุ

คุณสามารถเปลี่ยนbuildวิธีการทำเช่นนี้:

public Options build() {
    Options options = this.options;
    this.options = null;
    return options;
}

ซึ่งจะป้องกันสิ่งนี้ - การเรียกไปยังเมธอด setter บนตัวสร้างหลังจากการbuildโทรจะล้มเหลวด้วย NullPointerException หากคุณต้องการแฟลชคุณสามารถทดสอบค่าว่างและโยน IllegalStateException หรืออะไรก็ได้แทน และคุณสามารถเลื่อนไปยังคลาสพื้นฐานทั่วไปซึ่งสามารถใช้ได้กับผู้สร้างทั้งหมด


1
ฉันจะเปลี่ยนบรรทัดที่ 2 ไปที่:build() this.options = new Options();ด้วยวิธีนี้อินสแตนซ์ของตัวเลือกจะไม่เปลี่ยนรูปได้อย่างปลอดภัยรวมทั้งตัวสร้างจะสามารถใช้ซ้ำได้ในเวลาเดียวกัน
Natix

5

ผู้สร้างในรูปแบบของ Bloch อาจใช้หลายครั้งเพื่อสร้างวัตถุที่ "ส่วนใหญ่" เหมือนกัน นอกจากนี้วัตถุที่ไม่เปลี่ยนรูป (เขตข้อมูลทั้งหมดถือเป็นที่สิ้นสุดและไม่สามารถเปลี่ยนแปลงตัวเองได้) มีข้อดีด้านความปลอดภัยของเธรดซึ่งการเปลี่ยนแปลงของคุณอาจเอาชนะได้


0

หากตัวเลือกนั้นสามารถลอกแบบได้อย่างมีประสิทธิภาพ (ฉันหมายถึงโดยไม่คำนึงถึงส่วนต่อประสาน Cloneable) คุณสามารถใช้รูปแบบต้นแบบ - มีหนึ่งในตัวสร้างและโคลนในตัวสร้าง ()

หากคุณไม่ได้ใช้ส่วนต่อประสาน Cloneable คุณจะต้องคัดลอกทุกช่องดังนั้นมันจะเพิ่มสถานที่อื่นที่คุณต้องการเพิ่มดังนั้นอย่างน้อยสำหรับชั้นเรียนที่มีเขตข้อมูลอย่างง่ายโดยใช้ Cloneable จะเป็นความคิดที่ดี

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.