“Pratik, iyi bir noktaya geldikten sonra yaptığımız bir şey değildir. Sizi iyi bir noktaya getirmesi için yaptığımız bir şeydir.”
by Malcolm Gladwell Outliers S. 36
Tarih: Mart 10th, 2013 | Yazar: admin | Kategoriler: Komut Satırı | Etiketler: *.nix, cygwin, komutlar, linux | 1 Yorum »
Bu yazımda, buradaki yazıyı The Command Line is Your Best Friend okurken çıkardığım notları paylaşmak istiyorum. (Özet severlerin hoşuna gideceğini düşünerek
)
Kendim için not aldığım için, çok fazla detayları katmadım. Ama birilerine faydası dokunur diyerek paylaşıyorum.
Not 1: Yazıda geçen komut satırı *nix(Unix/Linux) ortamındaki komut satırıdır. Bu yüzden Windows ortamında bu komutları denemeniz için Cygwin uygulamasını indirebilirsiniz. Bu uygulama ile birçok BASH komutunu Windows işletim sistemi üzerinde çalıştırmanız mümkün.
Not 2: Bu yazıdakilere ek olarak, sizin de ekleyebileceğiniz kullanışlı komutlar veya püf noktalar varsa, yorum olarak paylaşırsanız sevinirim. Şimdiden teşekkürler.
Unix sistemlerde her şey bir dosyadır(file). Klavye, fare, ekran ve diğer her şey birer dosyadır.
Dosya türü ayraçları:
- 0 standart girdi(stdin)
- 1 standart çıktı(stdout)
- 2 standart hata(stderr)
Genel:
- Komut girilen satırın başında $ varsa kullanıcı root değil, # varsa ise kullanıcı root.
- ~ işareti o anki kullanıcıya ait home dizinini gösterir. Örnek; dizine gitmek için cd ~ kullanılır veya o dizindeki bir dosyayı açmak için: cd ~/dosyaismi
- kullaniciadi@computerhostname @’den önce kullanıcı adı sonrası ise bilgisayarın host ismi
- Herhangi bir değişkeni ulaşmak için $degiskenadı şeklinde kullanıyoruz.
- Bir değişkenin içerdiği değeri ekrana yazmak için echo $değişkenadi kullanıyoruz. Örnek: echo $PS1
- Dosya oluşturmak için touch dosyasına veya touch dizin/dosyaisimi de olabilir.
- mcedit dosyaadi şeklinde dosyaları düzenleyebilirsin. Programlama dili desteği olduğu için code renklendirmesi var. Editörden çıkmak için iki kez üst üste ESC basılarak çıkılıyor veya F10 ile. Aşağıda gözüken numaraların fonksiyon tuşlarındaki karşılıkları ile kullanabilirsin.
- nokta(.) işareti şu anki konumu ifade eder. iki nokta(..) işareti bir üst dizinin konumunu ifade eder. Örnek; şu anki konumdan alt bir dizine geçmek istiyorsak: cd ./altdizi
- cat dosyaismi komutu ile girilen dosya isminin içeriği okunabilir.
- grep “aranacak kelime” dizin/dosyaismi ile belirtilen dosyanın içeriği içinde arama yapılabilir. Ek olarak –color eklenerek gelen sonuçta eşleşen bölge renklendirilebilir. Örnek; grep “kelime” deneme.txt –color
- pipe: bu komut “|” işareti ile kullanılır. | işaretinden önceki komutun ürettiği çıktıyı | işaretinden sonraki komuta aktarır. Örnek; cat ./SecondDir/ThirdFile | grep “Another” veya ls | grep “deneme” –color Bu örnekte cat ile içeriğini okuduğumuz dosyanın çıkıtısı üzerinden, grep ile “deneme” kelimesini arıyoruz.
- sed: bu komut ile çıktı veya dosya içinde regular expression kullanarak arama yapılıyor. Örnek; cat ./SecondDir/ThirdFile | grep “Another” | sed -e “s/AnotherFile/MyFile/”
- [geçici]kendi komutunu tanımlama: alias komutismi=”çalıştırmak istediğim komutlar” Örnek: alias listele=”ls” Ancak bu yolla oluşturulan komutlar oturum süresince korunur. Oturumdan çıkıldığında(logout) bu komutlar silinir. Yani geçicidir.
- [kalıcı]kendi komutunu tanımlama: .bashrc veya .bashprofile dosyaların içerisine alias ile kendi komutumuzu kalıcı olarak tanımlayabiliyoruz. Bu iki dosya genelde kullanıcıya ait home klasöründe(~) bulunuyormuş.
- .dosyaismi gibi başında nokta(.) ile başlayan isimlendirmeler gizli dosyaları belirtmek için kullanılır.
- traceroute: bu komut ile yazacağımız hedefe giden yolda uğradığımız noktaları görebiliyoruz. Örnek; traceroute 8.8.8.8 veya traceroute www.google.com
- ifconfig: komutu ile ağ yapılandırmasını(network configuration) görebiliyoruz.
- netstat: komutu ile açık olan bağlantıları listeliyoruz. Örnek: netstat veya netstat -an
Yönlendirme(Redirection)
- İçeri yönlendirme <: bir komuta bir dosya yönlendirmek istiyorsak eğer, komut < dizin/dosyaismi şeklinde gönderebiliriz
- Dışarı yönlendirme >: bir komutun çıktısını bir dosyaya yazdırmak istiyorsak eğer, komut > dizin/dosyaismi şeklinde yazdırabiliriz. Örnek: bir klasörün içerisindeki dosya ve klasörleri bir dosyaya yazdırmak istiyorsak; ls > dosyaismi
Sadece hataları yönlendirmek istiyorsak eğer; ls 2> dosyaismi şeklinde veya hataları dışarıda bırakıp standard çıktıyı yönlendirmek istiyorsak eğer ls 2> dosyaismi olarak kullabiliriz. 2 yönlendirildiyse 1 ekranda yazar, 2 dosyaya gider. Yani kalan türler ekrana basılır. Bknz: Dosya türü ayraçları.
Türler arası da yönlendirme yapmak mümkün. Örneğin: stderr’i stdout’a doğru yönlendirebiliriz. Bu şekilde bir kullanımda 1(stdout) aynı zamand 2(stderr)’i de temsil eder. Örnek; find . -name *File 1>./SecondDir/ThirdFile 2>&1
Görmek istemediğimiz çıktıları /dev/null ‘a yönlendirebiliriz. /dev/null ‘a yönlendirilen herhangi bir şey yok olur, kayıt altına alınmaz. Örnek: wget -r -P /dev/null http://www.domain.com
Arama(Find)
- find aranacak klasörün yolu( nokta(.) ile şu anki klasörü iki nokta (..) ile bir üst klasörü belirtebiliriz) -name aranacak dosyanın tam adi deneme.txt gibi Örnek; find . -name deneme.txt veya find klasor -name deneme.txt
- find ile arama yaparken bilmediğimiz karakterler için ? ve * işaretlerini kullanabiliriz. ? bilinmeyen bir karakteri temsil ederken * karakteri birden fazla karakteri temsil eder. Örneğin; find . -name deneme.* veya find -name deneme.??? Üç kez soru işareti kullandık nedeni txt’nin 3 karakterli olması. Bu özel karakterler sadece dosya uzantısında değil dosya isminin kendisinde de kullanılabilir.
İzinler(Permissions)
- r okuma izni
- w yazma izni
- x çalıştırma(execute) izni
- d ise directory’i temsil ediyor olabilir.(Benim fikrim)
- - ise herhangi bir izin olmadığını temsil ediyor.
- chmod +rwx ./ThirdDir/ buradaki +‘nın anlamı izin ekledir. Bunun yerine - kullanılırsa izin çıkarılır.
- İzinlerin nümerik karşılıkları:
rwx: Each bit is set to 1: 111. Which in decimal is 7.
rw-: Is represented as 110. Which in decimal is 6.
r-x: Is represented as 101. Which in decimal is 5.
r--: Is represented as 100. Which in decimal is 4.
-wx: Is represented as 011. Which in decimal is 3.
-w-: Is represented as 010. Which in decimal is 2.
--x: Is represented as 001. Which in decimal is 1.
---: Is represented as 000. Which in decimal is 0.
Tarih: Aralık 16th, 2012 | Yazar: admin | Kategoriler: Dışlananlar | Etiketler: google chrome, uzantı | Yorum Yok »
Tarayıcıların yeteneklerini geliştirmek için uzantılara(eklentilere) başvururuz. Kimi zaman bazı uzantılar tarayıcı deneyimimiz oldukça verimli kılar. Ben de, bu yazımda kullandığım ve memnun kaldığım Google Chrome uzantılarını sizinle paylaşacağım.
- TransOver: Fare ile seçeceğimiz cümle veya kelimelerin bizim seçtiğimiz dile çeviriyor. Google Translate tabanlı çalışan bu uzantı özellikle kelime çevirisi bakımından oldukça başarılı. Seçilen kelimenin anlamlarını türüne göre(sıfat, zarf, isim…) gruplayarak anlamamızı kolaylaştırıyor.
- Evernote Web Clipper: Evernote‘un resmi Chrome uzantısı.(Evernote kullanmıyorsanız mutlaka denemenizi öneririm) Bu uzantının iki özelliği var. İlk olarak gezdiğiniz sayfalar hakkında not almanızı sağlıyor. İkinci ve benim en çok sevdiğim özelliği ise yaptığınız Google aramalarını aynı zamanda aldığınız notlar içerisinde de yapıyor. Örneğin: Google’da “elma” diye aradığınızda, uzantı ”elma” kelimesini notlarınız arasında da arıyor ve bir eşleşme çıkarsa Google’ın arama sonrası gelen sonuç sayfasında bu notları gösteriyor. Bu şekilde.
- Stylebot: Bazı web sayfalarının tasarımları hoşunuza gitmeyebilir veya gereksiz gördüğünüz parçaların(reklam, ilginizi çekmeyen ama oldukça yer kaplayan bileşenler…) sürekli önünüze çıkmasını istemeyebilirsiniz. Bu noktada Stylebot uzantısı yardımımıza koşuyor. Stylebot ile bir web sayfasını tasarımını kalıcı olarak değiştirebiliyoruz. Bu değişiklikleri yapmak için az da olsa CSS bilgisi gerekiyor. (Yaptığımız düzenlemeler sadece bizim tarayıcımızda çıkıyor
)
- Google Reader Open entry in background tab: İsmi oldukça uzun, ama yaptığı bir tane iş var. Google Reader’da okuduğumuz o anki yazıyı “v” tuşuna basarak arka planda yeni bir tab olarak açıyor. Arkaplan’da açtığı için Google Reader’daki diğer yazılara geri dönmeden devam edebiliyoruz.
- RSS Abonelik Uzantısı (Google’dan): Gezdiğimiz sayfaların varsa RSS’lerine tek tıkla abone olmamızı sağlıyor.
- Screen Capture (by Google): Adından da anlaşılacağı üzere ekran görüntüsünü kayıt edebiliyoruz bu uzantı ile. Hem tüm sayfayı, hem istediğimiz bir bölgenin görüntüsünü kolaylıkla kayıt edebiliyoruz. Ayrıca Picasa, Facebook, Imgur gibi servislere direkt olarak yakaladığımız görüntüyü göndermemizi sağlıyor.
- Clearly: Bir sayfadaki yazıyı, sayfada bulunan gereksiz şeylerden arındırıp, daha temiz bir okuma ekranı sağlıyor.
- XML Tree, JSONView: Bu iki eklenti ile XML veya JSON formatlarındaki verileri daha okunabilir yapabiliyoruz. Bu formatlar ile haşır neşir olan geliştiricilerin mutlaka işlerine yarayacaktır.
Sizin de kullanmaktan hoşlandığınız uzantılar varsa, yorum olarak paylaşırsanız sevinirim.
Tarih: Kasım 4th, 2012 | Yazar: admin | Kategoriler: Dışlananlar | Etiketler: google maps, konum, location, time zone, zaman dilimi | Yorum Yok »
Dünyanın tüm bölgelerine hitap eden bir web siteniz veya bir uygulamanız varsa, kullanıcıların içinde bulundukları saat dilimleri önemlidir. Bu duruma örnek vermek gerekirse; İngiltere’de bulunan bir üyeniz Türkiye’de bulunan bir üyeye sizin Amerika’da bulunan sunucunuz üzerinden mesaj attığını düşünürseniz, 3 farklı zaman dilimi ile uğraşmanız gerekmektedir. Yani İngiltere’den mesaj atan üyeye mesaj tarihini, İngiltere’nin zaman dilimine göre göstermeniz gerekir. Aynısı Türkiye’deki üye için de geçerlidir.
Kullanıcılara doğru tarihi göstermek için genelde zaman dilimini kullanıcıya sorarak öğreniriz. Bu yöntemi her zaman uygulayamayabiliriz veya daha pratik şekilde çözmek isteyebiliriz. Bu noktada bu yazının konusu olan, konuma göre zaman dilimi öğrenme çözümü derdimize derman olacaktır.
Google Maps API Web Servislerine yeni dahil olan The Google Time Zone API (Deneysel) ile konuma göre zaman dilimini öğrenebiliyoruz artık. Hem de oldukça kolay bir şekilde. Gelin bir bakalım…
İstekleri Yapacağımız URL:
https://maps.googleapis.com/maps/api/timezone/çıktı formatı(xml|json)?parametreler
İstekte Kullanacağımız Parametreler
- location: Zaman dilimini öğreneceğimiz konumun bilgisi. Enlem(latitude) ve boylam(longitude) bilgilerini araya bir virgül(,) koyarak kullanıyoruz. Örnek: 39.926562,32.817512
- timestamp: Zaman dilimini öğreneceğimiz konumun timestamp olarak zaman bilgisi. Yani, Ankara’nın şu anki zaman dilimini öğrenmek istiyorsak güncel timestamp bilgisini, bir ay önceki zaman dilimini öğrenmek istiyorsak bir ay önceki timestamp bilgisini girmeliyiz. Örnek: 1352052772
- sensor: Konum bilgisini GPS kullanarak öğrendiyseniz true değilse false
Yukarıdaki parametrelerin kullanılması zorunlu bunun dışında language parametresini kullanarak gelen cevabın hangi dilde üretilmesi gerektiğini belirleyebilirsiniz. Örnek: Türkçe için tr
Cevap Elementleri
- dstOffset: Bu bilgi bize, konumun ne kadar gün ışığından(yaz saati uygulaması) yararlandığını verir. Örnek: Türkiye’de yaz saati uygulandığı bir zamanı timestamp olarak gönderirsek sunucuya bu değer 3600(saniye cinsinden) olacaktır yani bir saat gün ışığından faydalandığımızı gösterir.
- rawOffset: Bu bilgi bize, konumun Eşgüdümlü Evrensel Zaman (UTC) ile olan farkını verir. Bu farkın içine gün ışığından yararlanma farkı dahil değildir. O yüzden konum için gün ışığından yararlanma söz konusu ise bu iki bilgiyi aynı anda değerlendirmekte fayda var.
- timeZoneId: Bu bilgi ile zaman dilimi ID’sini öğrenebiliriz. Örnek: Türkiye için Europe/Istanbul değeri gelecektir. Küçük bir not: Bu element üzerinden gelecek bilgi PHP’deki zaman dilimi ID’leri ile aynı. Dönüştürme işlemi olmadan direkt kullanbilirsiniz.
- timeZoneName: timeZoneId bilgisi teknik tarafta işe yarayacaksa bu bilgi de kullanıcı tarafında işe yarayabilir. Zaman dilimi bilgisini kullanıcının anlayacağı şekilde bu alan üzerinden öğrenebiliriz. Örnek: Türkiye için gelen değer bu şekilde olacaktır; Doğu Avrupa Standart Saati
Örnekler
Konum Ankara, Tarih: 4 Kasım 2012, Saat: 21:18:32 için Örnek Bir İstek ve Cevap
İstek:
https://maps.googleapis.com/maps/api/timezone/json?location=39.92077,32.85411&sensor=true×tamp=1352056712&language=tr
Cevap:
{
"dstOffset" : 0.0,
"rawOffset" : 7200.0,
"status" : "OK",
"timeZoneId" : "Europe/Istanbul",
"timeZoneName" : "Doğu Avrupa Standart Saati"
}
Konum Ankara, Tarih: 12 Temmuz 2012, Saat: 03:31:52(yaz saati uygulanan bir zaman) için Örnek Bir İstek ve Cevap
İstek:
https://maps.googleapis.com/maps/api/timezone/json?location=39.92077,32.85411&sensor=true×tamp=1342056712&language=tr
Cevap:
{
"dstOffset" : 3600.0,
"rawOffset" : 7200.0,
"status" : "OK",
"timeZoneId" : "Europe/Istanbul",
"timeZoneName" : "Doğu Avrupa Yaz Saati"
}
Bonus
- Konumu(enlem ve boylam) adres bilgisi üzerinden öğrenmek için bu yazımı okuyabilirsiniz.
- Konumu Geolocation üzerinden öğrenmek için bu yazımı okuyabilirsiniz.
- Konuyla ilgili Google Maps’in dökümanına buradan ulaşabilirsiniz.
Tarih: Ekim 29th, 2012 | Yazar: admin | Kategoriler: Tasarım Şablonları (Design Patterns) | Etiketler: behavioral design pattern, davranışsal tasarım şablonu | 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.
Tarih: Ekim 14th, 2012 | Yazar: admin | 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.