ซิงเกิลตันคืออะไร?
ซิงเกิลตันเป็นหนึ่งในรูปแบบการออกแบบที่ง่ายที่สุดที่สามารถนำไปใช้กับคลาสได้ บางครั้งผู้คนพูดว่า "คลาสนี้เป็นซิงเกิลตัน" หมายความว่าคลาสนี้ใช้รูปแบบการออกแบบซิงเกิลตัน บางครั้งจำเป็นต้องเขียนคลาสที่สามารถสร้างได้เพียงวัตถุเดียวเท่านั้น ตัวอย่างเช่น คลาสที่รับผิดชอบในการบันทึกหรือเชื่อมต่อกับฐานข้อมูล รูปแบบการออกแบบซิงเกิลตันอธิบายว่าเราสามารถทำงานดังกล่าวให้สำเร็จได้อย่างไร ซิงเกิลตันคือรูปแบบการออกแบบที่ทำสองสิ่ง:-
ให้การรับประกันว่าคลาสจะมีเพียงอินสแตนซ์เดียวของคลาส
-
จัดเตรียมจุดเข้าใช้งานส่วนกลางให้กับอินสแตนซ์ของคลาสนี้
-
คอนสตรัคเตอร์ส่วนตัว จำกัดความสามารถในการสร้างอ็อบเจ็กต์คลาสภายนอกคลาสเอง
-
วิธีการคงสาธารณะที่ส่งกลับอินสแตนซ์ของชั้นเรียน วิธีการนี้เรียก
getInstance
ว่า นี่คือจุดเข้าใช้งานส่วนกลางไปยังอินสแตนซ์ของคลาส
ตัวเลือกการดำเนินงาน
รูปแบบการออกแบบซิงเกิลตันถูกนำมาใช้ในรูปแบบต่างๆ แต่ละตัวเลือกมีดีและไม่ดีในแบบของตัวเอง ที่นี่เช่นเคย: ไม่มีอุดมคติ แต่คุณต้องมุ่งมั่นเพื่อมัน แต่ก่อนอื่น เรามากำหนดว่าอะไรดีและอะไรไม่ดี และตัวชี้วัดใดที่มีอิทธิพลต่อการประเมินการนำรูปแบบการออกแบบไปใช้ เริ่มจากแง่บวกกันก่อน ต่อไปนี้เป็นเกณฑ์ที่ทำให้การดำเนินการมีความน่าสนใจและน่าดึงดูดใจ:-
การเริ่มต้นแบบ Lazy: เมื่อมีการโหลดคลาสในขณะที่แอปพลิเคชันกำลังทำงานอยู่เมื่อจำเป็น
-
ความเรียบง่ายและโปร่งใสของโค้ด: แน่นอนว่าการวัดผลเป็นเรื่องส่วนตัว แต่มีความสำคัญ
-
ความปลอดภัยของเธรด: ทำงานอย่างถูกต้องในสภาพแวดล้อมแบบมัลติเธรด
-
ประสิทธิภาพสูงในสภาพแวดล้อมแบบมัลติเธรด: เธรดจะบล็อกซึ่งกันและกันน้อยที่สุดหรือไม่บล็อกเลยเมื่อแชร์ทรัพยากร
-
การเริ่มต้นแบบไม่ขี้เกียจ: เมื่อมีการโหลดคลาสเมื่อแอปพลิเคชันเริ่มทำงาน โดยไม่คำนึงว่าจำเป็นหรือไม่ (ความขัดแย้ง ในโลกไอที ขี้เกียจจะดีกว่า)
-
ความซับซ้อนและการอ่านโค้ดไม่ดี การวัดยังเป็นแบบอัตนัย เราจะถือว่าถ้าเลือดไหลออกจากดวงตา การนำไปปฏิบัติก็เป็นเช่นนั้น
-
ขาดความปลอดภัยของด้าย กล่าวอีกนัยหนึ่งคือ "อันตรายจากด้าย" การดำเนินการไม่ถูกต้องในสภาพแวดล้อมแบบมัลติเธรด
-
ประสิทธิภาพต่ำในสภาพแวดล้อมแบบมัลติเธรด: เธรดบล็อกซึ่งกันและกันตลอดเวลาหรือบ่อยครั้งเมื่อมีการแบ่งปันทรัพยากร
รหัส
ตอนนี้เราพร้อมที่จะพิจารณาตัวเลือกการใช้งานต่างๆ โดยระบุข้อดีและข้อเสีย:วิธีแก้ปัญหาง่ายๆ
public class Singleton {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return INSTANCE;
}
}
การใช้งานที่ง่ายที่สุด ข้อดี:
-
ความเรียบง่ายและความโปร่งใสของรหัส
-
ความปลอดภัยของด้าย
-
ประสิทธิภาพสูงในสภาพแวดล้อมแบบมัลติเธรด
- ไม่ใช่การเริ่มต้นแบบขี้เกียจ
การเริ่มต้นแบบ Lazy
public class Singleton {
private static Singleton INSTANCE;
private Singleton() {}
public static Singleton getInstance() {
if (INSTANCE == null) {
INSTANCE = new Singleton();
}
return INSTANCE;
}
}
ข้อดี:
-
การเริ่มต้นแบบ Lazy
-
ไม่ปลอดภัยต่อเธรด
ตัวเข้าถึงแบบซิงโครไนซ์
public class Singleton {
private static Singleton INSTANCE;
private Singleton() {
}
public static synchronized Singleton getInstance() {
if (INSTANCE == null) {
INSTANCE = new Singleton();
}
return INSTANCE;
}
}
ข้อดี:
-
การเริ่มต้นแบบ Lazy
-
ความปลอดภัยของด้าย
-
ประสิทธิภาพต่ำในสภาพแวดล้อมแบบมัลติเธรด
getInstance
การซิงโครไนซ์แล้ว และคุณสามารถป้อนได้ทีละรายการเท่านั้น ที่จริงแล้ว เราไม่จำเป็นต้องซิงโครไนซ์เมธอดทั้งหมด แต่เพียงส่วนหนึ่งของเมธอดที่เราเริ่มต้นคลาสอ็อบเจ็กต์ใหม่เท่านั้น แต่เราไม่สามารถรวมsynchronized
ส่วนที่รับผิดชอบในการสร้างอ็อบเจ็กต์ใหม่ไว้ในบล็อกได้: สิ่งนี้ไม่ได้ให้ความปลอดภัยของเธรด มันซับซ้อนกว่าเล็กน้อย วิธีการซิงโครไนซ์ที่ถูกต้องมีดังต่อไปนี้:
ตรวจสอบการล็อคสองครั้ง
public class Singleton {
private static Singleton INSTANCE;
private Singleton() {
}
public static Singleton getInstance() {
if (INSTANCE == null) {
synchronized (Singleton.class) {
if (INSTANCE == null) {
INSTANCE = new Singleton();
}
}
}
return INSTANCE;
}
}
ข้อดี:
-
การเริ่มต้นแบบ Lazy
-
ความปลอดภัยของด้าย
-
ประสิทธิภาพสูงในสภาพแวดล้อมแบบมัลติเธรด
-
ไม่รองรับ Java เวอร์ชันต่ำกว่า 1.5 (คำหลักระเหยได้รับการแก้ไขในเวอร์ชัน 1.5)
INSTANCE
ต้องเป็นอย่างใดอย่างหนึ่งfinal
หรือ volatile
การใช้งานครั้งสุดท้ายที่เราจะพูดถึงในวันนี้Class Holder Singleton
คือ
คลาสโฮลเดอร์ ซิงเกิลตัน
public class Singleton {
private Singleton() {
}
private static class SingletonHolder {
public static final Singleton HOLDER_INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.HOLDER_INSTANCE;
}
}
ข้อดี:
-
การเริ่มต้นแบบ Lazy
-
ความปลอดภัยของด้าย
-
ประสิทธิภาพสูงในสภาพแวดล้อมแบบมัลติเธรด
-
เพื่อการดำเนินการที่ถูกต้อง จำเป็นต้องรับประกันว่าคลาสอ็อบเจ็กต์
Singleton
จะเริ่มต้นได้โดยไม่มีข้อผิดพลาด มิฉะนั้น การเรียกเมธอดแรกgetInstance
จะสิ้นสุดด้วยข้อผิดพลาดExceptionInInitializerError
และการเรียกเมธอดครั้งต่อๆ ไปทั้งหมดจะล้มNoClassDefFoundError
เหลว
การนำไปปฏิบัติ | การเริ่มต้นแบบ Lazy | ความปลอดภัยของด้าย | ความเร็วมัลติเธรด | ควรใช้เมื่อใด? |
---|---|---|---|---|
วิธีแก้ปัญหาง่ายๆ | - - | + | เร็ว | ไม่เคย. หรือเมื่อการเริ่มต้นแบบขี้เกียจไม่สำคัญ แต่ไม่เคยดีกว่า |
การเริ่มต้นแบบ Lazy | + | - - | ไม่สามารถใช้ได้ | เสมอเมื่อไม่จำเป็นต้องใช้มัลติเธรด |
ตัวเข้าถึงแบบซิงโครไนซ์ | + | + | ช้า | ไม่เคย. หรือเมื่อความเร็วในการทำงานกับมัลติเธรดไม่สำคัญ แต่ไม่เคยดีกว่า |
ตรวจสอบการล็อคสองครั้ง | + | + | เร็ว | ในบางกรณีที่เกิดขึ้นไม่บ่อยนักเมื่อคุณจำเป็นต้องจัดการกับข้อยกเว้นเมื่อสร้างซิงเกิลตัน (เมื่อไม่สามารถใช้ Class Holder Singleton ได้) |
คลาสโฮลเดอร์ ซิงเกิลตัน | + | + | เร็ว | ทุกครั้งเมื่อจำเป็นต้องใช้มัลติเธรดและมีการรับประกันว่าออบเจ็กต์คลาสซิงเกิลจะถูกสร้างขึ้นโดยไม่มีปัญหา |
ข้อดีและข้อเสียของรูปแบบซิงเกิลตัน
โดยทั่วไปแล้ว singleton จะทำสิ่งที่คาดหวังไว้อย่างแน่นอน:-
ให้การรับประกันว่าคลาสจะมีเพียงอินสแตนซ์เดียวของคลาส
-
จัดเตรียมจุดเข้าใช้งานส่วนกลางให้กับอินสแตนซ์ของคลาสนี้
-
Singleton ละเมิด SRP (Single Responsibility Principle) - คลาส Singleton นอกเหนือจากความรับผิดชอบในทันทีแล้ว ยังควบคุมจำนวนสำเนาด้วย
-
การพึ่งพาของคลาสปกติหรือเมธอดในซิงเกิลตันไม่สามารถมองเห็นได้ในสัญญาสาธารณะของคลาส
-
ตัวแปรทั่วโลกไม่ดี ในที่สุดซิงเกิลตันก็กลายเป็นตัวแปรสำคัญระดับโลกตัวหนึ่ง
-
การมีซิงเกิลตันจะช่วยลดความสามารถในการทดสอบของแอปพลิเคชันโดยทั่วไปและคลาสที่ใช้ซิงเกิลตันโดยเฉพาะ
GO TO FULL VERSION