מה זה סינגלטון?
סינגלטון הוא אחד מדפוסי העיצוב הפשוטים ביותר שניתן ליישם בכיתה. אנשים אומרים לפעמים "מחלקה זו היא יחידה", כלומר, מחלקה זו מיישמת את דפוס העיצוב של יחיד. לפעמים יש צורך לכתוב מחלקה שניתן ליצור עבורה רק אובייקט אחד. לדוגמה, מחלקה האחראית על רישום או התחברות למסד נתונים. דפוס העיצוב של Singleton מתאר כיצד אנו יכולים לבצע משימה כזו. סינגלטון הוא דפוס עיצובי שעושה שני דברים:-
מספק ערובה לכך שלכיתה יהיה רק מופע אחד של המחלקה.
-
מספק נקודת גישה גלובלית למופע של מחלקה זו.
-
בנאי פרטי. מגביל את היכולת ליצור אובייקטי מחלקה מחוץ למחלקה עצמה.
-
שיטה סטטית ציבורית המחזירה מופע של המחלקה. שיטה זו נקראת
getInstance
. זוהי נקודת הגישה הגלובלית למופע המחלקה.
אפשרויות יישום
תבנית עיצוב הסינגלטון משמשת בדרכים שונות. כל אפשרות טובה ורעות בדרכה. כאן, כמו תמיד: אין אידיאל, אבל צריך לשאוף אליו. אבל קודם כל, בואו נגדיר מה טוב ומה רע, ואילו מדדים משפיעים על הערכת היישום של דפוס עיצוב. נתחיל מהחיובי. להלן הקריטריונים שנותנים ליישום עסיסיות ואטרקטיביות:-
אתחול עצלן: כאשר מחלקה נטענת בזמן שהאפליקציה פועלת בדיוק כשצריך.
-
פשטות ושקיפות הקוד: המדד, כמובן, הוא סובייקטיבי, אבל חשוב.
-
בטיחות חוטים: פועל כהלכה בסביבה מרובת חוטים.
-
ביצועים גבוהים בסביבה מרובת חוטים: חוטים חוסמים זה את זה באופן מינימלי או בכלל לא בעת שיתוף משאב.
-
אתחול לא עצלן: כאשר מחלקה נטענת כאשר האפליקציה מתחילה, ללא קשר אם יש צורך בה או לא (פרדוקס, בעולם ה-IT עדיף להתעצל)
-
מורכבות וקריאות לקויה של הקוד. המדד הוא גם סובייקטיבי. נניח שאם מגיע דם מהעיניים, היישום הוא כך וכך.
-
חוסר בטיחות חוטים. במילים אחרות, "סכנת חוט". פעולה שגויה בסביבה מרובת חוטים.
-
ביצועים גרועים בסביבה מרובת חוטים: שרשורים חוסמים זה את זה כל הזמן או לעתים קרובות בעת שיתוף משאב.
קוד
כעת אנו מוכנים לשקול אפשרויות יישום שונות, תוך פירוט היתרונות והחסרונות:פתרון פשוט
public class Singleton {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return INSTANCE;
}
}
היישום הפשוט ביותר. יתרונות:
-
פשטות ושקיפות הקוד
-
בטיחות חוטים
-
ביצועים גבוהים בסביבה מרובת חוטים
- אתחול לא עצלן.
אתחול עצלן
public class Singleton {
private static Singleton INSTANCE;
private Singleton() {}
public static Singleton getInstance() {
if (INSTANCE == null) {
INSTANCE = new Singleton();
}
return INSTANCE;
}
}
יתרונות:
-
אתחול עצלן.
-
לא בטוח בשרשור
אביזר מסונכרן
public class Singleton {
private static Singleton INSTANCE;
private Singleton() {
}
public static synchronized Singleton getInstance() {
if (INSTANCE == null) {
INSTANCE = new Singleton();
}
return INSTANCE;
}
}
יתרונות:
-
אתחול עצלן.
-
בטיחות חוטים
-
ביצועים גרועים בסביבה מרובת חוטים
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;
}
}
יתרונות:
-
אתחול עצלן.
-
בטיחות חוטים
-
ביצועים גבוהים בסביבה מרובת חוטים
-
לא נתמך בגרסאות 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;
}
}
יתרונות:
-
אתחול עצלן.
-
בטיחות חוטים.
-
ביצועים גבוהים בסביבה מרובת חוטים.
-
לפעולה נכונה, יש צורך להבטיח שאובייקט המחלקה
Singleton
מאותחל ללא שגיאות. אחרת, קריאת השיטה הראשונהgetInstance
תסתיים בשגיאהExceptionInInitializerError
, וכל אלה שלאחר מכן ייכשלוNoClassDefFoundError
.
יישום | אתחול עצלן | בטיחות חוטים | מהירות ריבוי השחלות | מתי להשתמש? |
---|---|---|---|---|
פתרון פשוט | - | + | מָהִיר | לעולם לא. או כאשר אתחול עצלן אינו חשוב. אבל אף פעם לא טוב יותר. |
אתחול עצלן | + | - | לא ישים | תמיד כאשר אין צורך בריבוי השחלות |
אביזר מסונכרן | + | + | לאט | לעולם לא. או כאשר מהירות העבודה עם ריבוי השחלות לא משנה. אבל אף פעם לא טוב יותר |
נעילה בבדיקה כפולה | + | + | מָהִיר | במקרים נדירים כאשר אתה צריך לטפל בחריגים בעת יצירת יחיד. (כאשר בעל המעמד Singleton אינו רלוונטי) |
מחזיק כיתה סינגלטון | + | + | מָהִיר | תמיד כאשר יש צורך ב-multithreading ויש ערובה לכך שאובייקט מחלקה singleton ייווצר ללא בעיות. |
יתרונות וחסרונות של דפוס הסינגלטון
באופן כללי, הסינגלטון עושה בדיוק את מה שמצפים ממנו:-
מספק ערובה לכך שלכיתה יהיה רק מופע אחד של המחלקה.
-
מספק נקודת גישה גלובלית למופע של מחלקה זו.
-
Singleton מפר את ה-SRP (Single Responsibility Principle) - מחלקת Singleton, בנוסף לאחריותה המיידית, שולטת גם במספר העותקים שלה.
-
התלות של מחלקה או שיטה רגילה בסינגלטון אינה גלויה בחוזה הציבורי של המחלקה.
-
משתנים גלובליים גרועים. הסינגלטון הופך בסופו של דבר למשתנה גלובלי כבד אחד.
-
הנוכחות של סינגלטון מפחיתה את יכולת הבדיקה של האפליקציה בכלל והמחלקות המשתמשות בסינגלטון בפרט.
GO TO FULL VERSION