.NetGurus

Diğer Yazılar

Google a Ekle

Add to Google



Tasarım Kalıpları 1 - Singleton Tasarım Kalıbı

Tasarım Kalıplarına Genel Bakış

Tasarım kalıpları, nesneye yönelik programlamada sık karşılaşılan bazı problemlerin çözümüne yönelik olarak ortaya atılmış yöntemlerdir. Gang of Four (GOF) olarak bilinen Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides tarafından yazılmış olan Design Patterns, Elements of Reusable Object-Oriented Software 'de 23 adet temel tasarım kalıbına yer verilmiştir. GOF bu tasarım kalıplarını, Creational, Structural ve Behavioral olmak üzere üç kategoride incelemişlerdir. Tasarım kalıpları makale serimizin ilki olan bu makalede Creational tasarım kalıplarından Singleton tasarım kalıbını detaylı olarak inceleyerek C# ile örnek bir kullanımını sunacağız.

Singleton Tasarım Kalıbı

Singleton tasarım kalıbının amacı, bir sınıftan yaratılmış sadece bir adet nesne olmasını ve bu nesneye genel erişimi sağlamaktır. Bunun yanında, nesnenin sadece o nesneye ilk defa ihtiyaç duyulduğunda yaratılmasını sağlamak (lazy load) da şart olmasa da performans açısından uyulması gereken bir kuraldır.

Singleton tasarım kalıbına ne zaman ihtiyaç duyabiliriz bir düşünelim. Mesela Windows masaüstü uygulaması geliştiriyoruz. Çok sık kullanılan şekliyle menü vb. kontrollerimizin yer aldığı bir MDI ana formumuz var ve diğer formlar (child) bu ana formdaki menülerden erişilerek ana formun içinde açılıyor. Amacımız eğer bir child form hiç açılmamışsa yeni yaratılıp açılması, ama açıksa yeni bir form yaratılmadan açık olan formun gösterilmesi ise singleton tasarım kalıbı tam da bu noktada işimize yarayacaktır.

Şimdi genel olarak singleton tasarım kalıbının farklı implementasyonlarnı görelim.

public sealed class Singleton
{
    static Singleton instance=null;

    Singleton() {}

    public static Singleton Instance
    {
        get
        {
            if (instance==null)
               {
                    instance = new Singleton();
               }
        return instance;
        }
    }
}

Bu singleton tasarım kalıbına thread-safe olmayan bir örnek. Yani if (instance==null) kontrolü iki farklı thread tarafından kontrol edilmiş ve ikisi de true olarak sonuç almış olabileceği için thread-safe değil.

 

public sealed class Singleton 
{
	Singleton( ) { }

	static readonly Singleton instance = new Singleton( );

	public static Singleton UniqueInstance 
	{
		get 
		{ 
			return instance;
		}
	}
}

Bu thread-safe bir implementasyon, fakat bu defa lazy load yok. Yani Singleton nesnesi ihtiyaç duyulduğu zaman değil ilk başta hemen oluşturuluyor.

 

public class Singleton 
{

	Singleton ( ) { }

	class SingletonCreator 
	{
		static SingletonCreator ( ) {}

		internal static readonly Singleton uniqueInstance = new Singleton( );
	}

	public static Singleton UniqueInstance 
	{
		get 
		{
			return SingletonCreator.uniqueInstance;
		}
	}
}

İşte hem thread-safe, hem de lazy-load olan bir singleton tasarım kalıbı implementasyonu örneği. Singleton nesnesinin ilk oluşturulması, SingletonCreator classının static olan uniqueInstance üye değişkenine ilk erişim ile olmaktadır.

MDI Form Uygulaması

Singleton tasarım kalıbının kullanımına yönelik olarak MDI form uygulamasından biraz bahsetmiştik. Şimdi anlaşılmasını kolaylaştırmak için örneğimizi biraz daha spesifik hale getirelim. Uygulamamızda frmAnaMenu ismini verdiğimiz bir MDI Parent formumuz olsun. Bunun içinde açılacak olan MDI child formlarımız da frmYeniKayit, frmArama, frmSecenekler formlarımız olsun. Normalde singleton tasarım kalıbını kullanmazsak bir child formu en basit haliyle şu şekilde açabiliriz.

	
    frmYeniKayit yeniKayitForm = new frmYeniKayit();
    yeniKayitForm.MdiParent = this;
    yeniKayitForm.Show();
    yeniKayitForm.Activate();

Child formlarımızı bu şekilde açtığımızda, her seferinde frmYeniKayit için yeni bir form açılacaktır. Fakat child formlarımızı singleton tasarım kalıbına göre tekrar düzenlersek:

 

    public partial class frmYeniKayit : Form
    {
        private static frmYeniKayit singletonForm = null;

        public static frmYeniKayit Instance()
        {
            if (singletonForm == null)
            {
                singletonForm = new frmYeniKayit();
                singletonForm.FormClosed += new FormClosedEventHandler(singletonForm_FormClosed);
            }
            return singletonForm;
        }

        static void singletonForm_FormClosed(object sender, FormClosedEventArgs e)
        {
            singletonForm.FormClosed -= new FormClosedEventHandler(singletonForm_FormClosed);
            singletonForm = null;
        }

        private frmYeniKayit()
        {
            InitializeComponent();
        }
    }

İlk verdiğimiz singleton tasarım kalıbı implementasyonuna göre frmYeniKayit formunu singleton özelliklerini sağlayacak şekilde tekrar düzenledik. Burada ekstra olarak FormClosed olayına bir ekleme yaptık. Form kapatıldığında formun null olarak atanmasını sağladık. Şimdi bunu ne için yaptık? Normalde form kapatıldığında form disposed oluyor fakat değeri null olmuyor, bu durumda singleton olarak bir nesne hala mevcut oluyor ama disposed olduğu için kullanmamız mümkün olmuyor. Biz burada formun kapatılmasıyla o nesneyi yok edip, forma tekrar ulaşılmak istendiğinde yeni bir nesne yaratılmasını sağlamış olduk.
Artık biz frmYeniKayit formunu MDI parenttan çağırırken

	frmYeniKayit frm = frmYeniKayit.Instance();
	frm.MdiParent = this;
	frm.Show();
	frm.Activate();

şeklinde yazıyoruz ve kullanıcı MDI parenttan Yeni Kayıt menüsüne her basışında varsa mevcut frmYeniKayit formu açılıyor, yoksa yeni bir frmYeniKayit formu oluşturuluyor.

Güzel, artık child formlarımızı singleton olarak açabiliyoruz. Ama size de biraz refactoring'e ihtiyacımız var gibi geldi mi? Gerçek bir uygulamada onlarca ve hatta belki de daha fazla child formumuz olabilir. Bu şekilde child formlarımızı singleton açmak için her bir child formumuzda aynı işlemleri tekrar tekrar yapmak gerekecektir ve çok fazla miktarda kod tekrarı olacaktır.

Refactor

 Şimdi ihtiyacımız olan, singleton olarak oluşturulacak bu child formları, kod tekrarı yapmadan oluşturabilen bir sınıf. Generic kullanarak bir deneyelim:

 

    public class SingletonFormProvider where T : Form,new()
    {
        private static T singletonForm = null;

        public static T Instance()
        {
            if (singletonForm == null)
            {
                singletonForm = new T();
                singletonForm.FormClosed += new FormClosedEventHandler(singletonForm_FormClosed);
            }
            return singletonForm;
        }

        static void singletonForm_FormClosed(object sender, FormClosedEventArgs e)
        {
            singletonForm.FormClosed -= new FormClosedEventHandler(singletonForm_FormClosed);
            singletonForm = null;
        }

        private SingletonFormProvider()
        {            
        }
    }

görüldüğü gibi yeni sınıfımız singleton tasarım kalıbının ilk verdiğimiz implementasyonundan pek de farklı değil. Sadece generic type eklenerek oluşturulacak singleton instance'ın tipini çağırılma esnasında belirlememize olanak sağlıyor. Şimdi bu SingletonFormProvider sınıfımızı nasıl kullanacağız peki?

 

    frmYeniKayit frm = SingletonFormProvider<frmYeniKayit>.Instance();
    frm.MdiParent = this;
    frm.Show();
    frm.Activate();

frmYeniKayit formumuzu singleton yapmadan, SingletonFormProvider sınıfı aracılığıyla çağırdığımızda yine frmYeniKayit sınıfının tek bir örneğine ulaşmış oluyoruz. Yani frmYeniKayit formu açıksa, açık olan form gösteriliyor, açık değilse yeni bir frmYeniKayit nesnesi yaratılıp o gösteriliyor.

Bu arada şunu da not düşmeden geçmeyelim.  Bu kullanım şeklinde çağıracağınız forma parametre geçiremezsiniz. Yani çağıracağınız formun constructor'ı parametreli olması gerekiyorsa bu şekilde kullanmanız mümkün olmaz. Fakat bu SingletonFormProvider sınıfında biraz değişiklik yaparak bunu da sağlamak mümkün. Bunun için kullanabileceğimiz SingletonFormProvider sınıfının yeni hali şu şekilde olacaktır.

 

    public class SingletonFormProvider where T : Form
    {
        private static T singletonForm = null;

        public static T Instance(params object[] args)
        {
            if (singletonForm == null)
            {
                
                singletonForm = (T)Activator.CreateInstance(typeof(T), args);
                singletonForm.FormClosed += new FormClosedEventHandler(singletonForm_FormClosed);
            }
            return singletonForm;
        }

        static void singletonForm_FormClosed(object sender, FormClosedEventArgs e)
        {
            singletonForm.FormClosed -= new FormClosedEventHandler(singletonForm_FormClosed);
            singletonForm = null;
        }

        private SingletonFormProvider()
        {            
        }
    }

Bunda ne değişiklikler yaptık? Öncelikle, birinci satırdaki generic tipi T'nin new() ile oluşturulabileceği ifadesini kaldırdır. Çünkü artık T tipinde yeni form yaratma işini singletonForm = new T() şeklinde değil, (T)Activator.CreateInstance(typeof(T), args) şeklinde, constructor'a parametre de vermeye olanak sağlayacak şekilde değiştirdik. public static T Instance() şeklinde olan singleton Instance döndüren metodumuzu da parametre alabilecek şekilde public static T Instance(params object[] args) olarak değiştirdik. Böylelikle artık formlarımızın constructor'ına parametre geçirmeye ihtiyacımız olursa, bu sorunu da halletmiş olduk.

İşte bu kadar. Artık child formlarımızda hiçbir değişiklik yapmadan, SingletonFormProvider sınıfını kullanarak MDI parent formumuzdan singleton olarak çağırabiliyoruz.

Ekte yukarıda örnek verdiğimiz kodlarla birlikte, MDI parent formdan child formları çağırırken kullanabileceğiniz kod tekrarı olmayan yöntemi ve Singleton tasarım kalıbına ilişkin sınıf diyagramını bulabilirsiniz.

Örnek uygulamayı indirmek için tıklayınız. (Singleton.zip - 24,05 kb)


Posted by omer on 11 Eylül 2009 Cuma 01:51
Permalink | Yorumlar (0) | Post RSSRSS comment feed