Serkan Kaba

Karalama Defteri

Singleton Pattern üzerine

Posted by Serkan Kaba 16 Nisan 2009

Singleton pattern kullanım amacı bir sınıfın kısıtlı nesnesini (hatta çoğunlukla tek) tek bir yerden yaratılması amacıyla kullanılmaktadır.  Şimdi bunu sağlayan Singleton sınıfı kodunu görelim. (Kod 1)

public class Singleton {
	private static Singleton theInstance = new Singleton();
	// Sınıf yüklendiğinde yaratılan tekil nesne

	public static Singleton getInstance() {
		// Dışarıdan nesne almak için bu metodu kullanacağız.
		return theInstance;
	}

	private Singleton() {
		// Constructor private tanımlansın ki dışarıdan erişilemesin.
		System.out.println("constructor");

		//Constructor 100 ms. bekleme gibi bir "iş" yapsın.
		try {
			Thread.sleep(100);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

Burada sınıfın nesnesine sadece getInstance() metodu ile erişilebilecektir. Ccnstructor 100 ms. gibi “makul” bir süre beklemektedir. Bu sınıftan nesneleri yaratan sınıfımızın kodunu da inceleyelim. (Kod 2)

public class SingletonTest {

	private static Singleton singletona, singletonb;

	public static void main(String[] args) {
		singletona = Singleton.getInstance();
		singletonb = Singleton.getInstance();
		System.out.println(singletona == singletonb);
	}

}

SingletonTest sınıfını çalıştırdığımızda aşağıdaki gibi bir çıktı vermektedir.

constructor
true

Görüldüğü üzere kodumuz amacına ulaşmış constructor 1 defa çağırılmıştır. Ancak bu yöntemde Singleton sınıfının nesnesi sınıf yüklendiği anda ilklenmektedir. Şimdi bu ilklemeyi ihtiyaç anına bırakalım (Lazy initilization). Yeni Singleton sınıfımız şu şekilde oluşacaktır. (Kod 3)

public class Singleton {
	private static Singleton theInstance;
	// İhtiyaç anında yaratılacak nesne

	public static Singleton getInstance() {
		// Dışarıdan nesne almak için bu metodu kullanacağız.
		// Eğer nesnemiz yaratılmamış ise yaratalım.
		if (theInstance == null) {
			theInstance = new Singleton();
		}
		// Şu an ya da önceden yaratılan nesneyi döndürelim.
		return theInstance;
	}

	private Singleton() {
		// Constructor private tanımlansın ki dışarıdan erişilemesin.
		System.out.println("constructor");

		// Constructor 100 ms. bekleme gibi bir "iş" yapsın.
		try {
			Thread.sleep(100);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

Bu şekilde de programımızı çalıştırdığımızda aynı çıktıyı vermekte ve beklenen şekilde işlemektedir. Şimdi SingletonTest sınıfını biraz değiştirelim ve getInstance() birden çok threadden çağırıldığında ne yapıyor onu görelim. (Kod 4)

public class SingletonTest {

	private static Singleton singletona, singletonb;

	public static void main(String[] args) {
		// getInstance() iki ayrı thread ile çağırılsın.
		Thread threada = new Thread() {

			@Override
			public void run() {
				singletona = Singleton.getInstance();
			}

		};
		threada.start();

		Thread threadb = new Thread() {

			@Override
			public void run() {
				singletonb = Singleton.getInstance();
			}

		};
		threadb.start();

		// Her iki thread tamamlanana kadar beklensin
		while (!(threada.getState() == Thread.State.TERMINATED && threadb
				.getState() == Thread.State.TERMINATED))
			;

		System.out.println(singletona == singletonb);
	}

}

Çıktımıza göre buu sefer sapıttı sanırım.

constructor
constructor
false

Evet constructor iki defa işledi ve bize iki ayrı nesne üretti. Şimdi ilklendirmemizi thread-safe hale getirelim. Bunu da getInstance() metodunu synchronized olarak tanımlayarak yapacağız. (Kod 5)

public class Singleton {
	private static Singleton theInstance;
	// İhtiyaç anında yaratılacak nesne

	public static synchronized Singleton getInstance() {
		// Dışarıdan nesne almak için bu metodu kullanacağız.
		// Eğer nesnemiz yaratılmamış ise yaratalım.
		// Metodu synchronized yaparak sayesinde aynı anda sadece bir yerden
		// çağırılmasını garanti ettik.
		if (theInstance == null) {
			theInstance = new Singleton();
		}
		// Şu an ya da önceden yaratılan nesneyi döndürelim.
		return theInstance;
	}

	private Singleton() {
		// Constructor private tanımlansın ki dışarıdan erişilemesin.
		System.out.println("constructor");

		// Constructor 100 ms. bekleme gibi bir "iş" yapsın.
		try {
			Thread.sleep(100);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

Evet! Eski çıktımıza geri döndük. Ancak unutmayalım ki synchronized Java’nın bize sağladığı bir imkan onun için Wikipedia Singleton pattern başlığında gördüğüm yöntemle devam edeceğiz. Bu yöntemde ilk koddaki gibi ilkleme sınıf yükleme esnasında ancak yardımcı başka bir sınıfın yüklenmesinde gerçekleşecek. (Kod 6)

public class Singleton {
	private static class SingletonHolder {
		// İlklemeyi ilk örnekteki gibi sınıf yüklemesi esnasına taşıyacağız.
		// Ancak bu sefer ilklemeyi Singleton değil yardımcı başka bir sınıf
		// gerçekleştirecek.
		private final static Singleton INSTANCE = new Singleton();
	}

	public static Singleton getInstance() {
		// Bu kullanım SingletonHolder sınıfının yüklenmesini dolayısıyla
		// Singleton nesnesinin ilklenmesini tetikleyecek.
		return SingletonHolder.INSTANCE;
	}

	private Singleton() {
		// Constructor private tanımlansın ki dışarıdan erişilemesin.
		System.out.println("constructor");

		// Constructor 100 ms. bekleme gibi bir "iş" yapsın.
		try {
			Thread.sleep(100);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

8 Yanıt to “Singleton Pattern üzerine”

  1. ahmet said

    Merhaba

    baska bir güvenli Singleton olusturma yontemi de enum kullanmak.

    bu hem kisa hem de cok guvenli.

    http://www.pasteninja.com/paste/2278

  2. salci said

    Merhaba,
    Güzel bir derleme olmuş. Bir alt başlıkta thread safety de anlatılabilirdi, ikisi birbirini güzel tamamlardı kanımca. Emeğinize sağlık.

  3. Serkan Kaba said

    Öncelikle teşekkür ederim. Aslında concurrency ve thread safety bir yazıda anlatılabilecek kadar basit bir konu değil ama bir race-condition örneğiydi bu yazıdaki. Belki race-condition nedir gibi bir yazı olabilir.

  4. Erdem said

    Singleton tasarım şablonu ile ilgili farklı görüşler de var. Örneğin:

    http://steve.yegge.googlepages.com/singleton-considered-stupid

    http://accu.org/index.php/journals/1470

    Sanırım tekli (singleton) tasarım şablonunu bir sınıfa ait tek bir örnek olmasını istediğimizde kullanıyoruz. Java için uygulaması nasıldır bilemiyorum ama C++ ile kullanımında bazı zorluklar var. Hatırladığım kadarıyla çoklu (multithreading) sistemlerde de tekli tasarım şablonu çalışması gerektiği gibi çalışmıyor. Gene de aradan uzun bir zaman geçtiği için tam emin değilim🙂

    Daha önce bu konuyu C Dili Haber Grubunda konuşmuştuk:

    http://tech.groups.yahoo.com/group/cdili/message/7602

  5. Selman said

    Şık bir anlatım olmuş. Gayet faydalı oldu.

    Teşekkürler

  6. okanakyuz said

    çok faideli olmuş

  7. Mücahid Uslu said

    Güzel ve sade bir anlatım olmuş.

  8. Öncelikle şunu belirtmek isterim ki güzel bir makale olmuş.
    http://javacodetips.blogspot.com.tr/2014/04/design-patterns-singleton-pattern.html adresinde konularda birtakım uyarılarla birlikte tavsiyelerin yer aldığı ve ilişkili tasarım desenlerinin anlatıldığı ingilizce bir anlatım da mevcuttur. Teşekkür ederim.

Bir Cevap Yazın

Aşağıya bilgilerinizi girin veya oturum açmak için bir simgeye tıklayın:

WordPress.com Logosu

WordPress.com hesabınızı kullanarak yorum yapıyorsunuz. Log Out / Değiştir )

Twitter resmi

Twitter hesabınızı kullanarak yorum yapıyorsunuz. Log Out / Değiştir )

Facebook fotoğrafı

Facebook hesabınızı kullanarak yorum yapıyorsunuz. Log Out / Değiştir )

Google+ fotoğrafı

Google+ hesabınızı kullanarak yorum yapıyorsunuz. Log Out / Değiştir )

Connecting to %s

 
%d blogcu bunu beğendi: