Javascript İle Nesne Yönelimli Programlama Uygulaması Nasıl Yapılır?

Javascript İle Nesne Yönelimli Programlama Uygulaması Nasıl Yapılır?
Javascript İle Nesne Yönelimli Programlama Uygulaması Nasıl Yapılır?

Ön Hazırlık ve Gereksinimler

Modern JavaScript ile OOP uygulamaları geliştirmek için ekstra bir kütüphaneye ihtiyacınız yoktur. Tarayıcıların yerel olarak desteklediği ES6 (ECMAScript 2015) ve sonraki sürümler, sınıf tabanlı programlama için gerekli tüm araçları sunar. Çalışma ortamınızın güncel olduğundan emin olmak için şu araçları kullanmanızı öneririz:

  • Node.js: Sunucu tarafı JavaScript çalıştırmak için en güncel LTS sürümü.
  • Modern Tarayıcı: Chrome, Firefox veya Edge'in güncel sürümleri.
  • Kod Editörü: VS Code gibi eklenti desteği güçlü bir editör.

Kodlarınızı test etmek için tarayıcınızın "Geliştirici Araçları" (F12) içerisindeki "Console" panelini kullanabilir veya terminal üzerinden node dosya_adi.js komutu ile çıktıları gözlemleyebilirsiniz.

Adım 1: Sınıf Yapısını Oluşturma ve Constructor Kullanımı

JavaScript'te bir sınıf, nesnelerin nasıl oluşturulacağını tanımlayan bir şablondur. class anahtar kelimesi ile sınıfımızı tanımlarız. constructor metodu ise nesne ilk oluşturulduğunda çalışan özel bir fonksiyondur ve başlangıç değerlerini atamak için kullanılır.

class Kullanici {
    constructor(ad, soyad, eposta) {
        this.ad = ad;
        this.soyad = soyad;
        this.eposta = eposta;
    }

    bilgileriGoster() {
        return `${this.ad} ${this.soyad} - ${this.eposta}`;
    }
}

const yeniKullanici = new Kullanici("Ahmet", "Yılmaz", "ahmet@example.com");
console.log(yeniKullanici.bilgileriGoster());

Yukarıdaki kodda, Kullanici adında bir sınıf tanımladık. this anahtar kelimesi, o an oluşturulan nesnenin kendisine referans verir. new operatörü ise sınıftan yeni bir örnek (instance) türetmemizi sağlar.

Adım 2: Kapsülleme (Encapsulation) ile Veri Güvenliği

Kapsülleme, nesnenin iç verilerini dışarıdan doğrudan erişime kapatmak ve sadece kontrollü yöntemlerle değiştirmektir. JavaScript'te özel (private) alanlar tanımlamak için değişkenin başına # işareti eklenir.

class BankaHesabi {
    #bakiye = 0; // Private değişken

    constructor(sahip) {
        this.sahip = sahip;
    }

    paraYatir(miktar) {
        if (miktar > 0) {
            this.#bakiye += miktar;
            console.log(`Yeni bakiye: ${this.#bakiye}`);
        }
    }

    getBakiye() {
        return this.#bakiye;
    }
}

const hesap = new BankaHesabi("Canan");
hesap.paraYatir(500);
// console.log(hesap.#bakiye); // Hata verir, dışarıdan erişilemez.

Bu yöntem, hassas verilerin yanlışlıkla değiştirilmesini engeller. #bakiye değişkenine doğrudan erişmeye çalışmak bir sözdizimi hatasına yol açacaktır. Bu, özellikle finansal veya kullanıcı kimlik bilgilerini içeren uygulamalarda kritik bir güvenlik önlemidir.

Adım 3: Kalıtım (Inheritance) ile Kod Tekrarını Önleme

Kalıtım, bir sınıfın özelliklerini ve metotlarını başka bir sınıfa aktarmaktır. extends anahtar kelimesi ile üst sınıfın (parent) özelliklerini alt sınıfa (child) miras bırakabiliriz. super() metodu ise üst sınıfın constructor'ını çağırmak için kullanılır.

class Calisan {
    constructor(isim) {
        this.isim = isim;
    }
}

class Yazilimci extends Calisan {
    constructor(isim, dil) {
        super(isim);
        this.dil = dil;
    }

    kodYaz() {
        console.log(`${this.isim}, ${this.dil} ile kod yazıyor.`);
    }
}

const dev = new Yazilimci("Deniz", "JavaScript");
dev.kodYaz();

Bu yapı sayesinde Calisan sınıfındaki ortak özellikleri tekrar yazmadan Yazilimci sınıfında kullanabildik. Bu, büyük ölçekli uygulamalarda kodun bakımını kolaylaştırır.

Adım 4: Polimorfizm (Çok Biçimlilik) Uygulaması

Polimorfizm, aynı isimdeki metotların farklı sınıflarda farklı davranışlar sergilemesidir. Örneğin, her hayvanın "ses çıkarması" farklıdır; ancak hepsinde sesCikar() metodu bulunabilir.

class Hayvan {
    sesCikar() {
        console.log("Hayvan ses çıkarıyor.");
    }
}

class Kedi extends Hayvan {
    sesCikar() {
        console.log("Miyav!");
    }
}

class Kopek extends Hayvan {
    sesCikar() {
        console.log("Hav!");
    }
}

const hayvanlar = [new Kedi(), new Kopek()];
hayvanlar.forEach(h => h.sesCikar());

Bu örnekte, hayvanlar dizisindeki her nesne kendi sesCikar metodunu çalıştırır. Nesne yönelimli programlamada bu esneklik, genişletilebilir sistemler kurmamızı sağlar.

OOP Yöntemlerinin Karşılaştırmalı Özeti

Kavram Avantajı Kullanım Amacı
Kapsülleme Veri Güvenliği Hassas verileri koruma
Kalıtım Kod Tekrarını Önleme Hiyerarşik yapı kurma
Polimorfizm Esneklik Farklı nesnelerle ortak arayüz
Kritik Uyarı: Nesne yönelimli programlama kullanırken, nesnelerinizi aşırı karmaşıklaştırmaktan kaçının. "Composition over Inheritance" (Kalıtım yerine birleşim) prensibini göz önünde bulundurarak, sınıflarınızın çok derin hiyerarşiler oluşturmasına izin vermeyin. Ayrıca, kullanıcı girdilerini sınıflarınıza dahil ederken her zaman XSS ve SQL injection gibi saldırılara karşı girdi temizleme (sanitization) yapmayı unutmayın.

Sıkça Sorulan Sorular

JavaScript'te sınıflar neden kullanılır?

Sınıflar, kodun daha düzenli, okunabilir ve yönetilebilir olmasını sağlar. Karmaşık verileri nesne modelleriyle temsil ederek hata yapma payını azaltır.

Private (#) değişkenler tüm tarayıcılarda çalışır mı?

Modern tarayıcıların tamamında ve Node.js'in güncel sürümlerinde desteklenmektedir. Çok eski tarayıcılar için Babel gibi derleyiciler kullanılmalıdır.

Constructor nedir ve ne işe yarar?

Constructor, bir sınıftan yeni bir nesne oluşturulduğunda çağrılan özel bir metottur. Nesnenin başlangıç özelliklerini tanımlamak için kullanılır.

Kalıtım (Inheritance) her zaman gerekli midir?

Hayır, her durumda gerekli değildir. Eğer sınıflar arasında mantıksal bir "bir türüdür" (is-a) ilişkisi yoksa, kalıtım yerine kompozisyon kullanmak daha doğrudur.

Static metotlar ne zaman kullanılır?

Bir sınıfın örneğini oluşturmadan doğrudan sınıf ismi üzerinden çağrılması gereken yardımcı fonksiyonlar için static anahtar kelimesi kullanılır.

Nesne Yönelimli Programlamada Hata Ayıklama ve Hata Yönetimi

OOP yapılarında hata yönetimi, kodunuzun sürdürülebilirliği için kritik bir öneme sahiptir. Özellikle karmaşık sınıf hiyerarşilerinde, bir nesnenin beklenen durumda olup olmadığını kontrol etmek, uygulamanın çökmesini engeller. JavaScript'te try...catch bloklarını sınıf metotları içerisinde kullanarak, hataları merkezi bir noktada yönetebilir ve hata ayıklama sürecini kolaylaştırabilirsiniz.

Aşağıdaki örnekte, bir banka hesabı sınıfında bakiye yetersizliği durumunda özel bir hata fırlatma senaryosunu inceleyelim:

class BankaHesabi {
    #bakiye = 0;

    paraCek(miktar) {
        try {
            if (miktar > this.#bakiye) {
                throw new Error("Yetersiz bakiye: İşlem gerçekleştirilemedi.");
            }
            this.#bakiye -= miktar;
            console.log(`Yeni bakiye: ${this.#bakiye}`);
        } catch (hata) {
            console.error(`Hata Yönetimi: ${hata.message}`);
        }
    }

    bakiyeYukle(miktar) {
        this.#bakiye += miktar;
    }
}

const hesap = new BankaHesabi();
hesap.paraCek(100); // Hata yakalanacak ve konsola yazdırılacaktır.

OOP Projelerinde Performans Optimizasyonu

Nesne yönelimli programlama, çok sayıda nesne oluşturulduğunda bellek (memory) kullanımını artırabilir. JavaScript motorları (V8 gibi) sınıfları optimize etmek için "Hidden Classes" (Gizli Sınıflar) yapısını kullanır. Performansı artırmak için şu ipuçlarına dikkat etmelisiniz:

  • Prototipleri Kullanın: Metotları constructor içerisinde değil, sınıfın gövdesinde tanımlayın. Böylece her örnek (instance) için metotlar yeniden oluşturulmaz, prototip zinciri üzerinden paylaşılır.
  • Gereksiz Nesne Oluşturmaktan Kaçının: Döngü içerisinde sürekli yeni nesneler oluşturmak yerine, mümkünse mevcut nesneleri güncelleyin.
  • Private Alanların Maliyeti: # ile tanımlanan özel alanlar, geleneksel özelliklere göre çok küçük bir performans maliyeti getirebilir; ancak güvenlik gereksinimleri bu maliyeti genellikle haklı çıkarır.

Aşağıdaki örnekte, metotların prototip üzerinde tanımlanmasının bellek verimliliği üzerindeki etkisini görebilirsiniz:

// Verimli yöntem: Metot prototipte kalır
class Urun {
    constructor(ad, fiyat) {
        this.ad = ad;
        this.fiyat = fiyat;
    }

    // Bu metot tüm örnekler tarafından paylaşılır
    bilgiVer() {
        return `${this.ad} - ${this.fiyat} TL`;
    }
}

const urun1 = new Urun("Laptop", 15000);
const urun2 = new Urun("Mouse", 500);

console.log(urun1.bilgiVer === urun2.bilgiVer); // true (Aynı referansı paylaşırlar)
İpucu: Büyük ölçekli uygulamalarda nesnelerin bellek tüketimini izlemek için Chrome DevTools içerisindeki "Memory" sekmesini kullanarak "Heap Snapshot" alabilir ve gereksiz nesne birikimlerini analiz edebilirsiniz.

Sonuç

JavaScript ile nesne yönelimli programlama, projelerinizi profesyonel bir seviyeye taşımanın anahtarıdır. Bu rehberde öğrendiğiniz kapsülleme, kalıtım ve polimorfizm kavramlarını küçük projelerinizde pratik ederek pekiştirebilirsiniz. OOP, sadece bir sözdizimi değil, aynı zamanda bir problem çözme disiplinidir. Bir sonraki adım olarak, "Design Patterns" (Tasarım Kalıpları) konusunu araştırarak, bu nesne yapılarını daha karmaşık mimarilerde nasıl kullanabileceğinizi keşfetmenizi öneririm.

Bu yazıya tepkinizi paylaşın:
Zeynep Kaya

Dijital içerik editörlüğü geçmişimle, adım adım rehberlerin en anlaşılır ve uygulanabilir şekilde kurgulanmasını sağlıyorum. Teknik detayları herkes için sadeleştirmeyi seviyorum.

Yorumlar (0)

Yorum Yaz