การจัดการตัวสร้างด้วยพารามิเตอร์จำนวนมากใน Java


106

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

ฉันจะรับทราบว่าการใช้ DI อัตโนมัติผ่านบางอย่างเช่น Guice น่าจะดี แต่เนื่องจากเหตุผลทางเทคนิคบางประการโครงการเฉพาะเหล่านี้จึง จำกัด เฉพาะ Java

รูปแบบการจัดระเบียบอาร์กิวเมนต์ตามตัวอักษรตามประเภทจะใช้ไม่ได้เพราะถ้าประเภทถูก refactored (วงกลมที่คุณส่งผ่านสำหรับอาร์กิวเมนต์ 2 ตอนนี้เป็นรูปร่าง) ก็อาจไม่เป็นระเบียบ

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

คำตอบ:


267

รูปแบบการออกแบบตัวสร้างอาจช่วยได้ ลองพิจารณาตัวอย่างต่อไปนี้

public class StudentBuilder
{
    private String _name;
    private int _age = 14;      // this has a default
    private String _motto = ""; // most students don't have one

    public StudentBuilder() { }

    public Student buildStudent()
    {
        return new Student(_name, _age, _motto);
    }

    public StudentBuilder name(String _name)
    {
        this._name = _name;
        return this;
    }

    public StudentBuilder age(int _age)
    {
        this._age = _age;
        return this;
    }

    public StudentBuilder motto(String _motto)
    {
        this._motto = _motto;
        return this;
    }
}

สิ่งนี้ช่วยให้เราเขียนโค้ดเช่น

Student s1 = new StudentBuilder().name("Eli").buildStudent();
Student s2 = new StudentBuilder()
                 .name("Spicoli")
                 .age(16)
                 .motto("Aloha, Mr Hand")
                 .buildStudent();

หากเราเว้นฟิลด์ที่กำหนดไว้ (น่าจะต้องระบุชื่อ) เราสามารถให้ตัวสร้าง Student โยนข้อยกเว้นได้ และช่วยให้เรามีอาร์กิวเมนต์เริ่มต้น / เป็นทางเลือกโดยไม่จำเป็นต้องติดตามลำดับอาร์กิวเมนต์ใด ๆ เนื่องจากลำดับของการเรียกเหล่านั้นจะทำงานได้ดีเท่า ๆ กัน


10
แน่นอนว่าด้วยการนำเข้าแบบคงที่คุณจะไม่ต้อง "เห็น" "ผู้สร้าง" เหล่านี้เลยด้วยซ้ำ ตัวอย่างเช่นคุณสามารถมีชื่อวิธีการแบบคงที่ (ชื่อสตริง) ซึ่งส่งคืนตัวสร้างและนักเรียน (StudentBuilder) ซึ่งส่งคืนนักเรียน ดังนั้นนักเรียน (ชื่อ ("โจ") อายุ (15) คำขวัญ ("ฉันเปียกตัวเอง"));
oxbow_lakes

2
@oxbow_lakes: ในตัวอย่างของคุณคลาสใดมีชื่อเมธอดแบบคงที่ (ชื่อสตริง)
user443854

ในทางเทคนิคเป็นไปได้ที่จะใช้ชั้นนักเรียนเพื่อสร้างนักเรียนใหม่ ฉันได้เพิ่มวิธีการในชั้นเรียนนักเรียนแล้วและมันก็ใช้ได้ดี ด้วยวิธีนี้ฉันไม่จำเป็นต้องมีคลาส Builder อื่น ฉันไม่แน่ใจว่าสิ่งนี้เป็นที่ต้องการหรือไม่ มีเหตุผลที่จะใช้คลาส (StudentBuilder) อื่นเพื่อสร้างหรือไม่
WVrock

1
@WVrock: ขึ้นอยู่กับการใช้งานของคุณ ดังที่ฉันพูดในคำตอบของฉันการทำเช่นนี้กับชั้นเรียนของนักเรียนเองอาจทำให้ชั้นเรียนอยู่ในสถานะเริ่มต้นครึ่งหนึ่งได้เช่นหากคุณมีฟิลด์ที่ต้องการซึ่งยังไม่ได้เริ่มต้น
Eli Courtwright

@EliCourtwright ฉันเดาว่ามันเกี่ยวกับความชอบ / การออกแบบโค้ด แทนที่จะทำให้ตัวสร้างโยนข้อยกเว้นฉันทำให้buildStudent()เมธอดโยนข้อยกเว้น
WVrock

25

คุณสามารถห่อหุ้มพารามิเตอร์ที่เกี่ยวข้องภายในออบเจ็กต์ได้หรือไม่?

เช่นถ้าพารามิเตอร์เป็นอย่างไร


MyClass(String house, String street, String town, String postcode, String country, int foo, double bar) {
  super(String house, String street, String town, String postcode, String country);
  this.foo = foo;
  this.bar = bar;

จากนั้นคุณสามารถมี:


MyClass(Address homeAddress, int foo, double bar) {
  super(homeAddress);
  this.foo = foo;
  this.bar = bar;
}


14

สิ่งที่คุณอาจต้องการทำคือมีคลาส Builder จากนั้นคุณจะทำสิ่งนี้:

MyObject obj = new MyObjectBuilder().setXxx(myXxx)
                                    .setYyy(myYyy)
                                    .setZzz(myZzz)
                                    // ... etc.
                                    .build();

ดูหน้า 8 และต่อไปนี้ของการนำเสนอ Josh Bloch (PDF) หรือบทวิจารณ์ของ Effective Java


8

การใช้รูปแบบตัวสร้างอาจเป็นทางออกหนึ่ง

แต่เมื่อคุณมาถึง 20 ถึง 30 พารามิเตอร์ฉันจะเดาว่ามีความสัมพันธ์ระหว่างพารามิเตอร์สูง ดังนั้น (ตามที่แนะนำ) การรวมไว้ในวัตถุข้อมูลที่มีเหตุผลน่าจะเหมาะสมที่สุด วิธีนี้วัตถุข้อมูลสามารถตรวจสอบความถูกต้องของข้อ จำกัด ระหว่างพารามิเตอร์ได้แล้ว

สำหรับโครงการทั้งหมดของฉันในอดีตเมื่อฉันมาถึงจุดที่มีพารามิเตอร์มากเกินไป (และนั่นคือ 8 ไม่ใช่ 28!) ฉันสามารถทำความสะอาดโค้ดได้โดยการสร้างโมเดลข้อมูลที่ดีขึ้น


4

เนื่องจากคุณถูก จำกัด ให้ใช้ Java 1.4 หากคุณต้องการ DI จากนั้นSpringก็เป็นตัวเลือกที่ดีมาก DI มีประโยชน์เฉพาะในตำแหน่งที่พารามิเตอร์ตัวสร้างเป็นเซอร์วิสหรือบางอย่างที่ไม่แตกต่างกันระหว่างรันไทม์

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


พารามิเตอร์ส่วนใหญ่เป็นบริการตามที่คุณกล่าวมาดังนั้น DI จึงเป็นสิ่งที่ฉันต้องการ ฉันคิดว่ารูปแบบ Builder ที่กล่าวถึงในคำตอบอื่น ๆ เป็นสิ่งที่ฉันหวังไว้
Steve Armstrong

4

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



1

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

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

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