อ่าน Effective Java ตอนที่ 3 (Singleton)

ผ่านมาแล้วสองตอน (ตอนที่แล้ว Effective Java ตอนที่ 2 (Builder)) มาตอนนี้ขอมาพูดเรื่อง Singleton ครับ หรือ class ที่มีการสร้างแค่ครั้งเดียวเท่านั้น!

โดยปกติแล้ว Singleton ทำใช้เป็นส่วนหนึ่งรองระบบที่มีอันเดียวชิ้นเดียว เช่น Window Manager หรือ File System

การสร้าง class Singleton นั้นสามารถทำให้มันยากต่อการ test ได้ เพราะว่ามันไม่สามารถสร้าง mock object สำหรับ singleton ได้ นอกจากว่าเราจะสร้าง interface ที่มีการทำงานเหมือนกันขึ้นมา

โดยก่อน Java release 1.5 นั้นแล้วมีสองทางที่เราจะทำ singleton ได้ ทั้งสองอย่างคือ ทำให้ constructor เป็น private แล้วใช้ public static ในการสร้างและเข้าถึง instance ซึ่งแบบแรก public static ของเรานั้นจะเป็น field ที่เป็น final ดังตัวอย่าง

// Singleton with public final field 
public class Elvis { 
  public static final Elvis INSTANCE = new Elvis(); 
  private Elvis() { ... } 
  public void leaveTheBuilding() { ... } 
}

ซึ่งแบบนี้ private constructor จะถูกเรียกแค่ครั้งเดียวเท่านั้น เพราะว่าการที่เป็น private constructor ทำให้ไม่มีใครเรียกได้ ยกเว้นตัวมันเอง โดย Elvis เนี่ย จะถูกสร้างครั้งแรกครั้งเดียวเท่านั้น ไม่มาก ไม่น้อยไปกว่านั้น ไม่มีอะไรที่จะมีทางสร้างได้ ยกเว้นจะไปซนใช้ reflection เปลี่ยน private เป็น public หรืออย่างอื่น ด้วยการใช้  AccessibleObject.setAccessible() และถ้าเราจะป้องกันการทำแบบนี้ เราต้องเช็คด้วยว่ามีการสร้างมากกว่าหนึ่งไหม ถ้าเกินอย่าลืม throw exception ด้วย

แบบที่สองนั้น คือการใช้ getInstance() โดย code จะคล้ายกันกับแบบแรก แต่ต่างกันนิดหน่อย

// Singleton with static factory
public class Elvis { 
  private static final Elvis INSTANCE = new Elvis(); 
  private Elvis() { ... } 
  public static Elvis getInstance() { return INSTANCE; } 
  public void leaveTheBuilding() { ... } 
}

โดยการใช้วิธีนี้ เราก็สามารถเข้าถึง object นี้ได้โดยการใช้ Elvis.getInstance() และไม่มีทางที่จะสร้าง object ได้หลายตัว (ยกเว้นใช้วิธีเดียวกันกับก่อนหน้านี้)

ข้อดีข้องการใช้ public field คือ มันง่าย และเข้าใจได้ทันทีว่า class เป็น singleton ด้วย public static final field (เพราะว่ามันเป็น final เนี่ยแหละ) แถมมันยังมีประสิทธิภาพมากอีกด้วย

ข้อดีของการใช้แบบ factory-method คือ มันสามารถปรับเปลี่ยนได้ง่าย ถ้าเกิดว่าเราเปลี่ยนใจ ไม่ใช้เป็น singleton แล้วหล่ะก็ เปลี่ยนได้ง่ายๆ ไม่จำเป็นต้องเปลี่ยน API เลย แค่เปลี่ยนจากการ return INSTANCE เป็นการ return ตัวใหม่เข้าไปแทน ข้อดีอย่างที่สองคือเรื่อง generic type (ซึ่งจะถูกพูดในตอนหลัง)

ทั้งสองแบบมีข้อดีทั้งคู่ แล้วเป็น final-field เหมือนกันแถมง่ายอีกต่างหาก

แต่หลังจาก release 1.5 เป็นต้นมาก มีแบบที่สาม โผล่มาอีก นั่นคือการใช้ enum นั่นเอง

// Enum singleton - the preferred approach 
public enum Elvis { 
  INSTANCE; 

  public void leaveTheBuilding() { ... } 
}

ด้วยวิธีนี้ทำให้ มันมีประสิทธิภาพดีเท่ากับแบบ public field ดีกว่านั้นคือเขียนง่ายกว่า แถมยังเป็นมีคุณสมบัติ Serialization ด้วย นอกจากนั้นมันยังแข็งแกร่งมากเช่นกัน ไม่สามารถทำ reflection ได้ แต่การทำแบบนี้ยังไม่ค่อยแพร่หลายนัก แต่มันเป็นการสร้าง singleton ที่ดีที่สุด (เขาบอกมา)

ข้อควรระวัง การสร้าง Singleton แบบ enum นั้น ใน android อาจจะมีการกินทรัพยากรมากกว่าปกติ ระวังด้วยครับ แต่ก็ไม่ได้เยอะขนาดนั้นนะ ไม่ใช่ว่าใช้ไม่ได้ ตัวผมเองยังใช้อยู่บ้าง (ก็มันเขียนง่าย) ดูได้จาก
https://www.youtube.com/watch?v=Hzs6OBcvNQE
https://plus.google.com/+JakeWharton/posts/bTtjuFia5wm

Comments