รูปแบบตัวสร้างแบบเพิ่มทีละขั้น (Incremental Builder Pattern)
ทั้งตัวสร้างแบบคงที่ (Static Factory) และตัวสร้าง (Constructor) ล้วนมีข้อจำกัดในการจัดการพารามิเตอร์จำนวนมากได้อย่างเหมาะสม หากสมมติว่าคลาสมีฟิลด์ 6 ฟิลด์ และต้องการสร้างตัวสร้างแยกกันสำหรับกรณีที่พารามิเตอร์มี 2 ตัว 3 ตัว ฯลฯ สามารถใช้รูปแบบตัวสร้างแบบเพิ่มทีละขั้นได้ดังตัวอย่างต่อไปนี้
แต่ถึงอย่างนั้น หากพารามิเตอร์มีจำนวนมากขึ้น ก็อาจทำให้เกิดความสับสนในการอ่านโค้ดว่าแต่ละค่ามีความหมายอย่างไร และอาจทำให้เกิดความผิดพลาดจากการใส่ค่าพารามิเตอร์ที่ชนิดข้อมูลเหมือนกันผิดที่ผิดทางได้
รูปแบบ Java Beans
รูปแบบ Java Beans สร้างออบเจ็กต์โดยใช้ตัวสร้างที่ไม่มีพารามิเตอร์ จากนั้นจึงเรียกใช้เมธอด setter เพื่อกำหนดค่าพารามิเตอร์ที่ต้องการ
รูปแบบ Java Beans ช่วยให้ไม่สับสนในการสร้างอินสแตนซ์แม้ว่าพารามิเตอร์จะมีจำนวนมากขึ้น แต่ก็ต้องเรียกใช้เมธอด setter หลายครั้งเพื่อสร้างออบเจ็กต์เพียงหนึ่งเดียว และความสอดคล้องกันของออบเจ็กต์อาจเสียไปก่อนที่มันจะเสร็จสมบูรณ์ ทำให้ไม่สามารถสร้างคลาสให้เป็นแบบคงที่ (Immutable) ได้
รูปแบบตัวสร้าง (Builder Pattern)
รูปแบบตัวสร้าง (Builder Pattern) เป็นรูปแบบที่ผสานความปลอดภัยของรูปแบบตัวสร้างแบบเพิ่มทีละขั้น (Incremental Builder Pattern) และความสามารถในการอ่านโค้ดของรูปแบบ Java Beans เข้าด้วยกัน
ไคลเอ็นต์จะไม่สร้างออบเจ็กต์เอง แต่จะเรียกใช้ตัวสร้างด้วยพารามิเตอร์ที่จำเป็นเพื่อรับออบเจ็กต์ตัวสร้าง (Builder Object) จากนั้นจึงใช้เมธอด setter ที่ออบเจ็กต์ตัวสร้างจัดเตรียมไว้เพื่อกำหนดค่าพารามิเตอร์ที่เลือกได้ และสุดท้ายก็เรียกใช้เมธอด build() ที่ไม่มีพารามิเตอร์เพื่อรับออบเจ็กต์ที่ต้องการ
คลาส Builder มีตัวสร้างที่รับพารามิเตอร์ที่จำเป็นเท่านั้น และพารามิเตอร์ที่เลือกได้อื่นๆ จะถูกเติมเต็มโดยเมธอด setter คล้ายๆ กับเมธอด setter และสุดท้ายก็สร้างออบเจ็กต์ NutritionFactsWithBuilderPattern ที่สมบูรณ์แบบโดยใช้เมธอด build() คลาส NutritionFactsWithBuilderPattern เป็นแบบคงที่ (Immutable) และเมธอด setter ของตัวสร้างจะส่งคืนตัวสร้างเอง ทำให้สามารถเรียกใช้แบบต่อเนื่องได้ รูปแบบนี้เรียกว่า Fluent API หรือ Method Chaining
ไคลเอ็นต์สามารถเขียนและอ่านโค้ดได้ง่ายขึ้นด้วยรูปแบบตัวสร้าง (Builder Pattern)
รูปแบบตัวสร้าง (Builder Pattern) เหมาะกับคลาสที่ออกแบบเป็นลำดับชั้น
คลาส Pizza.Builder เป็นคลาสแบบเจเนริกที่มีการใช้ Type Parameter แบบเรียกซ้ำ (Recursive Type Bound) และมีเมธอดนามธรรม self() เพิ่มเข้ามาเพื่อให้คลาสย่อยสามารถสนับสนุนการเรียกใช้แบบต่อเนื่อง (Method Chaining) ได้โดยไม่ต้องแปลงชนิดข้อมูล คลาสย่อยเพียงแค่ส่งคืนค่าของเมธอดนามธรรมนี้เป็นตัวมันเองก็เพียงพอแล้ว
ลองมาดูตัวอย่าง Pizza แบบนิวยอร์กและแบบ Calzone ซึ่งเป็นคลาสย่อยของ Pizza เพื่อดูความยืดหยุ่นของรูปแบบตัวสร้าง (Builder Pattern)
เมธอด build() ที่กำหนดไว้ในตัวสร้างของแต่ละคลาสย่อยจะส่งคืนคลาสย่อยที่เป็นรูปธรรม การที่เมธอดในคลาสย่อยส่งคืนค่าที่เป็นชนิดย่อยแทนที่จะเป็นชนิดของคลาสหลักที่เมธอดของคลาสหลักส่งคืน เรียกว่า Covariant Return Typing คุณสมบัติตัวนี้ทำให้ไคลเอ็นต์ไม่จำเป็นต้องแปลงชนิดข้อมูล
ไคลเอ็นต์สามารถใช้ enum ของ Pizza และ enum ของแต่ละคลาสย่อยร่วมกันได้ และสร้างออบเจ็กต์ให้สมบูรณ์แบบด้วยเมธอดที่เหมาะสม
ข้อเสียของรูปแบบตัวสร้าง (Builder Pattern)
- ต้องสร้างออบเจ็กต์ตัวสร้าง (Builder Object)
- โค้ดอาจยาว
สรุป
หากตัวสร้าง (Constructor) หรือเมธอดตัวสร้างแบบคงที่ (Static Factory Method) ต้องจัดการกับพารามิเตอร์จำนวนมาก ควรพิจารณาใช้รูปแบบตัวสร้าง (Builder Pattern)
แหล่งที่มา
- Effective Java
ความคิดเห็น0