OOP'de Polymorphism

Çok biçimlilik temel ilkelerden biridirObject-Oriented Programming. Farklı nesnelerin aynı yönteme, arayüze veya işleme farklı şekillerde yanıt vermesini sağlar. Basit bir ifadeyle polimorfizm, bir eylemin onu gerçekleştiren nesneye bağlı olarak birden fazla biçime sahip olabileceği anlamına gelir.

Bu kavram modern yazılım geliştirmede çok önemlidir çünkü geliştiricilerin esnek, yeniden kullanılabilir ve bakımı yapılabilir kod yazmasına yardımcı olur. Polimorfizm, farklı nesne türlerini ele almak için birçok koşullu ifade yazmak yerine, her nesnenin ortak bir yapıyı paylaşarak kendi davranışını tanımlamasına olanak tanır.

Giriş

Bu OOP serisinin önceki makalelerinde sınıfları, nesneleri, özellikleri, yöntemleri, yapıcıları, yıkıcıları, kalıtımı ve kapsüllemeyi tartıştık. Bu kavramlar, geliştiricilerin kodu anlamlı bileşenler halinde düzenlemesine ve nesnelerin dahili durumunu korumasına yardımcı olur.

Polimorfizm, nesnelerin dahili olarak farklı davranırken ortak bir arayüzü paylaşmasına izin vererek bu fikirlerin üzerine kuruludur. Bu, genişletilmesi daha kolay ve değiştirilmesi daha kolay yazılım sistemlerinin tasarlanmasını mümkün kılar.

Örneğin bir ödeme sistemi kredi kartlarını, PayPal'ı, banka havalelerini ve nakit ödemeleri destekleyebilir. Bu ödeme türlerinin hepsinin ödeme adı verilen bir yöntemi olabilir ancak her ödeme türü, ödemeyi farklı şekilde işleyecektir. Bu, polimorfizmin pratik bir örneğidir.

Object-Oriented Programming'da Polimorfizm Nedir?

Object-Oriented Programming'daki polimorfizm, aynı yöntem adının, arayüzün veya mesajın onu alan nesneye bağlı olarak farklı davranışlar üretebileceği anlamına gelir.

Polimorfizm kelimesi “birçok form” fikrinden gelmektedir. Programlamada bu, ortak bir eylemin farklı sınıflar tarafından farklı şekillerde uygulanabileceği anlamına gelir.

Örneğin, hesaplamaSalary adlı bir yöntemi düşünün. Tam zamanlı çalışanın, yarı zamanlı çalışanın ve serbest çalışanın hepsinin bir hesaplaSalary yöntemi olabilir, ancak her biri maaşı farklı şekilde hesaplayabilir. Yöntem adı aynıdır ancak uygulama, nesne türüne göre değişir.

Bu, geliştiricilerin belirli sınıf ayrıntıları yerine ortak davranışa dayalı kod yazmasına olanak tanır.

Polimorfizm Neden Önemlidir?

Polimorfizm önemlidir çünkü kod tekrarını azaltır ve yazılımı daha esnek hale getirir. Birden fazla sınıf aynı davranış adını veya arayüzünü paylaştığında uygulama onlarla genel bir şekilde çalışabilir.

Polimorfizm olmadan, geliştiricilerin her nesnenin türünü kontrol etmek ve hangi eylemin gerçekleştirileceğine karar vermek için çok sayıda if ifadesi veya geçiş durumu yazması gerekebilir. Bu, proje büyüdükçe kodun bakımını zorlaştırır.

Polimorfizmde her sınıf kendi davranışından sorumludur. Ana uygulama kodu, her nesnenin tam dahili uygulamasını bilmeye gerek kalmadan aynı yöntemi çağırabilir.

Bu, sistemi daha temiz, daha ölçeklenebilir ve gelecekte genişletilmesi daha kolay hale getirir.

Polimorfizm ve Gerçek Dünya Düşüncesi

Polimorfizmi gerçek dünyadaki örneklerle anlamak kolaydır. Arabalar, motosikletler, otobüsler ve kamyonlar gibi farklı araç türlerini hayal edin. Hepsi hareket edebilir ama her biri farklı şekilde hareket eder ve farklı kurallara, hıza, kapasiteye ve davranışa sahip olabilir.

OOP'da bu fikri taşıma gibi yaygın bir yöntem kullanarak temsil edebiliriz. Her araç sınıfı aynı genel eylemi paylaşırken hareket yöntemini farklı şekilde uygulayabilir.

Bu, geliştiricilerin gerçek dünya sistemlerini daha doğal bir şekilde modellemesine yardımcı olur. Yazılımın her araçla ilgili her ayrıntıyı bilmesine gerek yoktur. Sadece her aracın hareket edebildiğini bilmesi gerekiyor.

Polimorfizm Türleri

Polimorfizm, programlama diline ve uygulamanın tasarımına bağlı olarak farklı şekillerde uygulanabilir. En yaygın türler şunlardır:

  • Derleme zamanı polimorfizmi:Davranış, program çalıştırılmadan önce, genellikle yöntemin aşırı yüklenmesi yoluyla belirlenir.

  • Çalışma zamanı polimorfizmi:Davranış, program çalışırken, genellikle yöntemin geçersiz kılınması ve devralınması yoluyla belirlenir.

  • Arayüz tabanlı polimorfizm:Farklı sınıflar aynı arayüzü uygular ve kendi davranışlarını sağlar.

Bazı programlama dilleri bu formların tümünü desteklerken bazıları yalnızca belirli türleri destekler. Örneğin, PHP, yöntem geçersiz kılmayı ve arayüz tabanlı polimorfizmi destekler, ancak Java veya C# gibi bazı dillerde olduğu gibi geleneksel yöntem aşırı yüklemesini desteklemez.

Yöntemi Geçersiz Kılma

Yöntem geçersiz kılma, bir alt sınıf, üst sınıfta zaten mevcut olan bir yöntemin kendi sürümünü sağladığında gerçekleşir. Bu, polimorfizmin en yaygın biçimlerinden biridir.

Örneğin, Animal adlı bir ebeveyn sınıfın makeSound adında bir yöntemi olabilir. Dog, Cat ve Bird gibi alt sınıflar bu yöntemi geçersiz kılabilir ve farklı sesler sağlayabilir.

class Animal
{
    public function makeSound(): string
    {
        return 'Some sound';
    }
}

class Dog extends Animal
{
    public function makeSound(): string
    {
        return 'Bark';
    }
}

class Cat extends Animal
{
    public function makeSound(): string
    {
        return 'Meow';
    }
}

Bu örnekte, hem Köpek hem de Kedi, Animal'dan miras alır, ancak her sınıf, makeSound'un farklı bir uygulamasını sağlar. Yöntem adı aynıdır ancak sonuç nesneye bağlıdır.

Çalışma Zamanı Polimorfizmi Örneği

Çalışma zamanı polimorfizmi, programın, uygulama çalışırken hangi yöntem uygulamasının yürütülmesi gerektiğine karar vermesine olanak tanır.

Örneğin:

$animals = [
    new Dog(),
    new Cat(),
];

foreach ($animals as $animal) {
    echo $animal->makeSound();
}

Döngü her nesnede makeSound'u çağırır. Kodun nesnenin Köpek mi Kedi mi olduğunu kontrol etmesine gerek yoktur. Her nesne yöntem çağrısına nasıl yanıt vereceğini bilir.

Bu, polimorfizmin en güçlü faydalarından biridir. Genel kodun, her sınıfa sıkı sıkıya bağlanmadan belirli nesnelerle çalışmasına olanak tanır.

Arayüz Tabanlı Polimorfizm

Arayüzler, temiz yazılım tasarımında polimorfizmi uygulamak için yaygın olarak kullanılır. Bir arayüz, birden fazla sınıfın izleyebileceği bir sözleşmeyi tanımlar. Her sınıf, arayüz tarafından tanımlanan yöntemleri uygulamalıdır, ancak her sınıf kendi iç mantığını sağlayabilir.

Örneğin, bir ödeme sistemi bir PaymentMethod arayüzünü tanımlayabilir:

interface PaymentMethod
{
    public function pay(float $amount): bool;
}

class CreditCardPayment implements PaymentMethod
{
    public function pay(float $amount): bool
    {
        // Process credit card payment
        return true;
    }
}

class PayPalPayment implements PaymentMethod
{
    public function pay(float $amount): bool
    {
        // Process PayPal payment
        return true;
    }
}

Hem CreditCardPayment hem de PayPalPayment aynı arayüzü uygular. Her sınıf ödemeyi farklı şekilde işlese de uygulama, her iki sınıfı da ödeme yöntemi olarak değerlendirebilir.

Ödeme Sisteminde Polimorfizmin Kullanımı

Bir ödeme hizmeti, belirli bir ödeme sınıfına bağlı olmak yerine PaymentMethod arayüzüne bağlı olarak polimorfizmi kullanabilir.

class CheckoutService
{
    public function checkout(PaymentMethod $paymentMethod, float $amount): bool
    {
        return $paymentMethod->pay($amount);
    }
}

Bu örnekte CheckoutService, ödemenin kredi kartıyla mı, PayPal'la mı, banka havalesiyle mi yoksa gelecekteki başka bir ödeme yöntemiyle mi yapıldığını umursamaz. Yalnızca PaymentMethod arayüzünü takip eden bir nesne bekler.

Bu da sistemi esnek hale getiriyor. Daha sonra yeni bir ödeme yöntemi eklenirse CheckoutService'in yeniden yazılmasına gerek yoktur. Yeni bir sınıf aynı arayüzü kolayca uygulayabilir.

Polimorfizm ve Kalıtım

Polimorfizm sıklıkla kalıtımla bağlantılıdır. Alt sınıflar bir ebeveyn sınıftan miras aldığında, ebeveyn yöntemleri geçersiz kılabilir ve kendi davranışlarını sağlayabilirler.

Bu, ana sınıfın genel bir yapı tanımlamasına, alt sınıfların ise belirli davranışları tanımlamasına olanak tanır. Örneğin, Shape sınıfı bir hesaplaArea yöntemini tanımlayabilirken Circle, Rectangle ve Triangle sınıfları alanı farklı şekilde hesaplayabilir.

abstract class Shape
{
    abstract public function calculateArea(): float;
}

class Circle extends Shape
{
    public function __construct(private float $radius)
    {
    }

    public function calculateArea(): float
    {
        return pi() * $this->radius * $this->radius;
    }
}

class Rectangle extends Shape
{
    public function __construct(private float $width, private float $height)
    {
    }

    public function calculateArea(): float
    {
        return $this->width * $this->height;
    }
}

Hem Daire hem de Dikdörtgen şekildir ancak alanı farklı şekillerde hesaplarlar. Bu, kalıtım ve soyutlama yoluyla polimorfizmdir.

Polimorfizm ve Soyut Sınıflar

Soyut sınıflar aynı zamanda polimorfizm için de faydalıdır. Soyut bir sınıf, ortak davranışı tanımlayabilir ve alt sınıfların belirli yöntemleri uygulamasını gerektirebilir.

Örneğin, soyut bir Bildirim sınıfı bir gönderme yöntemini tanımlayabilir. EmailNotification, SMSNotification ve PushNotification gibi alt sınıflar gönderme yöntemini farklı şekilde uygulayabilir.

Bu tasarım, uygulamanın bildirimlerle genel bir şekilde çalışmasına olanak tanırken, farklı dağıtım kanallarını da desteklemeye devam ediyor.

Polimorfizm ve Dependency Injection

Bağımlılık enjeksiyonu ile birleştirildiğinde polimorfizm daha da güçlü hale gelir. Bağımlılık enjeksiyonu, bir sınıfın bağımlılıklarını dahili olarak oluşturmak yerine dışarıdan almasına olanak tanır.

Bağımlılıklar arayüzlere dayalı olduğunda, geliştiriciler ana sınıfı değiştirmeden bir uygulamayı diğeriyle değiştirebilirler.

Örneğin, bir rapor hizmeti bir Dışa Aktarıcı arayüzüne bağlı olabilir. Uygulama duruma göre PdfExporter, ExcelExporter veya CsvExporter sağlayabilir. Rapor hizmeti belirli bir sınıfa değil ortak arayüze bağlı olduğundan aynı kalır.

Gerçek Yazılım Projelerinde Polimorfizm

Polimorfizm birçok gerçek yazılım projesinde kullanılmaktadır. Web uygulamalarında, APIs, çerçevelerde, ödeme sistemlerinde, bildirim sistemlerinde, dosya depolama sistemlerinde, kimlik doğrulama sistemlerinde ve raporlama araçlarında görünür.

Laravel veya Symfony uygulamasında, farklı hizmetler aynı sözleşmeyi uyguladığında polimorfizm kullanılabilir. Örneğin, farklı posta sağlayıcıları aynı posta gönderen arayüzünü uygulayabilir. Farklı depolama sağlayıcıları aynı dosya depolama arayüzünü uygulayabilir. Farklı kullanıcı rolleri farklı izin davranışlarını uygulayabilir.

Bu, kodu daha uyarlanabilir hale getirir. Uygulama, sistemin büyük bölümlerini yeniden yazmaya gerek kalmadan sağlayıcıları değiştirebilir veya yeni özellikler ekleyebilir.

Polimorfizm ve Temiz Kod

Polimorfizm temiz kodu destekler çünkü koşullu mantığı azaltır ve sorumlulukların ayrılmasını geliştirir. Her sınıf, merkezi bir sınıfı her şeyi bilmeye zorlamak yerine kendi davranışını yönetir.

Örneğin, polimorfizm olmadan bir ödeme sistemi bunun gibi birçok koşulu içerebilir:

if ($type === 'credit_card') {
    // pay by credit card
} elseif ($type === 'paypal') {
    // pay by PayPal
} elseif ($type === 'bank_transfer') {
    // pay by bank transfer
}

Daha fazla ödeme yöntemi eklendikçe bu yaklaşımın sürdürülmesi zorlaşıyor.

Polimorfizmde her ödeme sınıfının kendi ödeme yöntemi vardır. Ödeme sistemi yalnızca ödemeyi çağırır. Bu, daha temiz ve bakımı daha kolay kod üretir.

Polimorfizm ve Açık Kapalı Prensibi

Polimorfizm, SOLID prensiplerinden biri olan Açık Kapalı Prensibi ile yakından ilgilidir. Açık Kapalı Prensibi, yazılımın genişletmeye açık, değişiklik yapmaya kapalı olması gerektiğini söylüyor.

Bu, geliştiricilerin mevcut test edilmiş kodu değiştirmeden yeni davranışlar ekleyebilmesi gerektiği anlamına gelir. Polimorfizm, yeni sınıfların aynı arayüzü uygulamasına veya aynı soyut sınıfı genişletmesine izin vererek bunu destekler.

Örneğin, bir uygulama zaten kredi kartı ve PayPal ödemelerini destekliyorsa, yeni bir Apple Pay ödeme sınıfı eklemek, ödeme mantığının yeniden yazılmasını gerektirmemelidir. Yeni sınıf aynı PaymentMethod arayüzünü izleyebilir.

Polimorfizmin Faydaları

Polimorfizm Object-Oriented Programming'da birçok fayda sağlar. Geliştiricilerin genişletilmesi, test edilmesi ve bakımı daha kolay sistemler tasarlamasına yardımcı olur.

Ana faydalar şunları içerir:

  • Kod tekrarının azaltılması.

  • Karmaşık if ve switch ifadelerinin azaltılması.

  • Esnekliği ve genişletilebilirliği geliştirmek.

  • Alternatif uygulamalarla kodun test edilmesini kolaylaştırma.

  • Temiz mimariyi ve tasarım modellerini desteklemek.

  • Farklı nesnelerin ortak bir arayüzü paylaşmasına izin vermek.

  • Büyük projelerde uzun vadeli sürdürülebilirliğin iyileştirilmesi.

Bu faydalar, polimorfizmi profesyonel yazılım geliştirme için en değerli kavramlardan biri haline getirmektedir.

Polimorfizm Kullanılırken Yaygın Hatalar

Yaygın bir hata, ihtiyaç duyulmadığında polimorfizmi kullanmaktır. Sistemin yalnızca tek bir basit davranışı varsa ve beklenen bir değişiklik yoksa, arayüzler ve birden fazla sınıf eklemek, kodu gerekenden daha karmaşık hale getirebilir.

Diğer bir hata ise açık bir sözleşmeyi temsil etmeyen zayıf bir arayüz oluşturmaktır. Bir arayüz anlamlı davranışı tanımlamalıdır. Yalnızca kodun daha gelişmiş görünmesi için oluşturulmamalıdır.

Üçüncü bir hata, ana tür veya arayüz yerine alt sınıf ayrıntılarına bağlı olmaktır. Bu, polimorfizmin faydasını azaltır ve kodun tekrar sıkı bir şekilde eşleşmiş olmasını sağlar.

İyi polimorfizm, kodu daha kafa karıştırıcı değil, daha basit hale getirmelidir.

Polimorfizm Kullanımına İlişkin En İyi Uygulamalar

Polimorfizmi doğru kullanmak için geliştiricilerin davranış ve sorumluluğa odaklanması gerekir. Sınıflar yalnızca aynı kavramın farklı biçimlerini gerçekten temsil ettiklerinde ortak bir arayüzü paylaşmalıdır.

Yararlı en iyi uygulamalar şunları içerir:

  • Farklı sınıflar aynı davranış sözleşmesini paylaştığında arayüzleri kullanın.

  • Sınıflar ortak mantık ve yapıyı paylaştığında soyut sınıfları kullanın.

  • Polimorfizmin davranışı daha net ifade edebileceği durumlarda büyük koşullu bloklardan kaçının.

  • Yöntem adlarını anlamlı ve tutarlı tutun.

  • Yalnızca teknik ayrıntılara değil, iş davranışına ilişkin arayüzler tasarlayın.

  • Çok basit problemler için polimorfizmi aşırı kullanmayın.

  • Somut sınıflar yerine soyutlamalara bağlı olmayı tercih edin.

Bu uygulamalar, geliştiricilerin polimorfizmi temiz ve pratik bir şekilde uygulamalarına yardımcı olur.

Yeni Başlayanlar İçin Polimorfizm Neden Önemlidir?

Yeni başlayanlar için polimorfizm ilk başta soyut gelebilir. Ancak ödemeler, bildirimler, şekiller, kullanıcılar, raporlar, dosya aktarıcılar gibi gerçek örneklere bağlanıldığında anlaşılması daha kolay hale gelir.

Ana fikir basittir: farklı nesneler aynı eylem adını paylaşabilir ancak bu eylemi farklı şekilde gerçekleştirebilir.

Polimorfizmi öğrenmek, yeni başlayanların birçok koşulu içeren prosedür kodu yazmaktan, genişletilmesi daha kolay olan nesne yönelimli kod yazmaya geçiş yapmasına yardımcı olur. Tasarım kalıplarını, çerçeve mimarisini ve profesyonel yazılım tasarımını anlamaya yönelik önemli bir adımdır.

Sonuç

Polimorfizm Objec'in temel ilkesidir