Bu bölümde, PHP hakkında nesne tabanlı programlama (OOP – object-oriented programming) üzerine bilgi vermeye çalışacağım.
PHP 5 ile OOP yapısı tamamen desteklenir oldu. OOP’da temel programlama kavramları şunlardır:
- Abstraction (Soyutlama)
- Polymorphism (Çok biçimlilik)
- Encapsulation (Sarmalama)
- Inheritance (Kalıtım)
Soyutlama, problemi çözerken ilgili metodların belli parçalar halinde yazılarak kodların basit ve anlaşılır hale getirilmesidir diyebiliriz. Çok biçimlilik ise, yazdığımız nesnenin yada fonksiyonun farklı veri yada giriş değerleri ile kullanılabilmesidir. Sarmalama ise, yazdığınız sınıf içerisindei metodların dışarıdan erişilebilir olup olmamalarını sağladığımız bir çeşit güvenlik katmanıdır diyebiliriz. Son olarak Kalıtım ise, var olan sınıfların kalıtım (miras alınarak) yeniden/istenildiği kadar oluşturululabilmesi.
Nesneler
Nesneler (class) PHP OOP yapısının temel taşıdır. Aslında zaten OOP yapısının temelinde de nesneler vardır. Günümüzde OOP yapısını destekleyen diller için de bu öğreneceklerimiz çoğunlukla geçerli olacaktır.
Nesneler yani class’lar, sizin yarafınızdan yapmak istediğiniz programın amacına uygun olarak yazılır yada başkası tarafından önceden yazılmış nesneler baz alınarak kullanılabilir. Bu nesneler kalıtım olayı ile oluşturulup kullanılır.
1 2 3 |
class Simple {} $object = new Simple(); |
Yukarıdaki minik örnekten yola çıakrsan sınıfımızın isminin Simple olduğunu ve
1 |
class Simple {} |
ile yazılmaya başlandığını görebiliriz. Elbette bu örnekte sadece nesnemizin adı tanımlanıyor, içerisinde hiçbir metod olmadığından bir işe yaramayacaktır…
1 |
$object = new Simple(); |
bölümünde ise $object değişkenimize new Simple() diyerek (miras alma) nesnemizin bir örneğini oluşturuyoruz. Eğer sınıfımızın içinde metodlarımız olsaydı artık bunları alt satırlarda $object değişkenimizin alt metodları/fonksiyonları gibi kullanabilirdik.
Nesne İçerisindeki Değişkenler
Nesne içerisinde değişken kullanımını ve erişimi kısa ve basit bir örnek ile sizlere zenim hazırlamak için açıklamaya çalışacağım. Aşağıdaki örneği inceleyelim;
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class Person { public $name = ""; } $p1 = new Person(); $p1->name = "Değer 1"; $p2 = new Person(); $p2->name = "Değer 2"; echo $p1->name . "\n"; echo $p2->name . "\n"; |
Burada, Person isminde bir sınıfımız var ve içerisinde $name adından public yapıda bir değişkene sahip. Değişkenlerin alabilecekleri yapıları (OOP’un sarmalama özelliğidir) daha sonra göreceğiz.
1 2 |
$p1 = new Person(); $p1->name = "Değer 1"; |
Yukarıda, sınıfırmızdan türetip ($p1), sınıf üyesi olan name değişkenine bir değer atıyoruz. Daha sonra aynı işlemi $p2 ile türetip yine name değişkenimize başka bir değer atıyoruz.
1 2 3 4 5 6 |
echo $p1->name . "\n"; echo $p2->name . "\n"; // Çıktısı // -------- // Değer 1 // Değer 2 |
Daha sonra kodumuzu çalıştırdığımızda $p1 ve $p2 içerisindeki name değerlerini ekrana yazıyoruz.
Burda bilinmesi gereken, gördüğünüz gibi aynı değişkene erişiyoruz ama neden farklı farklı değerler alabiliyor? Çünkü sınıfı 2 ayrı değişkende create ediyoruz ve ikisi için de bellekte farklı yer açılıyor. Yani birbirinden bağımsız olarak çalışıyorlar.
Metodlar
Metodlar, fonksiyonları içerisinde barındıran sınıfların gövdesidir diyebiliriz. Metodlar, OOP’un sarmalama (encapsulation) özelliği ile erişim yapısı istediğimiz gibi şekillendirilebilir. Özellikle büyük projelerde ve sınıflarda sarmalama özelliğini iyi kullanmak ve planlamak gerekir. Her zaman olduğu gibi bi örnek üzerinde bunları irdelersek daha faydalı olacaktır…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
class Circle { public $radius; function setRadius($radius) { $this->radius = $radius; } function area() { return $this->radius * $this->radius * M_PI; } } $c = new Circle(); $c->setRadius(5); echo $c->area(), "\n"; |
Yukarıdaki örnekte Circle isminde bi sınıfımız ve içinde 2 metodumuz var.
1 |
public $radius; |
Burada ise sınıfımız içerisinde bulunan değişkenimiz var (member field). public etiketi ise ona sınıf dışından da erişebileceğimzi anlamına geliyor.
1 2 3 |
function setRadius($radius) { $this->radius = $radius; } |
Burada setRadius isminde bir metodumuz var. Normal bir PHP fonksiyonu farkı yok, ancak sınıflar içerisinde tanımlanan fonksiyonlara method diyoruz. $this değişkeni ise özel bir değişkendir. Sınıf içerisindeki değişkenlerimize (member fields) ulaşmak için kullanıyoruz.
1 2 3 |
function area() { return $this->radius * $this->radius * M_PI; } |
Burada ise area metodumuz gerekli hesaplamaları yaparak return ile sonucu döndermekte.
Burada, metodlara genel bir bakış yapmış olduk.
Erişim Belirteçleri
Yukarıda bahsi geçen public tanımını hatırlıyorsunuz umarım, şimdi o konu üzerinde konuşacağız. PHP 5′de 3 tane erişim tipi vardır. public, private ve protected.
public, her yerden erişilebilir anlamına gelir. Hem sınıf içindeki metodlar erişir hem de sınıftan miras alıp oluşturduğumuz değişken üzerinden erişilebilir yani kalıtım yolu ile aktarılırlar.
private, bu erişim belirteci sadece sınıfın içinden erişilebileceği anlamına gelir. Bu öğeler kalıtım yoluyla aktarılmaz.
protected, bu erişim belirteci sadece sınıfın içinden erişilebileceği fakat alt sınıflara aktarılabileceği anlamına gelmektedir (extends ile)
extends
Eğer sınıfımız başka bir sınıfın alt sınıfı olacaksa extends anahtar sözcüğü ile aktarılır.
Şimdi, bir örnek ile görelim.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
class Base { public $name = "Base"; protected $id = 6124; private $is_defined = "yes"; } class Derived extends Base { public function info() { echo "This is Derived class\n"; echo "Members inherited: \n"; echo $this->name . "\n"; echo $this->id . "\n"; echo $this->is_defined . "\n"; } } $derived = new Derived(); $derived->info(); |
Burada, koda baktığımızda çoğu şeyi biliyoruz tek bilmediğimiz extends bölümü olabilir.
1 |
class Derived extends Base |
Burada Derived sınıfı Base sınıfından türetiliyor. Bunun anlamı Base sınıfı içerisindeki değişkenler ve var olsaydı metodları Derived sınıfına aktarıyor manasına gelmekte. Yukarıda erişim belirteclerini hatırlarsak ve bu kodu çalıştırdığımızda Base sınıfındaki değişkenler içinde sadece private $is_defined = “yes”; bölümünün Derived içerisine aktarılmadığını görüyoruz. Bu da sarmalama konusunun ne olduğunu bize açıklamış oluyor.
Kısaca, public heryerden ulaşılır ve aktarılır extends ile… private, sınıf içinden ulaşılır sadece ve extends ile aktarılabilir… protected ise sınıf içinden ulaşılır ancak extends ile aktarılamaz.
Yapılandırıcı
Yapılandırıcılar, sınıfların miras alındıkları anca otomatik tetiklenecek metoddur. Hemen örnekleyelim;
1 2 3 4 5 6 7 8 |
class Song { function __construct($song) { echo "Song $song is created \n"; } } $song = new Song("Bad romance"); |
Yukarıdaki örnekte görüldüğü gibi Song sınıfımızı miras aldığımız bölümde new Song(“Bad romance”); şeklinde parametre gönderebiliyoruz. Bu parametre de yapılandırıcı (__construct) içerisinde echo ile ekrana basılıyor. Elbette __construct metodu, parametresiz ayda bir yada birden fazla parametre çeklinde olabilir. İhtiyacımıza göre düzenleyebiliriz.
__toString() Metodu
Sınıfımızı create edip print yada echo ile ekrana basmak istersek __toString Metodu varsa eğer otomatikman devreye giriyor. Çok kullanılan bir tanım olmasa da sınıfımızı test/debug ederken işimize yarar. Örnek;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
class Cat { public $name; public $age; function __construct($name, $age) { $this->age = $age; $this->name = $name; } function __toString() { return "Cat: $this->name, Age: $this->age \n"; } } $missy = new Cat("Missy", 6); $lucky = new Cat("Lucky", 4); print $missy; echo $lucky; // Çıktısı // --------- // Cat: Missy, Age: 6 // Cat: Lucky, Age: 4 |
Kalıtım
Nesneye yönelik programlamanın temellerinden olan kalıtım (inheritance), yeni sınıflar yazarken elimizde var olan eski sınıfları kullanmamızı sağlar. Yeni sınıfın bir çok özelliği eski yazılan sınıfla aynıdır. Kalıtım yoluyla yeni sınıfta bu üyeleri (metodları) tekrardan yazmaya gerek kalmaz. Bazı üyeler ise farklıdır (Eğer tüm üyeler aynı olsa yeni sınıf yazmaya gerek kalmazd!). Eğer bu üyelerin tanımlamaları superclass’ta (üst sınıfta) mevcutsa, bu durumda bunlar farklılastırılır (overriding). Eğer yeni eklenecek üyelerin prototipleri superclasst’ta mevcut değilse bunlar direkt olarak yeni sınıfa (subclass) eklenebilir.
Kalıtım, yazılımın yeniden kullanılması ihtiyacına büyük ölcüde katkıda bulunur. Zaten kalıtım kavramının ortaya cıkmasının sebebi, özellikle büyük çaplı yazılım geliştirme çalışmalarında aynı kod parcalarının tekrar tekrar kullanılmasını önlemek ve yeni sınıfları var olanların üstüne inşa etmektir. Gerçek hayattaki nesne modeli de buna uygundur.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
class Base { function __construct() { echo "Construction of Base class \n"; } } class Derived extends Base { function __construct() { parent::__construct(); echo "Construction of Derived class \n"; } } $obj1 = new Base(); $obj2 = new Derived(); |
Yukarıdaki örnekte önemli olan nokta, Deriven’ın yapılandırıcı bölümündeki parent::__construct(); satırıdır. Burdan görülüyor ki, parent:: ile superclass’a ulaşabiliyoruz ve onun yapılandırıcısını tetikleyebiliyoruz. Umarım parent anahtar kelimesinin de ne iş yaptığını anlamış olduk.
Şimdi, biraz daha derin bir örnek yapalım,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
abstract class Being { protected $isAlive = true; public function isAlive() { if ($this->isAlive) { echo "Being is alive\n"; } else { echo "Being is not alive\n"; } } public function kill() { $this->isAlive = false; } } abstract class Animal extends Being { protected $age; public function __construct($age) { $this->age = $age; } protected function setAge($age) { $this->age = $age; } public function getAge() { return $this->age; } } class Cat extends Animal { private $name; public function __construct($name, $age) { $this->name = $name; parent::__construct($age); } public function getName() { return $this->name; } } $cat = new Cat("Cici", 4); $cat->isAlive(); echo $cat->getName() . " is " . $cat->getAge() . " years old\n"; $cat->kill(); $cat->isAlive(); |
Şimdi, burada yeni birkaç tanım kullandık. Öncelikle 2 sınıfımızın olduğunu hatırlayalım Being, Animal ve Cat. Animal sınıfı Being sınıfından kalırım alır, Cat sınıfı da Animal sınıfından kalıtım almakta.
1 |
abstract class Being { |
Being sınıfı abstract tanımlanmıştır. Abstract (soyut) nedir? Az sonra…
1 |
abstract class Animal extends Being { |
Yine burada Animal sınıfını abstract olarak yine diğer bir abstract olan Being sınıfından extends ile miras alıyoruz.
1 |
class Cat extends Animal { |
Burada ise artık dışarıdan erişip kullanacağımız ana sınıfımızı tanımlıyoruz. Tabi Cat sınıfından türeterek…
Kodu çalıştırdığımızda göreceğimiz şey söyledir;
1 2 3 |
Being is alive Cici is 4 years old Being is not alive |
Soyut Sınıflar (abstract)
Soyutlama (Abstraction) sayesinde bir nesne, spesifik tanımlamalar ve sadece o nesneye ait olabilecek detaylardan kaçınarak, ayrıntılarda boğulmadan kullanılabilir.
Soyutlama: Her zaman ve her sartta kullanılmaya uygun, mümkün olan bütün kosullar ve oluşabilecek hatalar göz önünde bulundurularak yazilmis ve özel olmayip genel olarak kullanilabilen fonksiyonlar kullanimi olarak tanimlayabiliriz.
Devamını okumak için tıklayınız…
Bir örnek verirsek;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
abstract class Drawing { protected $x = 0; protected $y = 0; public abstract function area(); public function getCoordinates() { echo "\$x is $this->x\n"; echo "\$y is $this->y\n"; } } class Circle extends Drawing { private $radius; public function __construct($x, $y, $r) { $this->radius = $r; $this->x = $x; $this->y = $y; } public function area() { return $this->radius * $this->radius * pi(); } public function __toString() { return "Circle, at x: $this->x, y: $this->y, radius: $this->radius"; } } $o = new Circle(12, 45, 22); echo "$o \n"; echo "Area of the circle: " . $o->area() . "\n"; echo $o->getCoordinates(); |
Çalıştırdığımızda çıktısı şu şekilde olacaktır;
1 2 3 4 |
Circle, at x: 12, y: 45, radius: 22 Area of the circle: 1520.53084434 $x is 12 $y is 45 |
Soyutlama konusunda internet üzerindeki diğer yazıları okumanızı tavsiye ederim.
Arayüzler (Interfaces)
Nesne arayüzleri, bir sınıfın gerçeklemesi gereken yöntemlerin belirtildiği kodu, bu yöntemlerin nasıl yönetileceğini tanımlamaksızın oluşturmanıza imkan sağlar.
Arayüzler, standart sınıflardan farklı olarak hiçbir yöntemin içeriği tanımlanmaksızın interface anahtar sözcüğü kullanılarak tanımlanırlar.
Bir arayüzdeki tüm yöntemler, arayüzün doğası gereği public olarak bildirilmelidir.
implements işleci
Bir arayüzü gerçeklemek için, implements işleci kullanılır. Arayüzdeki tüm yöntemler bir sınıf içersinde gerçeklenmelidir; aksi takdirde bir ölümcül hata alırsınız. Bir sınıfın, birden fazla arayüz tanımlaması arayüzler virgülle ayrılarak sağlanabilir.
Örnek verirsek;
1 2 3 4 5 6 7 8 9 10 11 12 13 |
interface IInfo { public function do_inform(); } class Some implements IInfo { public function do_inform() { echo "This is a Some class\n"; } } $sm = new Some(); $sm->do_inform(); |
Burada öncelikle genel programlama yapısı gereği interface tanımlamaları standart olarak “I” harfi ile başlar. Elbet şart değildir ama okunurluk ve alışkanlıklar olarak öyle kullanılsa iyi olur.
1 2 3 |
interface IInfo { public function do_inform(); } |
arayüzümüzü tanımlıyoruz ve içine do_inform metod başlığımızı tanımlıyoruz. Bilindiği üzere interface’de sadece metod başlıkları tanımlanır, metodun gövdesi implement edilen sınıfta yazılır. Örnektede bu şekilde, echo metodu implement edilen sınıfımızın içindeki metodda yer almakta.
Çok Biçimlilik (Polymorphism)
Çok biçimlilik (Polymorphism), kalıtım (Inheritance) ile iç içe geçmiş ilk bakışta karışık gibi görünen, hatta ne gerek var bunlara dedirten, bir yapıya sahiptir. Fakat işin içerisine girdikten sonra ne gibi kolaylıklar ve güzellikler sağladığını görünce dil tasarımcılarına hakkını teslim etmek kaçınılmaz oluyor. En genel anlamda, oluşturduğumuz nesnelerin gerektiğinde kılıktan kılığa girip başka bir nesneymiş gibi davranabilmesine çok biçimlilik diyebiliriz.
Örneklersek;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
abstract class Shape { private $x = 0; private $y = 0; public abstract function area(); } class Rectangle extends Shape { function __construct($x, $y) { $this->x = $x; $this->y = $y; } function area() { return $this->x * $this->y; } } class Square extends Shape { function __construct($x) { $this->x = $x; } function area() { return $this->x * $this->x; } } $shapes = array( new Square(5), new Rectangle(12, 4), new Square(8) ); foreach ($shapes as $shape) { echo $shape->area() . "\n"; } |
Çok biçimlilik konusunda yine internetteki başka kaynaklardan yararlanın.