singleton design pattern
KategorilerTasarım DesenleriYazılım Mimarisi

Singleton Pattern Nedir? Örnekli Anlatım

Singleton Design Pattern yani Singleton Tasarım Deseni, yazılım geliştirmede en yaygın kullanılan tasarım desenlerinden biridir. Creational Design Pattern‘ler arasında bulunan Singleton Design Pattern, uygulamada bir sınıfı o sınıftan yalnızca bir tane instance(örneklem) olması için kısıtlayan ve bu instance‘a global erişim sağlayan tasarım desenidir.

Ne Zaman Singleton Pattern Kullanılır?

Bu tasarım deseni bir sınıfın tek bir instance‘ının uygulamanın her yerinde koordine olarak kullanılması gerektiği zamanlarda kullanılır. Loglama, Ayarlar ve Önbellekleme bunlara örnek olarak gösterilebilir.

Singleton Pattern Nasıl Uygulanır?

Singleton Design Pattern‘in nasıl uygulanacağını bir C# örneğiyle görelim.

public class Singleton
{
    private static Singleton instance;
    private static readonly object lockObject = new object();

    // Yeni bir instance oluşturulmasını engellemek için private constructor oluşturuyoruz
    private Singleton()
    {
        // eğer başlangıçta olması gereken şeyler varsa buraya yazılacak
    }

    public static Singleton Instance
    {
        get
        {
            // Thread güvenliği için iki kere kontrol ediyoruz.
            if (instance == null)
            {
                lock (lockObject)
                {
                    if (instance == null)
                    {
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    }

    // Singleton sınıfının diğer kodları ve özellikleri
}

Bu örnekte Singleton sınıfından direk bir instance oluşturulmasını önlemek için private bir constructor oluşturuyoruz. Aynı zamanda sınıfın Instance isminde statik bir özelliği var, bu özellik bu sınıftan oluşturulan instance‘a erişimi sağlıyor.

Singleton singleton = Singleton.Instance;

Bu şekilde bu sınıftan oluşturulan tek instance‘a erişim sağlayabiliyoruz.

Bu şekilde anlatınca biraz havada kalmış olabilir, bir de gerçek hayattan örneklerle Singleton Design Pattern‘i inceleyelim.

Singleton Design Pattern Örnekler

Daha önce bahsetmiş olduğum Loglama mantığı için bir örnek verelim.

public class Logger
{
    private static Logger instance;
    private static readonly object lockObject = new object();

    // Yeni bir instance oluşturulmasını engellemek için private constructor
    private Logger()
    {
        // eğer başlangıçta olması gereken şeyler varsa buraya yazılacak
    }

    public static Logger Instance
    {
        get
        {
            if (instance == null)
            {
                lock (lockObject)
                {
                    if (instance == null)
                    {
                        instance = new Logger();
                    }
                }
            }
            return instance;
        }
    }

    public void Log(string message)
    {
        // Logging implementation
        Console.WriteLine($"Logging: {message}");
    }
}

Uygulamanın her yerinde buradaki loglama mantığını şu şekilde kullanabiliriz:

var logger = Logger.Instance; 
logger.Log("Önemli bir olay gerçekleşti.");

Başka bir örnek daha verelim, bu sefer de sistemin farklı yerlerinden tetiklenebilecek toplam oy miktarını sayacağımız bir oylama uygulaması olduğunu düşünelim.

public class VoteMachine
{
    private static VoteMachine _instance = null;
    private int _totalVotes = 0;

    private static readonly object lockObj = new object();

    private VoteMachine()
    {
    }

    public static VoteMachine Instance
    {
        get
        {
            if (_instance == null)
            {
                lock (lockObj)
                {
                    if (_instance == null)
                    {
                        _instance = new VoteMachine();
                    }
                }
            }
            return _instance;
        }
    }

    public void RegisterVote()
    {
        _totalVotes += 1;
        Console.WriteLine("Kaydedilen Oy #" + _totalVotes);
    }

    public int TotalVotes
    {
        get
        {
            return _totalVotes;
        }
    }
}

Oy verme işlemi her yapıldığında ekrana kaçıncı oyun kaydedildiği mesajını yazdırıyoruz ve toplam oy sayısını TotalVotes özelliğinde tutuyoruz. Bu sefer öncekinden farklı olarak thread güvenliğini neden eklediğimizi de görelim.

public class Program
{
    public static void Main(string[] args)
    {
        // sayılardan oluşan bir liste oluşturuyoruz
        var numbers = Enumerable.Range(0, 20);

        // numbers sayısı kadar oy verme işlemini paralel olarak çalıştırıyoruz
        Parallel.ForEach(numbers, i =>
        {
            var vm = VoteMachine.Instance;
            vm.RegisterVote();
        });

        Console.WriteLine(VoteMachine.Instance.TotalVotes);
    }
}

Sonuç olarak şu şekilde bir çıktı elde ediyoruz:

Kaydedilen Oy #1
Kaydedilen Oy #3
Kaydedilen Oy #7
Kaydedilen Oy #8
Kaydedilen Oy #5
Kaydedilen Oy #10
Kaydedilen Oy #11
Kaydedilen Oy #12
Kaydedilen Oy #13
Kaydedilen Oy #14
Kaydedilen Oy #15
Kaydedilen Oy #16
Kaydedilen Oy #17
Kaydedilen Oy #18
Kaydedilen Oy #19
Kaydedilen Oy #2
Kaydedilen Oy #9
Kaydedilen Oy #20
Kaydedilen Oy #4
Kaydedilen Oy #6
20

Gördüğünüz gibi 20 kere oy verme işlemi çalıştırıldı ve 20 adet oy verildiği görülebiliyor, çıktıdaki sıralama siz kodu çalıştırdığınızda farklı olacaktır, paralel olarak çalıştırdığımızdan böyle bir sonuç aldık.

Peki bir de Instance özelliğimizi aşağıdaki gibi güncelleyerek thread güvenliğini sağlamadan çalıştırsaydık ne olurdu onu görelim:

public static VoteMachine Instance
{
    get
    {
        if (_instance == null)
        {
            _instance = new VoteMachine();
            return _instance;
        }
    }
}

Burada lock(lockObj) korumasını kaldırdık, aldığımız sonuç ne şekilde değişti görelim:

Kaydedilen Oy #1
Kaydedilen Oy #1
Kaydedilen Oy #1
Kaydedilen Oy #5
Kaydedilen Oy #6
Kaydedilen Oy #3
Kaydedilen Oy #8
Kaydedilen Oy #9
Kaydedilen Oy #10
Kaydedilen Oy #11
Kaydedilen Oy #12
Kaydedilen Oy #13
Kaydedilen Oy #14
Kaydedilen Oy #15
Kaydedilen Oy #16
Kaydedilen Oy #1
Kaydedilen Oy #2
Kaydedilen Oy #7
Kaydedilen Oy #17
Kaydedilen Oy #4
17

Oy verme işlemi 20 kere çalıştırılmasına rağmen gördüğünüz gibi 17 tane oy saydı, bunun sebebi thread güvenliğini doğru şekilde sağlayamamamız, aynı anda 2 işlem yapıldığından veri kaybı yaşandı.

Sonuç

Bu yazıda Singleton Design Pattern hakkında temel bilgileri ve uygulama örneklerini ele aldık. Singleton Design Pattern, uygulamalarda daha iyi performans, verimli kaynak yönetimi ve tutarlılığı sağlama amacıyla kullanılan etkili bir tasarım yöntemidir.

Design Patternler hakkında genel bilgiler için Design Pattern yazısını ziyaret edebilirsiniz.

TotalVotes örneği kaynak: https://www.tutorialsteacher.com/csharp/singleton

Bir Cevap Yazın