סינגלטון (תבנית עיצוב)

תבנית סינגלטון (מאנגלית: Singleton, בעברית: יחידן לפי האקדמיה ללשון העברית) היא תבנית עיצוב בהנדסת תוכנה, אשר נועדה למקרים בהם מעוניינים להגביל את יצירת המופעים של מחלקה מסוימת למופע יחיד, או לעיתים גם למקרים בהם רוצים להגביל את מספר המופעים לכמות קבועה כלשהי. תבנית זו שימושית כאשר נדרש רק מופע אחד על מנת לתאם פעולות במערכת. מתכנתים מסוימים מגדירים את התבנית הזו כתבנית anti-pattern, כלומר, תבנית שנראית מאוד פשוטה וברורה, אבל בפועל היא לא אופטימלית, או שימושית. הגדרה זו נובעת מכך שתבנית זו היא שכיחה מאוד בקרב מפתחים, ולעיתים רבות יוצרת הגבלות שלא לצורך, במצבים בהם אין צורך ממשי במופע יחיד.

מימוש

מימוש התבנית חייב לעמוד בשני עקרונות: מופע יחיד של המחלקה, וגישה גלובלית. נדרשת האפשרות לגשת לאיבר במחלקה סינגלטון מבלי ליצור אובייקט של המחלקה, וכן מנגנון אשר ישמור את ערכי האיברים של המופע שנוצר. התבנית מושגת באמצעות מחלקה הכוללת פונקציה, אשר בקריאה הראשונה יוצרת מופע חדש, ובקריאות הבאות, מחזירה מצביע לאותו מופע שיצרה קודם. כדי להבטיח שאכן יהיה מופע בודד של המחלקה ולא ניתן יהיה ליצור אותה בצורה אחרת, הגדרת הגישה לפונקציית הבנאי (Constructor) מוגדרת כ-private או protected.

ביישומים מרובי תהליכונים (multi-threaded applications) יש להיזהר במימוש התבנית. אם שני תהליכונים ניגשים לפונקציית הבנייה באותו הזמן, ייתכן מצב בו שניהם ייצרו מופע – מצב אותו אנו מנסים למנוע.

דוגמאות מימוש

Java 
דרך בטוחה מבחינת תהליכונים (Threads) ב-Java הוצעה על ידי וילאם פו. היא נקראת גם "initialization on demand holder idiom" וממומשת בגישה של "אתחול עצל" (Lazy initialization). בגישה זו, יצירת המופע נדחית עד לרגע האחרון.

public class Singleton { 
  // Private constructor suppresses generation of a (public) default constructor
  private Singleton() {}

  /**
   * SingletonHolder is loaded on the first execution of Singleton.getInstance() 
   * or the first access to SingletonHolder.INSTANCE, not before.
   */
  private static class SingletonHolder { 
    private final static Singleton INSTANCE = new Singleton();
  }
  
  public static Singleton getInstance() {
    return SingletonHolder.INSTANCE;
  }
}

דרך המימוש המסורתית האלגנטית והפשוטה מתחשבת בריבוי תהליכים, אך לא מממשת אתחול עצל:

public class Singleton {
  private final static Singleton INSTANCE = new Singleton();
 
  // Private constructor suppresses generation of a (public) default constructor
  private Singleton() {}
 
  public static Singleton getInstance() {
    return INSTANCE;
  }
}

#C 
/// <summary>
/// Thread-safe singleton example without using locks
/// </summary>
public sealed class Singleton
{
    private static readonly Singleton instance = new Singleton();
 
    // Explicit static constructor to tell C# compiler
    // not to mark type as beforefieldinit
    static Singleton()
    {
    }
 
    Singleton()
    {
    }
 
    /// <summary>
    /// The public Instance property to use
    /// </summary>
    public static Singleton Instance
    {
        get { return instance; }
    }
}

++C 
class Singleton
{
    private:
        /* Here will be the instance stored. */
        static Singleton* instance;

        /* Private constructor to prevent instancing. */
        Singleton();

    public:
        /* Static access method. */
        static Singleton* getInstance();
};

/* Null, because instance will be initialized on demand. */
Singleton* Singleton::instance = 0;

Singleton* Singleton::getInstance()
{
    if (instance == 0)
    {
        instance = new Singleton();
    }

    return instance;
}

Singleton::Singleton()
{}

PHP 5 
תבנית סינגלטון ב-PHP, גרסה 5:

<?php
class Singleton {
  // object instance
  private static $instance;
  
  private function __construct() {}
  
  private function __clone() {}
  
  public static function getInstance() {
    if (self::$instance === null) {
      self::$instance = new self;
    }
    return self::$instance;
  }

  public function doAction() {
    ...
  }
}

//usage
Singleton::getInstance()->doAction();

?>

Objective-C 
דרך פשוטה לממש סינגלטון ב-Objective-C:

@interface MySingleton : NSObject
{
}

+ (MySingleton *)sharedSingleton;
@end

@implementation MySingleton

+ (MySingleton *)sharedSingleton
{
  static MySingleton *sharedSingleton;
  
  @synchronized(self)
  {
    if (!sharedSingleton)
      sharedSingleton = [[MySingleton alloc] init];
    
    return sharedSingleton;
  }
}

@end

אם אין חובה להשתמש בתהליכונים, התיאום (סינכרון) יכול להישאר מחוץ לקוד, ואז הפונקציה +sharedSingleton תיראה כך:

+ (MySingleton *)sharedSingleton
{
  static MySingleton *sharedSingleton;

  if (!sharedSingleton)
    sharedSingleton = [[MySingleton alloc] init];

  return sharedSingleton;
}

קישורים חיצוניים

 דב זילברמן, Design Patterns, הסדרה "תבניות". פרק 3: הסינגלטון.

Singleton
יחידה