Training

Builder Pattern ใน Java

20 Dec 2015

วันนี้เรามารู้จักกับ Builder Pattern กัน โดยเราจะใช้ Java เป็นตัวอย่างประกอบ เจ้า Design Pattern นี้ใช้สำหรับประยุกต์กับการสร้างออบเจกต์ Model หรือ Entity โดยเฉพาะครับ ว่าแล้วไปดูกันเลย

บ่อยครั้งที่ออบเจกต์ในบทบาท Model หรือ Entity มักประกอบไปด้วยแอททริบิวท์มากมาย อาทิเช่น ออบเจกต์ Customer ประกอบไปด้วยแอททริบิวท์ id, firstname, lastname และอื่นๆอีกมากมาย เป็นต้น และในการออกแบบคลาสของเรา เราก็ใส่คอนเซ็ปต์ Information Hiding และ Encapsulation เข้าไปอีก ทำให้โค้ดที่เรียกใช้นั้นเยินเย้อ ว่าแล้วลองมาดูตัวอย่างกัน ตัวย่างที่ 1 (Customer.java แบบ Conventional) แสดงโค้ดตัวอย่าง

ตัวอย่างที่ 1: Customer.java แบบ Conventional
package conventional;

public class Customer {

  private int id;
  private String firstname;
  private String lastname;
  private String address;
  private String email;
  private String phone;

  public Customer(){

  }

  public Customer(int id,String firstname,String lastname,String address,String email,String phone) {
    this.id = id;
    this.firstname = firstname;
    this.lastname = lastname;
    this.address = address;
    this.email = email;
    this.phone = phone;
  }

  public int getId() {
    return id;
  }

  public void setId(int id) {
    this.id = id;
  }

  public String getFirstname() {
    return firstname;
  }

  public void setFirstname(String firstname) {
    this.firstname = firstname;
  }

  public String getLastname() {
    return lastname;
  }

  public void setLastname(String lastname) {
    this.lastname = lastname;
  }

  public String getAddress() {
    return address;
  }

  public void setAddress(String address) {
    this.address = address;
  }

  public String getEmail() {
    return email;
  }

  public void setEmail(String email) {
    this.email = email;
  }

  public String getPhone() {
    return phone;
  }

  public void setPhone(String phone) {
    this.phone = phone;
  }

} // end of class

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

    Customer cust = new Customer();
    cust.setId(1);
    cust.setFirstname("James");
    cust.setLastname("Pat");
    cust.setAddress("Rama 3 Bangkok");
    cust.setEmail("james@gmail.com");
    cust.setPhone("081-123-4567");

    // or

    Customer cust2 = new Customer(1,"James","Pat","Rama 3 Bangkok","james@gmail.com","081-123-4567");

มันจะดีกว่ามั้ยถ้าเราสามารถโหลดออบเจกต์ Customer ในรูปแบบตามตัวอย่างข้างใต้นี้

    Customer cust = new Customer.Builder()
                        .id(1)
                        .firstname("James")
                        .lastname("Pat")
                        .address("Rama 3 Bangkok")
                        .email("james@gmail.com")
                        .phone("081-123-4567")
                        .build();

ซึ่งรูปแบบการเขียนลักษณะนี้หรือออกแบบคลาสแบบนี้ เราเรียกว่า Builder Pattern ครับ จะเห็นได้ว่ากระชับขึ้น แล้วเราจะออกแบบคลาสของเราอย่างไรหล่ะให้สามารถถูกเรียกใช้แบบนี้ได้ ไปดูซอร์สโค้ดของคลาส Customer ใหม่นี้กัน ตัวอย่างที่ 2 (Customer.java แบบ Builder) แสดงโค้ดตัวอย่าง

ตัวอย่างที่ 2: Customer.java แบบ Builder
package builder;

public class Customer {

  private int id;
  private String firstname;
  private String lastname;
  private String address;
  private String email;
  private String phone;

  public Customer() {
  }

  private Customer(Customer.Builder builder){
    id = builder.id;
    firstname = builder.firstname;
    lastname = builder.lastname;
    address = builder.address;
    email = builder.email;
    phone = builder.phone;
  } // end of Customer(builder)

  public static class Builder{
    private int id;
    private String firstname;
    private String lastname;
    private String address;
    private String email;
    private String phone;

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

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

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

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

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

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

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

  } // end of Builder class

  public int getId() {
    return id;
  }

  public void setId(int id) {
    this.id = id;
  }

  public String getFirstname() {
    return firstname;
  }

  public void setFirstname(String firstname) {
    this.firstname = firstname;
  }

  public String getLastname() {
    return lastname;
  }

  public void setLastname(String lastname) {
    this.lastname = lastname;
  }

  public String getAddress() {
    return address;
  }

  public void setAddress(String address) {
    this.address = address;
  }

  public String getEmail() {
    return email;
  }

  public void setEmail(String email) {
    this.email = email;
  }

  public String getPhone() {
    return phone;
  }

  public void setPhone(String phone) {
    this.phone = phone;
  }

  public String toString(){
    return id + ", " + firstname + " " + lastname + ", " + address +
            ", " + email + ", " + phone;   
  }

} // end of class

(OH NO!!! ทำไมมันยาวกว่าเดิมครับ? ตอนเรียกใช้หน่ะมันง่ายขึ้น แต่ลำบากตอนประกาศคลาส คิดซะว่าลำบากทีเดียวครับ) มาดูจุดที่น่าสนใจของตัวอย่างข้างต้นกัน โปรดสังเกตบรรทัดที่ 24 ว่า มี static อินเนอร์คลาส Builder อยู่

  public static class Builder{
    . . .
  }

ตรงนี้แหละที่ทำให้เราเรียกคำสั่ง new Customer.Builder() ได้ สรุปคือเรา new คลาส Builder นะ ไม่ใช่ Customer และเจ้าคลาส Builder นี้ก็ประกอบไปด้วยแอททริบิวท์เฉกเช่นเดียวกับคลาส Customer เลย นอกจากนี้คลาส Builder ยังประกอบไปด้วยเมธอดชื่อเดียวกับแอททริบิวท์อีกด้วย เมธอดเหล่านี้แหละที่ทำหน้าที่แทนเมธอด setter แบบเก่า แต่มันใช้เทคนิคการรีเทิร์นตัวมันเอง ทำให้เราสามารถเรียกใช้แบบ Method Chanining ได้ เช่น .id().firstname().lastname() ต่อกันไปได้เรื่อยๆ เป็นต้น แล้วสุดท้ายเราก็เรียกเมธอด build() เพื่อเป็นการรีเทิร์นออบเจกต์ชนิด Customer ที่ต้องการออกมา (เรา new Customer(this) แล้วส่ง builder เข้าไป ดังนั้นจึงต้องเตรียม Constructor รองรับรูปแบบนี้ด้วย) ไม่ยากใช่ไหมครับ นี่แหละสาเหตุที่เขาตั้งชื่อว่า Builder Pattern คือรูปแบบการเขียนหรือออกแบบโปรแกรมให้มัน Build ออบเจกต์ออกมาในแบบที่กระชับ ซึ่งโดยมากเราก็ประยุกต์ใช้กับออบเจกต์ Model ตัวอย่างที่ยกมานี้เป็นตัวอย่างแบบง่ายๆ ในการทำงานจริงคุณผู้อ่านก็ต้องไปประยุกต์ตามสถานการณ์และความเหมาะสมด้วย Pattern มันไม่ได้ตายตัว ขอเพียงคอนเซ็ปท์อย่าเพี้ยนก็ใช้ได้ :)


Books By Me

JavaScript Programming Guide book cover

JavaScript Programming Guide
Thai language
Kontentblue Publishing

About This Site

I use this site to share my knowledge in form of articles. I also use it as an experimental space, trying and testing things. If you have any problems viewing this site, please tell me.

→ More about me

→ Contact me

→ Me on facebook

Creative Commons Attribution License

creative commons logo

This license lets you distribute, remix, tweak my articles, even commercially, as long as you credit me for the original creation.

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