“Measuring programming progress by lines of code is like measuring aircraft building progress by weight.”
by Bill Gates

Strategy Design Pattern(Strateji Tasarım Şablonu)

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

Eminim bu yazıyı okuyanlar en az bir tane Shooter(Silah kullanılan oyunlar. FPS’ler TPS’ler…) oyunu oynamıştır. Shooter oyunlarının temeli düşmanlar, silah ve bu silahı kullanan karakter üzerine kurulmuştur. Bu üç öge olmadan bir shooter oyunu düşünülemez. Gelin bizde bir shooter oyununu PHP tarafında modelleyelim.

Character sınıfımız

namespace Strategy;
require_once 'Weapon.php';
class Character
{
    public $name;

    public function attack($weapon, Character $target)
    {
        switch ($weapon) {
            case 'machineGun':
                printf("'%s' makinalı silah ile vuruldu", $character->name);
                break;
            case 'revolver':
                printf("'%s' tabanca ile vuruldu", $character->name);
                break;
        }
    }
}

Bir kişiye makinalı silah ve tabanca kullanarak ateş edelim.

require_once 'Character.php';

$hedef = new \Strategy\Character();
$hedef->name = 'Emma Richardson';

$katil = new \Strategy\Character();
$katil->name = 'Albert Fish';

$katil->attack('machineGun', $hedef);
$katil->attack('revolver', $hedef);

Ekranımızda aşağıdakiler yazı.

'Emma Richardson' makineli silah ile vuruldu
'Emma Richardson' tabanca ile vuruldu

Yukarıdaki örnekte iki tane Character sınıfından örnekleme yaptık bunlardan biri $hedef isimli değişkene, diğerini $katil isimli değişkene atadık. $katil içinde bulunan karakterimiz $hedef içinde bulunan karaktere attack metodu ile saldırıyor ve verdiğimiz silah bilgisi ile hedefe ateş ediyor.

Yukarıdaki örneği prensipler bakımından inceleyelim.

  • attack metoduna bakarsak, switch yapısını fark edebiliriz. Her bir silah için ayrı ayrı case tanımlaması yaptık ki silah türlerini ayırt edebilelim. Bu yüzden her yeni silah için, yeni bir case eklememiz gerekiyor. Bu da open-closed(geliştirmeye açık-değiştirmeye kapalı) prensibine uymayan bir durum. Sisteme ekleyeceğimiz her bir silah için kullanıcı sınıfı olan Character sınıfında değişiklikler yapmamızı gerektirir. Bu yapı da zaman içerisinde uygulamamızı kırılgan bir duruma sokacaktır. (Gerçek hayattaki örnekler çoğunlukla daha karmaşık olur. Bunu aklımızda tutmamızda fayda var.)
  • Character sınıfımız olması gerekenden daha fazla sorumluluğa sahip. Yine attack metoduna bakarsak, sınıfın silah türlerini ve silahlar hakkında bilgiler içerdiğini görebiliriz. Oysa single responsibility(tek sorumluluk) prensibi bize her sınıfım bir tane sorumluğu olması gerektiğini söyler. Ancak biz Character sınıfına silahlar hakkında bilgi ekleyerek sınıfın sorumluluklarına birden ikiye çıkardık. Bu hali ile Character sınıfı hem bir karakteri, hem de silahları temsil ediyor.

Peki, single responsibility ve open-closed prensiplerini bozmadan uygulamamızı nasıl geliştirebiliriz? Bunu cevabı yazımızın konusu olan Strateji tasarım şablonunda.

Strateji tasarım şablonunu uyguladıktan sonra sınıflarımızın son hali aşağıdaki şekilde olacaktır.

Weapon arayüzü

namespace Strategy;
interface Weapon
{
    public function fire(\Strategy\Character $character);
}

MachineGun sınıfı

namespace Strategy;
require_once 'Weapon.php';
use Strategy\Weapon;
class MachineGun implements Weapon
{
    public function fire(\Strategy\Character $character)
    {
        printf("'%s' makineli silah ile vuruldu", $character->name);
    }
}

Revolver sınıfı

namespace Strategy;
require_once 'Weapon.php';
use Strategy\Weapon;
class Revolver implements Weapon
{
    public function fire(\Strategy\Character $character)
    {
        printf("'%s' tabanca ile vuruldu", $character->name);
    }
}

Character sınıfı

namespace Strategy;
require_once 'Weapon.php';
class Character
{
    public $name;

    public function attack(\Strategy\Weapon $weapon, Character $target)
    {
        $weapon->fire($target);
    }
}

Character sınıfındaki değişikliklere gelmeden önce isterseniz yeni eklenen sınıflara ve arayüzlere bakalım. Öncelikle Weapon adında yeni bir arayüz(interface) ekledik. Bu arayüzün koşullarını yerine getiren(implement) her bir sınıf Weapon özelliği kazanacaktır. Örneğimizde is Weapon arayüzü kullanan sınıflardan fire metodunu mutlaka barındırmalarını istedik. MachineGun ve Revolver sınıfına bakarsanız bu isteğin karşılık bulduğunu görebilirsiniz.

Character sınıfına gelirsek, oradaki attack metodunda bazı değişikler yaptık. Öncelikle metodun $weapon argümanın başına \Strategy\Weapon eklendiğini fark etmişsinizdir. Bunu ekleyerek $weapon argümanı için gönderilen içeriğin mutlaka Weapon arayüzüne sahip olmasını istedik. (Gönderilen içerik Weapon arayüzüne sahip değilse PHP fatal error verecektir.) Diğer yandan yaptığımız tanımlama ile $weapon için gönderilen sınıfın hangi metotları ile iletişim kuracağımızı kabul etmiş sayıldık. Biz, bundan sonra sadece Weapon arayüzünde tanımlı olan metotları attack metodunda kullanacağız. Yani bu örnekte sadece fire metodunu kullanacağız. Çünkü Weapon arayüzünde sadece o tanımlı. Burada şunu belirtmekte fayda var. Arayüz dışındaki metotlara da erişebiliriz ama bu önerilen bir yöntem değildir. Çünkü arayüzde bulunmayan metotların olup olmadığı veya nasıl çalıştığının bir garantisi yok. Oysa arayüz içinde bulunan metotlarda, bu garanti arayüz tarafından sağlanıyor.

Ayrıcı attack metodun içinde bulunan switch yapısını tamamen kaldırdık. Bunun yerine Weapon arayüzünün fire metodunu kullanarak hedefe ateş ediyoruz.

Character sınıfında yaptığımız son değişiklikler sayesinde single responsibility prensibini artık ihlal etmiyoruz. Çünkü Character sınıfının silah sorumluğunu ondan alıp Weapon arayüzü üzerinden MachineGun ve Revolver sınıfına devrettik. Character sınıfın şu anda tek bir sorumluluğu var o da; karaktere ait bilgileri ihtiva etmek.

Son yaptığımız değişiklikler ile beraber kodumuzu çalıştıralım.

require_once 'Character.php';
require_once 'MachineGun.php';
require_once 'Revolver.php';

$hedef = new \Strategy\Character();
$hedef->name = 'Emma Richardson';

$katil = new \Strategy\Character();
$katil->name = 'Albert Fish';

$machineGun = new \Strategy\MachineGun();
$revolver = new \Strategy\Revolver();

$katil->attack($machineGun, $hedef);
$katil->attack($revolver, $hedef);

Çıktımız

'Emma Richardson' makineli silah ile vuruldu
'Emma Richardson' tabanca ile vuruldu

Her şey yolunda gözüküyor. Yukarıda bahsettiğim open-closed prensibine kodumuz uyuyor mu peki? Bunu yeni bir silah ekleyerek test edebiliriz. Bu silah da sniper olsun.

Sniper sınıfı

namespace Strategy;
require_once 'Weapon.php';
use Strategy\Weapon;
class Sniper implements Weapon
{
    public function fire(\Strategy\Character $character)
    {
        printf("'%s' sniper ile vuruldu", $character->name);
    }
}

Yeni eklediğimiz Sniper sınıfı ile beraber tüm silahları kullanarak hedefimize saldıralım.

require_once 'Character.php'; 
require_once 'MachineGun.php'; 
require_once 'Revolver.php'; 
require_once 'Sniper.php'; 
$hedef = new \Strategy\Character(); 
$hedef->name = 'Emma Richardson';

$katil = new \Strategy\Character();
$katil->name = 'Albert Fish';

$machineGun = new \Strategy\MachineGun();
$revolver = new \Strategy\Revolver();
$sniper = new \Strategy\Sniper();

$katil->attack($machineGun, $hedef);
$katil->attack($revolver, $hedef);
$katil->attack($sniper, $hedef);

Çıktı

'Emma Richardson' makineli silah ile vuruldu
'Emma Richardson' tabanca ile vuruldu
'Emma Richardson' sniper ile vuruldu

Evet! Strateji tasarım şablonu sayesinde kullanıcı(Character) sınıfımızda herhangi bir değişiklik yapmadan uygulamamıza kolayca yeni bir silah ekledik. (Değiştirme yapmadan geliştirme yaptık.) Open-closed prensip ihlalini de bu sayede ortadan kaldırdık.

Örneğimizi Strateji tasarım şablonu çerçevesinde incelersek;

  • Weapon arayüzüne sahip olan her bir sınıf birer stratejidir.
  • Her bir strateji kendine has algoritmalara sahiptir. Bu örnek için Revolver tek tek atış ederken, MachineGun arka arkaya ateş ettiğini düşünebiliriz.
  • Stratejimiz değiştiğinde, uygulamamızın davranışları da değişecektir. Bu da strateji tasarım şablonunu davranışsal tasarım şablonu(behavioral design pattern) yapmaktadır. Kullandığımız örnekte her bir silahın kendine has atışı olduğuna göre uygulamamız aynı amaç için birden fazla ve birbirinden farklı davranışlara sahip olmuş demektir.

Sonuç

Geliştirdiğimiz uygulamada bir amacı(yukarıdaki örnekte attack) birden fazla yöntem(MachineGun, Revolver…) kullanarak uygulamamıza farklı davranışlar kazandırmak istiyorsak veya sınıfların birbiri hakkında daha az bilgiye sahip olmasını istiyorsak Strateji Tasarım Şablonunu kullanabiliriz.

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


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.


Factory Design Pattern(Fabrika Tasarım Şablonu)

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

Tasarım şablonları yazı dizisine tasarım şablonlarının bana ne ifade ettiğini anlatarak bir önceki yazımda giriş yapmıştım. Bu yazımda ise Oluşturucu Tasarım Şablonlarının(Creational Design Patterns) bir üyesi olan Factory Design Pattern(Fabrika Tasarım Şablonu) ile devam edeceğim.

Onlarca uçağım var. Boeing, Airbus, Cessna marka, onlarca boy boy, çeşit çeşit uçağım var. Tüm bu uçakları da kendim üretiyorum. Bir fabrikam yok, nasıl üretileceğini aklımda tutuyorum yeri gelince aklımda tuttuğum bu şeyleri kullanarak istediğim uçağı üretebiliyorum. Hemen şimdi size bir tane Boeing 747-8 üreteyim.

Önce sınıflarımıza bakalım;

namespace DesignPatters\Factory;

abstract class AbstractAircraft
{
    public $cruisingSpeed;
    public $model;
}
class Boeing extends AbstractAircraft {}
class Airbus extends AbstractAircraft {}
class Cessna extends AbstractAircraft {}

Ve üretim…

$aircraft = new \DesignPatters\Factory\Boeing();
$aircraft->model = '747-8';
$aircraft->cruisingSpeed = 917;

Artık Boeing 747-8 modelinde bir uçağımız var. Bu uçağı Türk Hava Yollarına hediye ediyorum. Malum ellerinde hiç jumbo jet yok benden armağan olsun. THY uçağı İstanbul Atatürk Havalimanı – Ankara Esenboğa Havalimanı arasında kullanıyor.
THY Boeing 747-8’i çok beğenmiş bir tane daha istiyor. Tabii diyorum ve hemen bir tane daha üretiyorum.

$aircraft = new \DesignPatters\Factory\Boeing();
$aircraft->model = '747-8';
$aircraft->cruisingSpeed = 917;

Bu uçağıda İstanbul Atatürk Havalimanı – John F. Kennedy International Havalimanı arasında kullanmaya başlıyorlar.

İşler çok iyi gidiyor. Her gün yeni bir uçak talebi geliyor çeşitli havayolu şirketlerinden. 3 ay sonra 100 küsur tane uçak üretip vermişim havayolu şirketlerine. Bir gün THY’den telefon geliyor bu uçakların yolcu sayısı bilgisi yok diyor telefondaki görevli. Hemen ekleyelim diye de ekliyor. Tamam diyorum ben de hiç tereddüt etmeden. Niye tereddüt edeyim ki AbstractAircraft sınıfına passengers adında yeni bir property(özellik) eklerim olur biter. Ekliyorum da..

abstract class AbstractAircraft
{
    public $cruisingSpeed;
    public $model;
    public $passengers;
}

Düşündüğüm gibi ilk aşama oldukça kolay oldu. Şimdi tüm ürettiğim uçaklara yolcu sayısını girmem gerekiyor yani ürettiğim 100 küsur uçağa tek tek kendilerine ait yolcu sayısı bilgisini girmem gerekiyor…

Offf! Ben 100’den fazla yerde ayrı ayrı uçak üretimi yapmışım. Şimdi bunların hepsine yolcu sayısı bilgisi eklemem için bu yerleri tek tek bulup düzenlemem gerekiyor. Akılsız başın cezasını parmaklar çekiyor ve ben 100’den fazla üretim yaptığım yeri bulup yolcu sayısı verisini eklemeye başlıyorum. Umarım herhangi birinde hata yapmam. Yapacağım iş basit olsa bile herhangi birini unutmam oldukça muhtemel çünkü 100’den fazla uçak üretim yerini tespit edip düzenleme yapmam gerekiyor. Birini unutmam çok büyük sorunlara neden olabilir 🙁

Başarısız bir programcılık hikayesi ile başlamak istedim bu yazıma. Peki yukarıda hikayenin öznesi olmak istemiyorsak ne yapmalıyız? İşte bunun cevabı Fabrika Tasarım Şablonun’da gizli.

Şablonumuzun ismi Fabrika. Gerçek dünyada ki bir fabrikayı düşünelim. Fabrikaya bir emir gönderilir. Bu emir şu şekilde olabilir; Bana Boeing’in 747-8 modelinden bir uçak üret. Fabrika bu talebi değerlendirir ve üretimi Boeing 747-8’e göre gerçekleştirir.

O zaman gerçek dünyada ki fabrikayı yazılım dünyasına uyarlayalım…

class Factory
{
    public static function create($model)
    {
        switch ($model) {
            case 'Boeing 747-8':
                $aircraft = new Boeing();
                $aircraft->model = '747-8';
                $aircraft->cruisingSpeed = 917;
                break;
            case 'Airbus a380':
                $aircraft = new Airbus();
                $aircraft->model = 'a380';
                $aircraft->cruisingSpeed = 945;
                break;
            default:
                throw new \RuntimeException('Undefined aircraft');
        }
        return $aircraft;
    }
}

Fabrikamızı çalıştırıp üretimi gerçekleştirelim.

$aircraft = \DesignPatters\Factory\Factory::create('Boeing 747-8');

Gördüğünüz gibi artık bir uçak üretirken o uçağın cruising speed değerini, model numarasını bilmemize gerek kalmadı. Bunları bizim adımıza bilip kullanan biri var o da uçak fabrikamız.

Yukarıdaki başarısızlık hikayesinde Fabrika Tasarım Şablonu kullandığımızı düşünelim. Yine 100 küsur uçak ürettik farklı yerlerde bunları kullandık. Ama bu sefer bu uçakların üretimini fabrikamız üzerinden gerçekleştirdik. Ve yine THY bizden uçaklara yolcu sayısı bilgisini eklememizi istedi. Hemen fabrikamızda bu isteği gerçekleştirdik.

class Factory
{
    public static function create($model)
    {
        switch ($model) {
            case 'Boeing 747-8':
                $aircraft = new Boeing();
                $aircraft->model = '747-8';
                $aircraft->cruisingSpeed = 917;
                $aircraft->passengers = 500;
                break;
            case 'Airbus a380':
                $aircraft = new Airbus();
                $aircraft->model = 'a380';
                $aircraft->cruisingSpeed = 945;
                $aircraft->passengers = 853;
                break;
            default:
                throw new \RuntimeException('Undefined aircraft');
        }
        return $aircraft;
    }

}

Fabrikamızın son hali bu şekilde. Artık 100’den fazla uçağımızın hepsi yolcu sayısını biliyor. Ve bu işlemi yaparken sadece bir tek yerde düzenleme yaptık. Sadece fabrikamızda.

Gördüğünüz gibi fabrika tasarım şablonunu kullanarak daha sonra yapacağımız düzenlemeleri kolaylıkla uygulayabiliyoruz.

Fabrika Tasarım Şablonunu Kullanmanın Bize Getirileri;

  • Fabrika tasarım şablonu ile kod tekrarını(duplication of code) önüne geçiyoruz. (Yukarıdaki örnekte her üretimde yolcu sayısını tek tek girmemiş olduk)
  • Fabrika tasarım şablonu ile nesneleri kullanan kullanıcılar, nesneler hakkında çok daha az bilgiye ihtiyaç duyuyor. (Yukarıdaki örnekte uçağı kullanan her bir kullanıcı artık o uçağın yolcu sayısını bilmek zorunda değil)
  • Fabrika tasarım şablonu ile çok daha esnek bir yapı elde etmiş oluruz.

Bu yazımda kullandığım örneğin kodlarına buradan ulaşabilirsiniz.


Tasarım Şablonları (Design Patterns)

Tarih: Ocak 8th, 2012 | Yazar: | Kategoriler: Tasarım Şablonları (Design Patterns) | Etiketler: , , , | 2 Yorum »

Çok uzun süredir(4 şubat 2011’den bu yana) bloguma bir şeyler yazamadım.  Üzerine yazı yazacak konu bulma sıkıntısı ve zaman darlığından dolayı 1 yılı yakın süredir yeni bir yazı giremedim. Bu nedenle Tasarım Şablonları adlı bir yazı dizisi yazmaya karar verdim. Bu yazıyı da, bu yazı dizisine girizgah olarak yazmaya karar verdim.

Herhangi bir kavramın ne ifade ettiğini anlamak için en çok kullandığım teknik, o kavrama verilen isimde geçen kelimelerin anlamlarına bakmak olur benim için. Bu yazımda anlatmaya çalışacağım Tasarım Şablonları(Design Patterns) konusunda da bu teknik ile giriş yapacağım.

TDK’ya göre “Tasarım”;

isim Zihinde canlandırılan biçim, tasavvur

Tasarım kavramına gerçek hayattan basit bir örnek vererek başlayalım. 2 adet bardağı uzay boşluğunda herhangi bir konuma yerleştirsek bu bir tasarım olur.(Sadece 2 adet bardak olmasına rağmen sonsuz sayıda kombinasyon olabilir. Bu yüzden ortaya bir tasarım koymak aslında oluşan tasarımlar arasında herhangi birini seçmekten de geçiyor.) Programlamada düşünürsek eğer, sınıfların iç yapısı, sınıfların konumu, sınıfların ve bileşenlerinin çalışma şekilleri, yine sınıfların ve bileşenlerin birbirleriyle olan ilişkilerinin nasıl olacağı gibi konular birer tasarım konularıdır. Yani “Nasıl?” sorusunun cevabı yapacağımız tasarımdır.

Yine TDK’ya göre “Şablon”;

isim Üzerindeki harf ve şekillerin çevre çizgileri kalem ucu girecek biçimde oyuk olan, bu çizgilerden kalemle istenilen biçim elde edilen, metal veya plastikten cetvel

mecaz Çok kez tekrarlandığından kanıksanmış basmakalıp örnek

 Hala kullanılıyor mu bilmiyorum ama benim ilkokulu okuduğum yıllarda solda bulunan cetvel öğrencilerin olmazsa olmazlarından biriydi.

 

 

Bu şablon cetveli doğuş amacını düşünelim. Daire çizme ihtiyacımız var. Düzgün daire çizmek de haliyle biraz zor. Ve sık sık daire çizmemiz gerekiyor. O zaman şu şekilde diyebiliriz; ihtiyacımız net bir şekilde ifade edebiliyorsak ve çok fazla değişikliğe açık bir ihtiyaç değilse, neden bir kalıp kullanarak ihtiyacımızı karşılamayalım. Bu noktada oluşturduğumuz şablonlar ile sık sık doğan bir ihtiyacımızı kolaylıkla gidermek mümkün.

O zaman tasarım ve şablon kavramlarını birleştirelim. Bir ihtiyacı karşılamak için bir tasarıma ihtiyacımız var. Bu ihtiyaç çok yeni ve bize özel bir ihtiyaç değil. Daha evvelde birileri bu ihtiyaca sahip olmuş ve bu ihtiyacı tasarım geliştirerek gidermişler. Hatta tasarımları o kadar iyi olmuş ki herkes tarafından bilinir ve kullanılır olmuş. Bu tasarımlara genel olarak Tasarım Şablonları adını veriyoruz.

Yukarıda genel olarak gerçek hayattan, yazılıma özel teknik kavramlar yerine, gündelik hayattan örnekler vererek tasarım şablonları konusuna eğilmeye çalıştım. İsterseniz işin biraz da yazılım boyutuna bakalım.

Yazılım geliştirici olarak programlama süreci boyunca geliştirdiğimiz uygulamanın ihtiyaçlarını gidermemiz en büyük görevimizdir. Bu ihtiyaçları giderirken genelde her adıma karşımıza çıkan Nasıl? sorusunu çözmek ile uğraşırız. Çoğunlukla birden fazla alternatif üreterek bu alternatifler arasından en uygununu bulmaya uğraşırız. Bu genelde sancılı ve sıkıntılı bir süreçtir. Çünkü oluşturduğumuz tasarım ilerde doğacak yeni ihtiyaçlara ne kadar karşılayacağını kestirmemiz oldukça zordur. Bu noktada diğer yazılım geliştiricilerin tecrübelerinden faydalanabiliriz. Yani bizim kendi tasarımlarımız yerine, başkalarının geliştirdiği tasarımlar ile sorunumuzu çözebiliriz.

Programcılar tarafından ihtiyaçları karşılamak üzere geliştirilmiş ve tekrar kullanılabilir yapıda olan program parçalarına Tasarım Şablonu denir.

Tasarım Şablonu Kategorileri ve Tasarım Şablonları

  1. Oluşturucu tasarım şablonları (Creational patterns) bunlar; Abstract Factory, Builder, Factory, Prototype, Singleton tasarım şablonları.
  2. Yapısal tasarım şablonları(Structual patterns) bunlar; Adapter, Bridge, Facede, Decorator, Composite, Flyweight, Proxy tasarım şablonları.
  3. Davranışsal tasarım şablonları(Behavioral patterns) bunlar; Command, Memento, Strategy, Iterator, State, Chain of Responsibility, Mediator, Observer, Template Method, Visitor tasarım şablonları.

Yukarıda yazdıklarım dışında birçok tasarım şablonu daha var. Ancak ben genel olarak bilinenleri yazdım.

Tasarım şablonunu kullanmak bize ne sağlar?

  • Tasarım şablonları edinilen tecrübeler doğrultusunda oluştuğu için genellikle bizim o an oluşturduğumuz tasarımlardan daha iyi olacaktır. Gelecekte doğabilecek yeni ihtiyaçlara daha iyi cevap verebilir.
  • Tekerleği yeniden icat etmek yerine, vaktimizi daha belirli ve bize özel ihtiyaçlarda kullanabiliriz.
  • Tekrar kullanılabilir yapıda oldukları için yine vaktimizi daha iyi değerlendirmemize sağlar.
  • Tasarımlar bir programcıya ait olmadığı için diğer programcıların tasarım anlaması çok daha kolay olur.
  • Tasarım şablonlarını bizim dışımızda başka birileri tarafından da kullanıldığı için sürekli birileri tarafından geliştirilir.

Sonuç

Tasarım şablonları ile doğan tasarım ihtiyaçlarımızı gidermemiz mümkün. O zaman genel kabul görmüş tasarım şablonlarını öğrenirsek ihtiyacımız olduğunda bize uygun olan tasarım şablonunu seçerek ihtiyacımızı giderebiliriz. Bu sayede hem vakitten kazanmış oluruz hem de birçok programcı tarafından kullanılan bir tasarımı uygulamış oluruz.

Tasarım şablonları yazı dizisinde kendimce önemli gördüğüm tasarım şablonlarını PHP kullanarak anlatmaya çalışacağım.

Okuduğunuz için teşekkürler. Diğer yazılarda görüşmek üzere…


Css ile Su Damlası Menüsü (Water Wave Menu)

Tarih: Şubat 4th, 2011 | Yazar: | Kategoriler: Css | Etiketler: | 4 Yorum »

Geçen gün cep telefonumda Need for Speed Hot Pursuit oynarken navigasyon menüsü dikkatimi çekti. Menüdeki herhangi bir butona tıkladığımızda bir su birikintisine, bir damla düşmüş gibi arkasında dalgalar bırakıyordu. Bu şekilde bir menüyü acaba sadece CSS kullanılarak yapılmış mıdır diye kısa bir araştırma yaptım ama herhangi bir sonuca ulaşamadım.

Yapılmış herhangi bir örnek bulamayınca kendim yapmaya karar verdim.

Menüyü şu adımları izleyerek oluşturdum;

  • Öncelikle yüksek border-radius değerleri ile ile yuvarlak halkalar elde ettim.
  • En üste menü elamanın kendisini z-index değerlerini ayarlayarak yerleştirdim. Bunu altına ise dalga efektini verecek elementleri ilave ettim.
  • Son olarak mouse, menü elemanlarının üzerine geldiğinde CSS’in transition özelliğini kullanarak animasyon şeklinde alta kalan halkaları genişlettim. Tabi halkalar genişlerken bulundukları konumu kaybettikleri için aynı anda top ve left değerlerini de uygun bir şekilde yine CSS transition özelliği ile ayarladım.

Sadece Chrome ve Safari de çalışıyor. Buradan test edebilir buradan da indirebilirsiniz