תבנית Singleton

מתוך ויקיפדיה, האנציקלופדיה החופשית
קפיצה אל: ניווט, חיפוש

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

מימוש[עריכת קוד מקור | עריכה]

תרשים UML של מחלקה המממשת את תבנית סינגלטון

מימוש התבנית חייב לעמוד בשני עקרונות: מופע יחיד של המחלקה, וגישה גלובלית. נדרשת האפשרות לגשת לאיבר במחלקה singleton מבלי ליצור אובייקט של המחלקה, וכן מנגנון אשר ישמור את ערכי האיברים של המופע שנוצר. התבנית מושגת באמצעות מחלקה הכוללת פונקציה, אשר בקריאה הראשונה יוצרת מופע חדש, ובקריאות הבאות, מחזירה מצביע לאותו מופע שיצרה קודם. כדי להבטיח שאכן יהיה מופע בודד של המחלקה ולא ניתן יהיה ליצור אותה בצורה אחרת, הגדרת הגישה לפונקציית הבנאי (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; }
    }
}

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;
}