“Bazı şeylerin gitmesine izin vermek işte bu nedenle çok önemlidir. Onları serbest bırakmak. Gevşek olanı kesmek. İnsanların hiç kimsenin işaretli kağıtlarla oynamadığını anlaması gerekiyor; bazen kazanırız ve bazen kaybederiz. Hiçbir şeyi ger almayı bekleme, yaptıkların için takdir edilmeyi bekleme, ne kadar zeki olduğunun keşfedilmesini bekleme ya da aşkının anlaşılmasını. Daireyi tamamla. Gururlu, yetersiz ya da kibirli olduğun için değil, sadece artık onun senin yaşamında yeri olmadığı için. Kapıyı kapat, plağı değiştir, evi temizle, tozdan kurtul. Geçmişte olduğun kişi olmayı bırak ve şu an kimsen o ol.”
by Paulo Coelho - Zahir S. 204

Singleton Design Pattern(Singleton Tasarım Şablonu)

Tarih: Ekim 14th, 2012 | Yazar: | Kategoriler: Php, Tasarım Şablonları (Design Patterns) | Yorum Yok »

Siteme giren her kullanıcı için bir User nesnesi oluşturuyorum. Amacım bu nesne üzerinden siteme giren kullanıcının bilgilerine ulaşmak.

User sınıfım

namespace Singleton;
class User
{
    public $firstName;
    public $lastName;

    /**
     * @return string Kullanıcının ad ve soyadı.
     */
    public function getFullName()
    {
        return $this->firstName . ' ' . $this->lastName;
    }
}

Kullanıcı siteme girdi. İstek sunucuya ulaştı ve benim PHP uygulamam çalışmaya başladı. Kullanıcıyı takip etmek için bir adet User sınıfından örnekledim. Şu şekilde;

namespace Singleton;
require_once 'User.php';
class Bootstrap
{
    public function __construct()
    {
        $user = new \Singleton\User();
        $user->firstName = 'Can';
        $user->lastName = 'Aydoğan';
    }

}

User sınıfından siteme giren kullanıcı için yeni bir nesne türettim. Buraya kadar her şey yolunda. Şimdi başka bir sınıf içinde siteme giren kullanıcının bilgilerine ulaşmaya çalışalım.

namespace Singleton;
require_once 'User.php';
class Foo
{
    public function __construct()
    {
        $user = new \Singleton\User();

        //Kullanıcının ad ve soyad bilgilerini ekrana yaziyoruz.
        echo $user->getFullName();
    }
}

Hadi bunları kullanalım…

require_once 'Bootstrap.php';
require_once 'Foo.php';

$bootstrap = new \Singleton\Bootstrap();
$foo = new \Singleton\Foo();

Öncelikle Foo sınıfımızın __constructor methoduna bakalım. Bu method içinde User sınıfından yeni bir kullanıcı nesnesi oluşturdum ve getFullName methodu ile bu kullanıcın ad ve soyad bilgilerini ekrana yazdım. Ekranda peki ne yazdı? Söyleyeyim hiçbir şey. Neden peki?

Ben kullanıcıyı Bootstrap sınıfı içinde oluşturdum ve ad soyad bilgilerini orada tanımladım. Daha sonra Foo sınıfı içinde yeni bir kullanıcı nesnesi oluşturdum. Bu yeni nesnede daha önce tanımlamış olduğum ad soyad bilgileri yok. Foo sınıfında oluşturduğum nesne tamamen yeni ve Bootstrap sınıfında oluşturduğum nesne ile hiçbir ilgisi yok. Burada bariz bir mantık hatası var. Siteme giren bir kullanıcı için iki adet kullanıcı nesnesi oluşturdum. (Gerçek hayattan bir örnek verirsek bir insanın iki adet nüfus cüzdanı olması gibi düşünebiliriz bu durumu) Oluşturduğum nesneler birbirinden habersiz ve iki ayrı kullanıcı gibi hareket ediyorlar. Birinde ad soyad bilgisi varken diğerinde bu bilgi yok. Bu yüzden Foo sınıfında kullanıcın ad soyad bilgilerine ulaşamıyorum.

Hem bu mantık hatasını gidermek hem de kullanıcın bilgilerine tüm sınıflarda ulaşmak için ne yapabilirim?

Bu noktada yardımımıza Singleton tasarım şablonu yetişiyor. Bir sınıfı sadece bir kez örnekleyerek tüm uygulama boyunca bu örneği kullanmamızı sağlar Singleton tasarım şablonu. Yukarıdaki örnekteki ihtiyaçlarımızı tam olarak karşılıyor. Negzel!

Singleton Tasarım Şablonunun Uygulanması ve Kullanımı

Yukarıda kullandığımız örneğe tasarım şablonumuzu uygulayalım.

User sınıfımızın son hali

namespace Singleton;
class User
{
    public $firstName;
    public $lastName;
    private static $_instance;

    private function __construct(){}

    public static function getInstance()
    {
        if (null === self::$_instance) {
            self::$_instance = new User();
        }
        return self::$_instance;
    }
    /**
     * @return string Kullanıcının ad ve soyadı.
     */
    public function getFullName()
    {
        return $this->firstName . ' ' . $this->lastName;
    }
}

Sınıfımızın son halini incelediyseniz eğer, eklenen 2 yeni methodu ve bir adet özelliği(property) fark etmişsinizdir. Bunları ne amaçla eklediğimden başlıyorum.

  • $_instance: Bu özelliği(property) static olarak belirledik. Yani bu özelliğe atayacağımız değer ne olursa olsun saklanacaktır. new ile onlarca yeniden türetsek de. Sadece bir tane değer tutacaktır. O da son atanan değer olacaktır. Çünkü bu özelliği static olarak işaretledik, diğer static olmayan özellikler gibi her yeni oluşturulan nesneye özel veri tutmaz. Sadece bir adet veriyi ihtiva eder.
  • __constructor methodu: Bu methodun görünürlüğünü(visibility) private olarak ayarladık. Bu sayede bu sınıfın, sınıfın kendisi dışında örneklenmesinin önüne geçiyoruz. Bildiğiniz gibi new methodu ile her yeni örneklenen sınıf otomatik olarak __constructor methodunu çalıştırır. Ama burada biz görünürlüğünü private olarak ayarladık yani bu sınıfı, sınıf dışında new methodu ile örneklemeye çalıştığımızda __constructor methodu çalışmak isteyecektir. Ancak __constructor methodunu private olarak ayarladığımız için sınıfın kendisi dışında yaratılamayacak ve PHP hata verecektir. Bu sayede sınıfımızın dışarıdan örneklenmesinin önüne geçiyoruz ve sınıfımızdan oluşan nesnenin bir adet ile sınırla kalmasını sağlıyoruz.
  • getInstance methodu: Zurnanın zırt dediği yere geldik. Gördüğünüz gibi 3 satırdan oluşan çok basit bir method. İçerisine bakacak olursak. if ile $_instance özelliğinin null olup olmadığını kontrol ediyoruz. Eğer null ise, içine yeni bir User nesnesi örnekliyoruz. Daha sonra $_instance özelliğini return ile döndürüyoruz. Fark ettiyseniz bu methodu da static ile işaretledik. Bu sayede bu sınıfı örneklemeden direk olarak bu methoda ulaşıp, kullanabiliyoruz.

İşin mantığına gelirsek. Aslında çok karmaşık bir şey ile karşılamıyoruz. $_instace özelliğini static olarak işaretleyip atayacağımız değeri tüm uygulama boyunca saklıyoruz. Haliyle bu özelliğe nesnemizin kendisini atayınca tüm uygulama boyunca aynı nesneye $_instance üzerinden ulaşmış oluyoruz. Kısaca $_instance’a ilk atanan User nesnesi tüm uygulama boyunca aynı yani ilk atadığımız User nesnesi.

Singleton kullanımına bakalım

Bootstrap sınıfımızın son hali

namespace Singleton;
class Bootstrap
{
    public function __construct()
    {
        $user = \Singleton\User::getInstance();
        $user->firstName = 'Can';
        $user->lastName = 'Aydoğan';
    }
}

Foo Sınıfımızın son hali

namespace Singleton;
class Foo
{
    public function __construct()
    {
        $user = \Singleton\User::getInstance();

        //Kullanıcının ad ve soyad bilgilerini ekrana yaziyoruz.
        echo $user->getFullName();
    }
}

Sınıfları Kullanalım

require_once 'Bootstrap.php';
require_once 'Foo.php';
$bootstrap = new \Singleton\Bootstrap();
$foo = new \Singleton\Foo();

Ve ekranda artık “Can Aydoğan” yazısını görebiliyoruz. Bundan sonra \Singleton\User::getInstance(); kullanarak örneklediğimiz nesneye herhangi bir sınıftan ulaşabiliriz. Her seferinde ulaştığımız nesne ilk oluşan nesnedir. Yani uygulamamızın her noktasında aynı User nesnesine ulaşacağız. İhtiyacımız da tam olarak buydu.

Sonuç

Singleton tasarım şablonun bir sınıftan sadece bir tane üretmek gerektiğinde ve bu üretilen nesneye uygulamamızın her yerinden ulaşmak istediğimiz durumlarda kullanabiliriz.

Ancak günümüz yazılım standartları içerisinde Singleton tasarım şablonun test edilmesi zor olan yapısı nedeniyle çok fazla kullanılması önerilmez. Örneğin Zend Framework 2. sürümü ile çoğunlukla Singleton ihtiyacını ServiceLocator tasarım şablonu ile karşılamaktadır.

Bu yazımda kullandığım örneğe buradan ulaşabilirsiniz.



Yorum Yap