Uyarlanabilir Singleton sınıfı
Daha önceki singleton ile ilgili yazımda, bu yazılım tasarım desenin nasıl çalıştığını ve nerede kullanabileceğimize göre bilgi vermiştim. Fakat bu sınıf, malesef get_class_name fonksiyonunun eksikliğinden ötürü her sınıf için extend ile uyarlamaya izin vermiyordu.
PHP 5.3.0 gelmesiyle, artık bu mümkün.
Örnek:
//php.net`den
class Abstract_Singleton
{
/**
* Instance of singleton's class
*
* @var array
*/
private static $_instance;
/**
* All constructeur should be protected
*/
protected function __construct(){}
/**
* Singleton method to load a new object
*
* @return HlPwd_SingletonModel
*/
public static function getInstance()
{
if (!isset(self::$instance[ get_called_class() ]))
{
$c = get_called_class();
self::$instance = new $c;
}
return self::$instance;
}
/**
* Destroy the singleton
*/
public function destroySingleton()
{
unset(self::$_instance[ get_class($this) ]);
}
/**
* Destructeur
*/
public function __destruct()
{
unset(self::$_instance);
}
/**
* Impeach the clone of singleton
*/
public function __clone()
{
trigger_error('Cloning not allowed on a singleton object', E_USER_ERROR);
}
}
Class X extends Abstract_Singleton {
public function __construct() {
echo "Yuklendim.\n";
}
}
X::getInstance();
X::getInstance();
X:destroySingleton();
X::getInstance();
class Abstract_Singleton
{
/**
* Instance of singleton's class
*
* @var array
*/
private static $_instance;
/**
* All constructeur should be protected
*/
protected function __construct(){}
/**
* Singleton method to load a new object
*
* @return HlPwd_SingletonModel
*/
public static function getInstance()
{
if (!isset(self::$instance[ get_called_class() ]))
{
$c = get_called_class();
self::$instance = new $c;
}
return self::$instance;
}
/**
* Destroy the singleton
*/
public function destroySingleton()
{
unset(self::$_instance[ get_class($this) ]);
}
/**
* Destructeur
*/
public function __destruct()
{
unset(self::$_instance);
}
/**
* Impeach the clone of singleton
*/
public function __clone()
{
trigger_error('Cloning not allowed on a singleton object', E_USER_ERROR);
}
}
Class X extends Abstract_Singleton {
public function __construct() {
echo "Yuklendim.\n";
}
}
X::getInstance();
X::getInstance();
X:destroySingleton();
X::getInstance();

Temmuz 17th, 2009 @ 6:29 pm
Merhaba.
PHP 5.3.0 nimetlerinden faydalaniyoruz artik! :)
Kodu denemek isteyenler icin:
X:destroySingleton(); da bir ‘:’ eksik.
Ayrica $instance degiskeni yerine $_instance degiskeni hata almamanizi saglayacaktir.
Not: Bu yazinin akabinde detayli bir php 5.3.0 yazisi gelecek mi yoksa is basa mi dustu deyip kollari sivayalim mi? :)
Temmuz 20th, 2009 @ 12:05 pm
Düzeltme için teşekkürler, biraz aceleye gelmiş yazı
5.3.0 için 1 büyük yazı yerine, buradaki gibi işlevine göre parça parça tanıtacağım. Nitekim mesela namespace’leri, get_class_name’in gelişinin yaratacağı farkları, ve ereg->preg dönüşümünü tek bir postta hem karışır, hem uzun sıkıcı bir şey olur sanırım.
Sorduğun şey quickstart benzeri birşeyse düşünmüyorum, daha changelog’undaki her maddeyi alt alta koyunca sayfa uzayıp gidiyor, ben üşendim o işten.
Temmuz 22nd, 2009 @ 3:11 pm
The üşengeç :)
Ağustos 20th, 2009 @ 12:50 pm
Merhaba Gökçe.
Singleton sınıfları gerçekten çok yararlılar. Özellikle fonksiyonlar ve başka sınıflar içerisinden bir sınıf çağırırken, acaba daha önceden çağrılmış mı çağrılmamış mı tereddütünden bizleri kurtarıyorlar.
“Singleton::getInstance($sınıf)” şeklinde kullandığımızı düşünerekten fikrini almak istediğim 2 nokta var:
1-) Eğer örneğini oluşturduğumuz sınıfın __construct metoduna parametre(ler) göndermek istersek (ki genelde istiyoruz) “getInstance” metodunu en etkin şekilde nasıl düzenleyebiliriz? “Singleton::getInstance($sınıf, $parametreler)” şeklinde parametreleri Array tipinde geçirsek nasıl olur?
2-) Bazen bir sınıftan, birbirinden farklı görevlerde kullanmak üzere çoklu nesneler oluşturmamız gerekebiliyor.
$obje = new Class;
$bu_baska_bir_obje = new Class;
$bu_bambaska_bir_obje = new Class;
gibi… Böyle bir durumda “$_instance” property’sini multi-dimension bir array olarak yeniden düzenlesek sence nasıl olur? Aşağıdaki gibi mesela:
Array(
[$sınıf] => array(
[0] => // $obje,
[1] => // $bu_baska_bir_obje,
[2] => // $bu_bambaska_bir_obje
))
“Singleton::getInstance($sınıf, $paramemetreler, $index)” şeklinde de aynı sınıfın çoklu örnekleri saklayabiliriz ve daha sonra index numarasındn erişebiliriz diye düşünüyorum.
Teşekkürler.
Ağustos 20th, 2009 @ 2:06 pm
Merhaba,
Öncelikle, singleton sınıfını kullanırken dikkat edilecek noktalarından başlıcalarından birisi, asla sınıfın context’ini değiştirmemek. O yüzden __construct protected, değiştirilemez, parametre içermez, vs. Nitekim, bu sınıfı birden fazla yerde, fakat aynı sınıf olarak getirmek sözkonusu, yani aynı işlevi yapmasını bekliyoruz. Bu nedenle __construct sırasında context değişkenleri sabit olmalı ve sınıf işleyişini asla değiştirmeden ne zaman çağırılırsa çağırılsın, belleğindeki aynı işlevi yapmalı, sonuç olarak çağırdığımız sınıf eğer aynı olmayacaksa, onu singleton yapmamızın hiç bir anlamı yok.
Senin söylediğin ise ‘factory’ design patternine yaklaşıyor. Bu factory sınıfları, başka sınıfları driver olarak gören, onlarla önceden interface olarak tanımladığımız methodlarla iletişim kuran bir üst-sınıf. Factory design patternını singleton özelliğine sahip bir yükleyici olarak düşünebiliriz, can alıcı nokta bu sınıf contextini değiştirmiyor, hala görevi sadece interface’i belli bir sınıfı çağırmak ve onunla iletişimini sağlamak, sanırım örnek verirsem daha açıklayıcı olur:
$image::factory(’imagemagick’)->load(’a.jpg’)->resize(array(’x'=>125,’y'=>125));
$image::factory(’gd’)->load(’a.jpg’)->resize(array(’x'=>125,’y'=>125));
gd ve imagemagick alt sınıflarında, load ve resize image interfaceinde kesin olarak tanımlanmış, methodlar. Sen hangi sınıfı yüklüyorsan, o sınıfa göre işlem yapıyorlar. Varolan örnekleri incelemek istersen, Kohana’daki driver yapılarını incelemeni öneririm.
Fakat tek sınıf instanceları değilde, array olarak sınıf geçirmekten bahsetmişsin, eğer böyle bir durum varsa, bu sınıfların ‘araç’ sınıflar olması çok olası. Eğer bu sınıflar araçsa ( html::makeTable() ) gibi, kendi contextlerine ihtiyaç duymadan tasarlanmalıdırlar, bunun yolu ise static methodlar. Böylece varolan sınıfa, instance referansını herhangi bir şekilde bildirmeden, o sınıfın içinde herhangi bir yerde html::makeTable(); mail::send(”x”,..); gibi çağrımlarda bulunabilirsin. Tertemiz kod olur. Kendi contextine ihtiyaç duyan sınıflar bile bu şekilde tasarlanabiliyorlar, örneğin socket::sendCmd(”eburhan.com”,80,”GET / HTTP 1/0″), kendi içinde önce static function sendCmd(..) { $c = socket::connect($h,$p); … } ile resource handlerını alıyor, sonra komutu gönderiyor, hepsi static.
İkincisi ise illaki context’e ihtiyaç duyulan sınıflar, $db gibi. Nitekim illa connect etmesi, db select etmiş olması, vb durumları olduğundan instance’ının geçirilmesi şart olabilir. Benim aklımdaki çözümler,
1 ) bu tür sınıflar birden fazla kullanılmıyorsa, singleton yapmak, böylece ilk context her zaman korunur. Handikap: Belki de uygulama iki veritabanı üzerinde aynı anda çalışıyordur, illaki iki adet sınıfın olması şarttır.
2 ) bir registery singleton sınıfı kullanırsın. Bu singleton registery sınıfının görevi verilen objeler ve arrayleri istendiğinde geri vermektir. Aynı zamanda anahtara göre var mı yok mu sorgulaması da yaptırılabilir. Böylece Registery::add(’db’,array($dbo1,$dbo2)); şeklinde attığın iki sınıfı, başka bir sınıfın içinden, if ($db = Registery::get(’db’) AND count($db)) { throw new Exception(’No available db interface resources found’,321); } şeklinde kontrol ettirebilirsin ve işleyebilirsin.
3 ) çağrılan her bir methodda, gerekli interface listeleri verilir, hamallıktır, ama çalışır.
Dahası için ve olaki bir yerlerde hatalıysam:
http://www.devshed.com/c/a/PHP/ObjectOriented-Programming-Through-Design-Patterns/
Ağustos 20th, 2009 @ 4:50 pm
Buarada konuyla ilgili adam kılık kontrol etmediğim bir önceki verdiğim link pek işe yarar şeyler içermediğini görünce
Türkçe, Emre Çevik tarafından yazılmış iki güzel anlatım buldum:
http://code.internet.com.tr/singleton-design-pattern/ ( not: buradaki örnekte instanceların contextleri değişmiş olma ihtimaline göre arrayli olarak tutulmuş, bu aslında singleton standartında olmayan birşey )
http://code.internet.com.tr/factory-method-design-pattern/
Ağustos 28th, 2009 @ 11:45 am
gökce patternlerin ikisinide vermis yapilmasi gereken ikisini birlestirmek, (factory singleton) hem parametre vere bilir hem her siniftan birtane olusur.,
factory de new sinif yerine sinif::getinstance
@eburhan
yukarda bazen bir siniftan birbirinden farkli görevler icin coklu nesneler olusturmam gerekiyor demissin, mesela ne icin buna gerek duyuyorsun?