ile

Hiç Bilmeyenler İçin Nesne Yönelimli Programlamaya Giriş-2

Sınıf

Bu yazı daha önce yazdığım Hiç Bilmeyenler İçin Nesne Yönelimli Programlamaya Giriş yazısının devamıdır. Önce onu okumanızı öneririm.

View story at Medium.com

Önceki yazıda, nesne yönelimli programlama mantığını anlamaya çalıştık. Özetlemek gerekirse kabaca şunları anlatmıştım;

  1. Nesne kelimesinin ne anlama geldiğini
  2. Felsefi, fiziksel, psikolojik ve dilbilimsel yönleri de olduğunu
  3. Programlamada nesne kavramının ne ifade ettiğini
  4. Neden nesne yönelimli programlama öğrenmemiz gerektiğini
  5. Nesnelerin birbirleriyle ilişkilerini

Şimdi kavramları anlatmaya ve örnekler vermeye devam edeceğim. Tüm yazılarımda olduğu gibi kavramları kuru kuruya ve salakça tanımlar vermek yerine, işe yarar, elle tutulur örneklerle mantığı okuyucunun kafasında oturtmaya çalışıyorum. Bazen yazılarımın dili laubali olabilir, ama yazının eğlenceli ve anlaşılabilir olması, bence ciddi olmasından daha avantajlı.

Ben çeşmeden kana kana su içmeyi çok severim mesela.

Hayatta herşeyi yaparken bir amacımız var, örneğin su içerken amacımız susuzluk hissimizi gidermek. Bu işi yaparken de belli başlı yöntemler kullanabiliriz. Örneğin;

  1. Bardağa çeşmeden su doldurabiliriz.
  2. Bakkaldan 0,5 litrelik su alabiliriz.
  3. Çeşmeye ağzımızı dayayıp lıkır lıkır buz gibi suyu içebiliriz.

Yani belli bir amacı gerçekleştirmek için, farklı yöntemler uygulayabiliriz. Bu işi yaparken, ilk durumda susuzluk hissimiz, atıyorum %100 iken, işlemi gerçekleştirdikten sonra susuzluk hissimiz %0’a iner. Bilgisayar programları yazarken de, yazdığımız programların, belli kullanıcılar için, belli amaçları gerçekleştirmesini isteriz. İşte, programlarımızı, nesnelerimiz yazarken yapmamız gereken ilk iş, amacımızın ne olduğuna karar vermek. İkincisi de bu programı kimin kullanacağı. Şu anda düşünme tasarlama aşamasındayız. Yani özetle, belli kullanıcılar var ve bu kullanıcıların gerçekleştirmek istediği şeyler, ulaşmak istedikleri sonuçlar var.

Üzümünü ye, bağını sorma (Information Hiding)

Çeşme örneğinde, oturtmamız gereken en önemli mantık şu: Suyu içen kişi suyun dağdan mı, pınardan mı, nehirden mi geldiğini önemsemez. Onun için önemli olan, suyun temizliği, soğukluğu gibi kendini doğrudan ilgilendiren konulardır. Kitaplarda çok sık kullanılan bir diğer örnek, araba kullanırken, motorun, katalitik konvertörün, alternatörün o anda ne yaptığı ile ilgilenmiyoruz. Araç kullanıcısı olarak bizim amacımız bir yerden bir yere gitmek, ve bunu aracın direksiyonunu, vitesini, pedallarını vb. kullanarak gerçekleştiriyoruz. İşte bu “üzümünü ye, bağını sorma” olayına nesne yönelimli programlamada “bilgi gizleme” veya ingilizcesiyle “information hiding” deniyor.

Dolayısıyla bir programı yazmaya başlarken önce programın amacına karar vereceğiz. İşi nasıl yapacağına sonra karar vereceğiz.

Genel olarak programlar

Günümüzde genel olarak yazılımlara baktığımızda, basitçe hepsinin listelerden oluştuğunu görüyoruz. Programlar, bu listelere yeni kayıt eklemek, kayıt güncellemek, silmek ve görüntüleme işlemleri yapıyorlar. En basitinden bir yapılacaklar listesi uygulamasına yapacağımız işleri, bir bloga uygulamasına makalelerimizi kaydederken, Youtube videoları, instagram resimleri, twitter 140 karakterlik mesajları ve facebook’da saçma sapan şeyleri kaydetmemizi sağlıyor. Yani bu programların temel olarak mantığı aynı. Bu temel amaç dışındaki herşey ekstra. Bu program örneğine CRUD, yani create (yarat), read (oku), update (güncelle), delete (sil) deniyor. Ayrıca bu programlarda, kayıtlar üzerinde arama, filtreleme, sıralama işlemleri yapabiliyoruz. Mesela GittiGidiyor, HepsiBurada veya Sahibinden gibi sitelerde, aradığımız ürünleri fiyatlarına ya da eklenme tarihlerine göre sıralayabiliyoruz.

Sözlük Uygulaması

Sözlük derken, ekşisözlük değil tabii. Yeni yazdığım Nesne Yönelimli Programlama kitabı için bir sözlük uygulamasına ihtiyacım var. Girdilerin eklenebildiği, aranabildiği, farklı farklı sözlükler oluşturabileceğim, basit bir uygulama yapmak istiyorum ki, kitabımın sonunda bu sözlüğe yer verebileyim. Bu örnek uygulama da CRUD mantığı ile çalışacak. Şimdi tek tek programla yapmak istediğim şeylere bakalım:

  1. Bir kullanıcı olarak farklı farklı sözlükler yaratmak istiyorum. Bu sözlükleri açıp, içindeki girdileri görmek istiyorum.
  2. Sözlüğe yeni bir girdi, yani başlık ve açıklama girmek istiyorum. (Şimdilik her başlıkta sadece tek bir açıklama metin alanı olacak.) (Create)
  3. Başlığı girdiğimde, açıklamayı görmek istiyorum. (Read)
  4. Bu girdileri listelemek istiyorum. (Her sayfada 10’ar tane olacak şekilde. Buna pagination deniyor.) Ayrıca listeyi istersem sıralamak istiyorum. Örneğin alfabetik olarak ya da ekleme tarihine göre. (Sort)
  5. Girdiyi hatalı girdiysem, güncellemek ya da silmek istiyorum. (Update, Delete)
  6. Arama yapmak istiyorum. (Hem başlıklarda hem de açıklama metninde) (Search)
  7. Sözlüğün Word dosyası olarak çıktısını almak istiyorum.

EkşiSözlükteki gibi farklı kullanıcıların benim sözlüğüme eklenti yapmalarını veya sözlüğümü görmelerini istemiyorum, ama bu sonraki konu. Şimdilik en temel ihtiyaçlara odaklanmak en doğrusu. Mesela ekşiSözlükteki gibi bir başlığa birden fazla girdi eklemek, birden fazla kullanıcının sözlüklere ekleme yapabilmesini sağlamak, girdileri sosyal medyada paylaşmak, etiket girmek veya bkz. ile bir başlıktan diğer bir başlığa link oluşturmak gibi özellikleri sonradan da geliştirebiliriz. Bunları işe başlar başlamaz, aa ilerde kullanılır diye yazmaya kalkarsak ayvayı yeriz ki bu da yazılımcıların yaptığı en büyük hatalardan biridir. (Buna featuritis veya feature creep deniyor.)

İstediklerimi, yani ihtiyaçlarımı (requirements) listeledikten sonra, yazacağım programın hangi sınıflardan oluşacağına karar vermeye geldi sıra. Bunu yaparken İhtiyaçlardaki isimlere bakmak, sınıf tasarımı yapmada verimli yöntemlerden bir tanesi. İsimlere tek tek bakalım:

  1. Sözlük (Yani bir başlık metni, mesela Felsefe Sözlüğü ya da Nesne Yönelimli Programlama sözlüğü gibi.)
  2. Başlık (Her başlığın şimdilik tek bir açıklama metni olacak)
  3. Kullanıcı (Yani ben.)

Peki bu nesneleri nasıl kullanacağım? Buna da tek tek karar vermek gerekiyor. Nesneleri iyi ayarlamanın en iyi yollarından biri, onların sorumluluklarını önceden iyi belirlemektir. Ayrıca aklımızda tutmamız gereken önemli bir konu da, her nesnenin bir adet sorumluluğa sahip olmasıdır ki buna Tek Sorumluluk Prensibi yani (Single Responsibility Principle) deniyor. Yani yazacağımız nesne tek bir işi düzgünce yapan, bağımsız bir program gibi olacak. İstersek bu nesneyi programdan koparıp başka bir programda kullanabileceğiz.

Nesne tek başına her boku yapmaya çalışmıycak arkadaşlar. Yani sözlük nesnesi, kullanıcıları yönetmiycek, şifrelerle ilgilenmiycek, kendi başına temiz temiz bağımsız şekilde çalışacak. Eğer bir nesne her boku tek başına yapıyorsa bu kötü tasarımdır ve buna Tanrı Nesnesi (God Object) denir, test edilmesi zordur, sırt pırt hata çıkarır. Uzak durun.

Yeri geldiğinde bu prensiplere de ayrıntısıyla değineceğim. Şurada konuyla ilgili güzel bir kaynak mevcut.

Beklentiler

Sınıflarımızdan beklentilerimizi başta belirlemek uygulama tasarımımızın en önemli kısmı. Yani Sözlük sınıfı ile ne yapacağız, nasıl kullanacağız, sözlük sınıfı bize hangi hizmetleri sunacak ona karar verdiğimiz anda, işimiz %90 oranında kolaylaşacak. Sözlük sınıfı ile yapabileceğimiz şeyler şimdilik şunlar olsun:

  1. Sözlük bir isimle oluşturmak. (construct)
  2. Bu adı değiştirmek yani güncellemek. (setTitle)
  3. Sözlüğe girdi eklemek. (addEntry)
  4. Tüm başlıkları görüntülemek. (getEntries)
  5. Başlıklar içinde arama yapmak. (search)
  6. Dönen başlıkları ada göre veya eklenme tarihine göre sıralamak. (sort)

Şimdilik basitçe sözlük sınıfı ile yapabileceğimiz işlemlerin bunlar olduğuna karar verdik.

Burada foksiyonlar, koşullar, döngüler değişkenler gibi temel programlama bilginiz olduğunu farzediyorum. Eğer bu konularda bilginiz yoksa, kitapçılardan Projelerle PHP 7 adlı kitabımı satın alabilirsiniz. Satın alma linki de şu: www.seckin.com.tr/kitap/911934237

Kodları yazarken ingilizce kullanmaya özen gösteriyorum. Özellikle değişken isimlerinizin, yorumlarınızın ingilizce olması önemli. İngilizce dünyada evrensel bir dil olduğundan ve genellikle açık kaynaklı yazılımlar geliştirdiğimden, kodlarımın Mozambik’te veya Çin’de öğrenmeye istekli bir kişi tarafından okunabiliyor olması önemli. İngilizce’nin önemi konusunda ayrı bir makale de yazacağım.

Önceden kurduğunuzu tahmin ettiğim webserver’in içinde dictionary isimli bir dizin oluşturalım. Bu dizinde Dictionary.php isimli bir dosya açalım (İlk harf özellikle büyük olsun ki dosyanın bir sınıf içerdiğini anlayalım) ve sınıfımızı içinde metodlar olmadan yazmaya başlayalım:

<?php

namespace
MidoriKocak;


class Dictionary
{
private $title;
private $entries;
}

Satır satır ilerleyelim. Namespace dediğimiz kavram, yazdığımız sınıfların, aynı isimdeki diğer sınıflarla karışmamasını sağlayan, PHP 5.3 versiyonunda gelen bir icat. Ben örneklerimde kendi ismimi kullanıyorum, fakat bu konuda farklı doğru pratikler mevcut. Bunları daha detaylı anlatacağım.

“class Dictionary” diyerek sınıfımızın adına karar verdikten sonra, {} süslü parantezler içinde sınıfımızın değişken ve metodlarını yazacağız. Burada $title ve $entries, sınıfımıza ait değişkenler. Değişken ve metodların başına private yazdığımızda, o değişken ve metodlara sadece o sınıf içerisinden erişebiliyoruz ki, başka sınıflar, benim sınıfıma ait değişkenlere kafalarına göre erişip değiştirmesinler. Değişkenler ayrıca public ve protected’da oluyorlar. Değişken public olduğunda, süslü parantezin dışından herhangi bir programcı değişkenin anlık değerini değiştirebilir ve sınıfımızın çalışmasını bok edebilir. Doğru bir pratik olarak bütün değişkenlerinizi, private başlatmaya alışmanız çok önemli. Protected kavramını daha sonra anlatacağım.

Getter, Setter metodları. (alıcı, düzenleyici)

E peki bütün değişkenler private olursa bunlara nasıl erişeceğiz? Getter ve Setter metodları ile. İsimlerinin illa getBilmemne olmasına gerek yok ama doğru ve oturmuş bir pratik olarak nesne değişkeninizin ismi ne ise, başına get yazmak iyidir. Yani başlığı dışarıdan almak için getTitle, düzenlemek için de setTitle public metodlarına ihtiyacımız var. Bu sayede örneğin, kullanıcının boş başlık oluşturmasını engelleyebiliriz, ya da başlık için bir karakter sınırı koyabiliriz ki, adam başlığa bir trilyon karakter girmeye çalışıp programımızı çökerttirmesin.

public function setTitle(string $title)
{
if (($title != "") && (strlen($title) <= 70)) {
$this->title = $title;
}
}

public function diyerek metodumuza dışarıdan erişilebileceğini belirttik. string $title diyerek, string değişkeninin string yani metin tipinde olmasını garantiledik. Buna “type hinting” yani tip ipucu deniyor. PHP 5 sürümünden beri var. İlk if bloğunda göreceğimiz şekilde değişkenin boş olmadığını ve uzunluğunun da en çok 70 karakterden oluşması gerektiğini söyledik.

Burada yaptığımız işleme VALIDATION yani doğrulama deniyor ki, kullanıcılardan girdi alan uygulamalar yazarken, güvenlik açısından çok dikkat etmemiz bir kavram. Tüm kullanıcı girdileri lanetlidir diye bir söz vardır. Programımızın düzgün çalışmasını, daha programı yazarken bu şekilde garanti altına alıyoruz. Bir de bazı pislik insanların kalkıp bu girdilere HTML, Javascript, SQL kodu sokuşturmalarına da engel olmalıyız. Bunun için de belirli güvenlik önlemleri var. Bu işleme de SANITIZATION yani temizleme deniyor. Bu konuya da programımız, internet formundan kullanıcı girdileri alacak hale geldiğinde derinlemesine girişeceğiz.

Bu tip küçük detaylara dikkat ettiğimizde, programımızın nispeten hatasız çalışmasını, başımızı ağrıtacak bug’lar ortaya çıkmamasını garanti etmiş oluyoruz.

Eğer sınıf değişkenimiz public $title; şeklinde belirtilmiş olsaydı, dışarıdan herhangi bir kimse bizim validation kurallarımızı sallamadan sınıfımızın değişkenine tecavüz edebilirdi ki bunu istemeyiz. Bu yüzden nesne yönelimli programlamayı öğrenirken, gerçek hayatta kullanılan pratiklere en baştan aşina olmanızı istiyorum.

$this->title = $title;

$this ifadesi, burada içinde bulunduğumuz sınıfa ait nesneyi ifade ediyor. “->” operatörüyle değişken eğer bir nesne ise, sahip olduğu değişkene erişebiliyoruz. Nesneyi programımızda takır takır kullanmaya başladığımızda size, sınıf ve nesne kavramı arasındaki farkı da anlatacağım. Devam edelim.

Peki $title değişkenimize nasıl erişeceğiz? Bunun için de getTitle() isimli bir metod yazacağız. Bu metodun tek görevi, sınıfta bulunan private $title değişkenini kullanıcıya döndürmek.

public function getTitle():string
{
return $this->title;
}

Metodu yazarken : işaretinden sonra gelen string kelimesine dikkat ettiniz mi? Bu PHP 7 ile gelen harika bir yenilik, “return type” yani dönüş tipi olarak adlandırılıyor. Bu da metodumuzun hangi değeri döndüreceğini önceden belirlememizi sağlıyor. Yani kazara başka bir tipte değişken döndürmeyeceğiz ki, programımızı kullanan kullanıcı string yani metin yerine int yani sayı görüp afallamasın, mavi ekran vermesin, bug arayıp kafasını duvarlara vurmasın.

Sınıfımızın son hali şu şekilde:

<?php

namespace
MidoriKocak;


class Dictionary
{
private $title;
private $entries;

public function setTitle(string $title)
{
if (($title != "") && (strlen($title) <= 70)) {
$this->title = $title;
}
}

public function getTitle():string
{
return $this->title;
}

}

Dikkat etmeniz gerek iki kavram şunlar:

  1. Sınıf: Bir nesnenin kalıbını belirler.
  2. Nesne: Sınıftan kullanıcı tarafından üretilir.

Yani bu ikisi birbirinin aynısı değilidir. Mesela Sınıf audi A8’in çizimleri ise, nesne, yollarda züppelik yapan, makas atan 34TT264 plakalı hıyar arabadır.

Peki kullanıcı bu sınıfı nasıl kullanacak? Aynı dizin altında app.php isimli bir dosya oluşturalım:

<?php

require_once 'Dictionary.php'
;
$dictionary = new MidoriKocakDictionary();
echo $dictionary->getTitle();

Require ifadesiyle aynı dizindeki Dictionary.php dosyasını çağırdık. Dosya çağırma işleminin 4 adet yöntemi var.

  1. require: Dosyayı çağırır, bulamazsa programın çalışmasını durdurur.
  2. require_once: Dosyayı bir sefer çağırır, yani daha önce herhangi bir yerde require veya require_once ifadesiyle aynı dosya çağırıldıysa tekrar çağırmaz. Dosya bulunamadıysa, programın çalışmasını durdurur.
  3. include: Dosyayı çağırır, bulamazsa HATA (Warning) mesajı verir ama programı çalıştırmaya devam eder. Eğer o dosyada tanımlanan bir metoda veya değişkene erişmeye çalışırsanız, hata alır bol bol nerde hata yaptım diye ağlarsınız.
  4. include_once: Dosyayı bir sefer çağırır, yani daha önce herhangi bir yerde include veya include_once ifadesiyle aynı dosya çağırıldıysa tekrar çağırmaz. Dosya bulunamadıysa, çalışır ve kafanızı duvarlara vurursunuz.

O yüzden şimdilik require_once kullanacağız. Daha sonra, composer ve autolader kullanmaya başladığımızda bunlara veda edeceğiz, ama onu composer bölümünde anlatacağım. Şimdilik takılmayın. https://getcomposer.org/

$dictionary isimli bir değişken oluşturduk ve sınıfımızın başında belirttiğimiz Namespace ismi ile sınıfımızı new ifadesiyle yarattık. Dosyayı daha önceden doğru dürüst kurmuş olduğunuzu tahmin ettiğim yerel webserver üzerinden 127.0.0.1/dictionary/app.php adresiyle çağıralım.

Şöyle ölümcül bir hatayla karşılaşmamız gerekiyor, hatayı göremiyorsanız, PHP hata gösterme ayarlarınız kapalıdır. Onu da bi zahmet php.ini içinden açmalısınız, detaya girmiyorum.

Ölümcül hata

Hata neden oldu? Çünkü, $title değişkenimizi set etmeden, yani ona bir değer vermeden istemeye kalktık. getTitle metodunun sonuna :string yazarak metin tipinde bir değişken döndürmesini emrettik ama herhangi bir değer atamadığımızdan null döndürmeye kalktı. Bu tanımlamayı yaptığımız için ileride olabilecek bir çok bug’ın önüne geçmiş olduk. Null değişkenin henüz tanımlı olmadığını ya da bilerek veya isteyerek null değerine eşit olduğunu gösteren “yok” anlamına gelen ifadedir. Bu null belasından neden kaçmamız gerektiğini detaylıca, en iyi pratikler bölümünde detaylıca yazacağım.

Önce app.php içinde setTitle yazarak bu olayı çözebilirdik ama zırt pırt bunu kontrol edemeyiz ve programımızın da hata vermesini önlemek istiyoruz peki ne yapacağız? Sihirli __construct() metodunu kullanacağız.

__construct() metodu

PHP’de bir metodun başında iki alt çizgi varsa, ve tanımlı sihirli metodlardan (magic methods) bir tanesiyse, PHP yorumlayıcısı metodu tanır, ve gerektiği zamanda kendisi çalıştırır. Construct, yani inşa et metodu da, biz new MidoriKocakDictionary() dediğimiz anda çalışacak olan metod. Yani bir sınıftan nesne yarattığımızda bu metod çalışacak. Şu şekilde sınıfımızın başına __construct metodunu ekleyelim:

<?php

namespace
MidoriKocak;


class Dictionary
{
private $title;
private $entries;

public function __construct(string $title)
{
$this->title = $title;
$this->entries = [];
}

public function setTitle(string $title)
{
if (($title != "") && (strlen($title) <= 70)) {
$this->title = $title;
}
}

public function getTitle():string
{
return $this->title;
}
}

Burada construct metodunda, parametre olarak string $title, yani metin tipinde başlık değişkeni tanımladı. $title parametresini kullanarak $this-title ifadesiyle nesnemizin $title değişkenini tanımladık. Null belasından kaçmak için de $entries değişkenimize boş bir dizi (array) ekledik. Daha sonra sözlüğe kaydedeceğimiz kelimeleri bu dizide tutacağız.

app.php’yi çalıştıralım ve ikinci ölümcül hatamızla karşılaşalım:

İkinci ölümcül hata

Hata neden oldu? Çünkü app.php’yi değiştirmedik. __constructor() metodu içinde metin tipinde bir parametre olması gerektiği ve biz new MidoriKocakDictionary() şeklinde çağırdığımızdan, PHP derleyicisi, “E başlık nerde ulan?” hatası verdi. app.php’yi şu şekilde değiştirelim:

<?php

require_once 'Dictionary.php'
;
$dictionary = new MidoriKocakDictionary("Nesne Yönelimli Programlama Sözlüğü");
echo $dictionary->getTitle();

Tarayıcı ile app.php dosyasını açalım ve tertemiz başlığımızı görelim:

Hatasız mis gibi.

Bu yazıda şimdilik anlatacaklarım bu kadar.

Özetle;

  1. Sınıf ve nesne kavramlarının ne olduklarını
  2. Sınıfların nasıl tasarlandıklarını
  3. Ne işe yaradıklarını
  4. Nasıl yazıldıklarını
  5. Nasıl kullanılmaları gerektiğini
  6. Sihirli __constructor() metodunu öğrendik

Bir sonraki yazıda, sınıfımızı oluşturmaya ve programımızı yazmaya devam edeceğiz.

Bir sonraki yazıya şuradan ulaşabilirsiniz:

View story at Medium.com


Projelerle PHP 7

Ben Mutlu Koçak, Bilgisayar Mühendisiyim, ZCPE Sertifikasına sahibim ve “Hiç Bilmeyenler İçin İnternet Programlamaya Giriş — PHP 7” adlı kitabın yazarıyım. Kitabım: https://www.seckin.com.tr/kitap/911934237
Özgeçmişim:
http://represent.io/mtkocak.pdf 
Websitem:
http://mynameismidori.com

Yorumla

Yorum