Herkese selam! Bu yazıda, nesne yönelimli programlamanın (OOP) temel özelliklerini pratik JavaScript örnekleriyle gözden geçireceğiz.(Nesne Tabanlı Programlama olarak da bilinir)
OOP ana kavramlarından, neden ve ne zaman faydalı olabileceğinden bahsedeceğiz ve size JS kodunu kullanarak bolca örnek vereceğiz.
Getirin Bakalım!

Nesneye Yönelimli Programlamaya Giriş
Varlıklar nesneler olarak kodlanmıştır ve her varlık, varlık tarafından gerçekleştirilebilecek belirli bir bilgi (özellikler) ve eylemler (yöntemler) grubunu gruplayacaktır.
OOP, kod modülerliğini ve organizasyonunu kolaylaştırdığı için büyük ölçekli projelerde çok kullanışlıdır.
Varlıkların soyutlanmasını uygulayarak, program hakkında, belirli eylemleri gerçekleştiren ve birbirleriyle etkileşime giren farklı aktörlerle, dünyamızın işleyişine benzer şekilde düşünebiliriz.
OOP’yi nasıl uygulayabileceğimizi daha iyi anlamak için küçük bir video oyununu kodlayacağımız pratik bir örnek kullanacağız. Karakter yaratmaya odaklanacağız ve OOP’nin bu konuda bize nasıl yardımcı olabileceğini göreceğiz.👽 👾 🤖
Nesneler Nasıl Oluşturulur – Sınıflar
Yani herhangi bir video oyununun karaktere ihtiyacı var, değil mi? Ve tüm karakterlerin renk, boy, isim vb. gibi belirli özellikleri (özellikleri) ve atlama, koşma, yumruk atma vb. gibi yetenekleri (yöntemleri) vardır. Nesneler, bu tür bilgileri depolamak için kullanılacak mükemmel veri yapısıdır.👌
Diyelim ki elimizde 3 farklı karakter “tür” var ve her türden 2 tane olmak üzere 6 farklı karakter yaratmak istiyoruz.
Karakterlerimizi yaratmanın bir yolu, nesneleri şu şekilde nesne değişmezlerini kullanarak manuel olarak oluşturmak olabilir:
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 | const yaratik1 = { isim: "Alian", tur: "alien", konus: () => console.log("Ben Alian ben bir yaratığım."), uc: () => console.log("Zuuuuhaaaiiiihaaa!!") } const yaratik2 = { name: "Predator", tur: "alien", sayPhrase: () => console.log("Yaşamak için kaç"), uc: () => console.log("Zuuuuhaaaiiiihaaa!!") } const bocek1 = { isim: "Buggy", tur: "bug", konus: () => console.log("Hata ayıklayıcınız benimle çalışmıyor!"), gizlen: () => console.log("Beni şimdi yakalayamazsın!") } const bocek2 = { isim: "Erik", tur: "bug", konus: () => console.log("Kafeinsiz içerim!"), gizlen: () => console.log("Beni şimdi yakalayamazsın!") } const robot1 = { isim: "Tito", tur: "robot", konus: () => console.log("Yemek yapabilirim, yüzebilir ve dans edebilirim!"), donus: () => console.log("Optimus prime!") } const robot2 = { isim: "Terminator", tur: "robot", konus: () => console.log("Görüşürüz bebeğim!"), donus: () => console.log("Optimus prime!") } |
Tüm karakterlerin isim ve tur özelliklerine ve ayrıca konus metoduna sahip olduğunu görüyoruz. Ayrıca her türün sadece o türe ait bir metodu da vardır (örneğin, uzaylılarda uc yöntemi vardır).
Gördüğünüz gibi, bazı veriler tüm karakterler tarafından paylaşılıyor, bazı veriler her tür tarafından paylaşılıyor ve bazı veriler her bir karaktere özel.
Bu yaklaşım işe yarıyor. Bunun gibi özelliklere ve yöntemlere mükemmel şekilde erişebileceğimizi görün:
1 2 3 4 5 6 | console.log(yaratik1.isim) // Çıktı: "Ali" console.log(bocek2.tur) // Çıktı: "bug" robot1.konus() // Çıktı: "Yemek yapabilirim, yüzebilir ve dans edebilirim!" robot2.donus() // Çıktı: "Optimus prime!" |
Bununla ilgili sorun, hiç iyi ölçeklenmemesi ve hataya açık olmasıdır. Oyunumuzun yüzlerce karaktere sahip olabileceğini hayal edin. Her biri için özellikleri ve yöntemleri manuel olarak ayarlamamız gerekecek!
Bu sorunu çözmek için, nesneler yaratmanın ve bir dizi koşul verilen farklı özellikleri ve yöntemleri ayarlamanın programlı bir yoluna ihtiyacımız var. Ve sınıflar bunun için iyidir. 😉
Sınıflar (class)
Sınıflar, önceden tanımlanmış özelliklere ve yöntemlere sahip nesneler oluşturmak için bir plan belirler. Bir sınıf oluşturarak, daha sonra o sınıftan, sınıfın sahip olduğu tüm özellikleri ve yöntemleri devralacak nesneleri başlatabilir (oluşturabilirsiniz).
Önceki kodumuzu yeniden düzenleyerek, karakter türlerimizin her biri için aşağıdaki gibi bir sınıf oluşturabiliriz:
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 | class Yaratik{ //Sınıf Adı constructor(isim,ifade){ //Sınıfın kurucu metodu(işlevi) this.isim = isim this.ifade = ifade this.tur = "alian" } uc = () =>{ console.log("Zuuuuhaaaiiiihaaa!!") } //function(){ } anonim fonksiyon konus = () =>{ console.log(this.ifade) } } class Bocek{ //Sınıf Adı constructor(isim,ifade){ //Sınıfın kurucu metodu(işlevi) this.isim = isim this.ifade = ifade this.tur = "bug" } gizlen = () => { console.log("Beni şimdi yakalayamazsın!")} konus = () => { console.log(this.ifade) } } class Robot{ constructor(isim,ifade){ //Sınıfın kurucu metodu(işlevi) this.isim = isim this.ifade = ifade this.tur = "robot" } donus = () => { console.log("Optimus prime!")} konus = () => { console.log(this.ifade) } } |
Ve sonra bu sınıflardan karakterlerimizi şu şekilde somutlaştırabiliriz:
1 2 3 4 5 6 7 8 | const yaratik1 = new Yaratik("Alian","Ben Alian ben bir yaratığım.") const yaratik2 = new Yaratik("Predator","Yaşamak için kaç") const bocek1 = new Bocek("Buggy","Hata ayıklayıcınız benimle çalışmıyor!") const bocek2 = new Bocek("Erik","Kafeinsiz içerik!") const robot1 = new Robot("Tito","Yemek yapabilirim, yüzebilir ve dans edebilirim!") const robot2 = new Robot("Terminator","Görüşürüz bebeğim!") |
Ardından, her bir nesnenin özelliklerine ve yöntemlerine aşağıdaki gibi erişebiliriz:
1 2 3 4 5 6 | console.log(yaratik1.isim) console.log(bocek2.tur) robot1.konus() robot2.donus() |
Bu yaklaşım ve genel olarak sınıfların kullanımı hakkında güzel olan şey, bu “planları” yeni nesneler oluşturmak için “manuel” yaptığımızdan daha hızlı ve daha güvenli bir şekilde kullanabilmemizdir.
Ayrıca, her nesne özelliğinin ve yönteminin nerede tanımlandığını (sınıfta) açıkça tanımlayabildiğimiz için kodumuz daha iyi organize edilmiştir. Bu da gelecekteki değişikliklerin veya uyarlamaların uygulanmasını çok daha kolay hale getirir.
Sınıflar hakkında akılda tutulması gereken bazı şeyler:
“bir programdaki sınıf, hem verileri hem de bu veriler üzerinde çalışan davranışları içeren özel veri yapısının bir “türünün” tanımıdır.
Sınıflar, bu tür bir veri yapısının nasıl çalıştığını tanımlar, ancak sınıfların kendileri somut değerler değildir. programda kullanabileceğiniz somut değer, bir sınıf (“new” anahtar sözcüğü ile) bir veya daha fazla kez somutlaştırılmalıdır.
- Sınıfların gerçek varlıklar veya nesneler olmadığını unutmayın. Sınıflar, gerçek nesneleri oluşturmak için kullanacağımız planlar veya kalıplardır.
- Sınıf adları, kural olarak, büyük ilk harf ve camelCase ile bildirilir. class anahtar sözcüğü bir sabit oluşturur, bu nedenle daha sonra yeniden tanımlanamaz.
- Sınıfların her zaman, daha sonra o sınıfı başlatmak için kullanılacak bir yapıcı yöntemi olması gerekir. JavaScript’teki bir yapıcı(consturtor), yalnızca bir nesne döndüren eski bir işlevdir. Bununla ilgili özel olan tek şey, “new” anahtar kelimesiyle çağrıldığında, prototipini döndürülen nesnenin prototipi olarak atamasıdır.
- “this” anahtar sözcüğü sınıfın kendisine işaret eder ve yapıcı yönteminde sınıf özelliklerini tanımlamak için kullanılır.
- Yöntemler, yalnızca işlev adı ve yürütme kodu tanımlanarak eklenebilir.
- JavaScript prototip tabanlı bir dildir ve JavaScript sınıfları içinde yalnızca sözdizimsel olarak kullanılır. Bu, burada büyük bir fark yaratmıyor, ancak bilmek ve akılda tutmak güzel.
OOP’nin Dört İlkesi
OOP normalde OOP programlarının nasıl çalıştığını belirleyen 4 temel ilkeyle açıklanır. Bunlar kalıtım, kapsülleme, soyutlama ve polimorfizmdir. Her birini gözden geçirelim.
Kalıtım – Miras – Inheritance
Kalıtım, diğer sınıflara dayalı sınıflar oluşturma yeteneğidir. Kalıtım ile, bir ebeveyn sınıf (belirli özellikler ve yöntemlerle) ve ardından üst sınıftan sahip olduğu tüm özellikleri ve yöntemleri miras alacak alt sınıflar tanımlayabiliriz.
Bunu bir örnekle görelim. Daha önce tanımladığımız tüm karakterlerin ana karakterimizin düşmanları olacağını hayal edin. Ve düşman olarak hepsi “güç” özelliğine ve “saldırı” yöntemine sahip olacaklar.
Bunu uygulamanın bir yolu, sahip olduğumuz tüm sınıflara aynı özellikleri ve yöntemleri eklemek olacaktır, bunun gibi:
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 | class Yaratik{ //Sınıf Adı constructor(isim,ifade,guc){ //Sınıfın kurucu metodu(işlevi) this.isim = isim this.ifade = ifade this.guc = guc this.tur = "alian" } uc = () =>{ console.log("Zuuuuhaaaiiiihaaa!!") } //function(){ } anonim fonksiyon konus = () =>{ console.log(this.ifade) } saldir = () => { console.log(`Şuan ${this.guc} ile saldırıyorum`)} } class Robot{ constructor(isim,ifade,guc){ //Sınıfın kurucu metodu(işlevi) this.isim = isim this.ifade = ifade this.guc = guc this.tur = "robot" } donus = () => { console.log("Optimus prime!")} konus = () => { console.log(this.ifade) } saldir = () => { console.log(`Şuan ${this.guc} ile saldırıyorum`)} } |
1 2 3 4 5 6 7 8 | const yaratik1 = new Yaratik("Alian","Ben Alian ben bir yaratığım.",150) const robot2 = new Robot("Terminator","Görüşürüz bebeğim!",50) console.log(yaratik1.guc) robot2.saldir() |
Ancak kodu tekrarladığımızı görebilirsiniz ve bu optimal değil. Daha sonra tüm düşman türleri tarafından genişletilen bir ebeveyn “Düşman” sınıfı ilan etmek daha iyi bir yol olacaktır, şöyle:
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 | class Dusman{ constructor(guc) { this.guc = guc } saldir = () => { console.log(`Şuan ${this.guc} ile saldırıyorum`)} } class Yaratik extends Dusman{ //Sınıf Adı constructor(isim,ifade,guc){ //Sınıfın kurucu metodu(işlevi) super(guc) //Üst sınıfa iletiyoruz this.isim = isim this.ifade = ifade this.tur = "alian" } uc = () =>{ console.log("Zuuuuhaaaiiiihaaa!!") } //function(){ } anonim fonksiyon konus = () =>{ console.log(this.ifade) } } ..... |
Düşman sınıfının tıpkı diğerleri gibi göründüğünü görüyoruz. Parametreleri almak ve bunları özellik olarak atamak için yapıcı(consturtor) yöntemini kullanırız ve yöntemler basit işlevler gibi bildirilir.
Çocuk sınıfında, kalıtım almak istediğimiz ebeveyn sınıfı bildirmek için extends anahtar sözcüğünü kullanırız. Daha sonra yapıcı yönteminde, “guc” parametresini bildirmeli ve özelliğin üst sınıfta bildirildiğini belirtmek için super işlevini kullanmalıyız.
Yeni nesneleri somutlaştırdığımızda, parametreleri karşılık gelen yapıcı işlevinde bildirildiği gibi iletiyoruz! Artık ebeveyn sınıfında açıklanan özelliklere ve yöntemlere erişebiliriz.😎

1 2 3 4 5 6 7 8 9 | const yaratik1 = new Yaratik("Alian","Ben Alian ben bir yaratığım.",150) const yaratik2 = new Yaratik("Predator","Saklanacak yer arayın!!",75) console.log(yaratik2.guc) yaratik1.saldir() |
Şimdi, tüm karakterlerimizi (düşman olsalar da olmasalar da) gruplayan yeni bir ebeveyn sınıf eklemek istediğimizi ve “hız” ve “hareket” yöntemini bir özellik ayarlamak istediğimizi varsayalım. Bunu şu şekilde yapabiliriz:
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 | class Karakter { constructor (hiz) { this.hiz = hiz } hareket = () => console.log(`${this.speed} hız ile gidiyorum!`) } class Dusman extends Karakter{ constructor(guc,hiz) { super(hiz) this.guc = guc } saldir = () => { console.log(`Şuan ${this.guc} ile saldırıyorum`)} } class Yaratik extends Dusman{ //Sınıf Adı constructor(isim,ifade,guc){ //Sınıfın kurucu metodu(işlevi) super(guc) //Üst sınıfa iletiyoruz this.isim = isim this.ifade = ifade this.tur = "alian" } uc = () =>{ console.log("Zuuuuhaaaiiiihaaa!!") } //function(){ } anonim fonksiyon konus = () =>{ console.log(this.ifade) } } |
İlk önce yeni “Karakter” ebeveyn sınıfını ilan ediyoruz. Ardından Dusman sınıfına genişletiyoruz. Ve son olarak Yaratik sınıfımızdaki consturtor ve super işlevlere yeni “hiz” parametresini ekliyoruz.
Parametreleri her zamanki gibi geçirmeyi başlatıyoruz ve işte yine, “büyükbaba” sınıfından özelliklere ve yöntemlere erişebiliriz.👴
Artık kalıtım hakkında daha fazla bilgi sahibi olduğumuza göre, mümkün olduğunca kod tekrarından kaçınmak için kodumuzu yeniden düzenleyelim:
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 | class Karakter { constructor (hiz) { this.hiz = hiz } hareket = () => console.log(`${this.speed} hız ile gidiyorum!`) } class Dusman extends Karakter{ constructor(isim,ifade,guc,hiz) { super(hiz) this.isim = isim this.ifade = ifade this.guc = guc } konus = () =>{ console.log(this.ifade) } saldir = () => { console.log(`Şuan ${this.guc} ile saldırıyorum`)} } class Yaratik extends Dusman{ constructor(isim,ifade,guc,hiz){ super(isim,ifade,guc,hiz) this.tur = "alian" } uc = () =>{ console.log("Zuuuuhaaaiiiihaaa!!") } } class Bocek extends Dusman{ constructor(isim,ifade,guc,hiz){ super(isim,ifade,guc,hiz) this.tur = "bug" } gizlen = () => { console.log("Beni şimdi yakalayamazsın!")} } class Robot extends Dusman{ constructor(isim,ifade,guc,hiz){ super(isim,ifade,guc,hiz) this.tur = "robot" } donus = () => { console.log("Optimus prime!")} } const yaratik1 = new Yaratik("Alian", "Benim adım Alian!", 10, 50) const yaratik2 = new Yaratik("Predator", "Yaşamak için kaçın!!", 15, 60) const bocek1 = new Bocek("Buggy", "Hata ayıklayıcınız benimle çalışmıyor!", 25, 100) const bocek2 = new Bocek("Erik", "Kafeinsiz içerim!", 5, 120) const robot1 = new Robot("Tito", "Yemek yapabilirim, yüzebilir ve dans edebilirim!", 125, 30) const robot2 = new Robot("Terminator", "Hasta la vista, baby!", 155, 40) |
Tüm paylaşılan özellikleri ve yöntemleri ortak bir üst sınıfa taşıdığımız için tür sınıflarımızın artık çok daha küçük göründüğünü görün. İşte bu tür bir verimlilik mirası bize yardımcı olabilir.😉
Miras hakkında akılda tutulması gereken bazı şeyler:
- Bir sınıfın miras alacağı yalnızca bir ebeveyn sınıfı olabilir. Bunun etrafından geçecek ve yollar olsa da, birden fazla sınıfı genişletemezsiniz.
- Ebeveyn, büyük ebeveyn, büyük büyük ebeveyn sınıfları vb. ayarlayarak miras zincirini istediğiniz kadar uzatabilirsiniz.
- Bir alt sınıf, bir üst sınıftan herhangi bir özelliği miras alırsa, kendi özelliklerini atamadan önce
super()işlevini çağıran üst özellikleri atamalıdır.
Örnek(Çalışır):
1 2 3 4 5 6 7 8 9 | class Yaratik extends Dusman{ constructor(isim,ifade,guc,hiz){ super(isim,ifade,guc,hiz) //önce super() kullanılmalıdır this.tur = "alian" } uc = () =>{ console.log("Zuuuuhaaaiiiihaaa!!") } } |
Örnek(Hata Veririr):
1 2 3 4 5 6 7 8 9 | class Yaratik extends Dusman{ constructor(isim,ifade,guc,hiz){ this.tur = "alian" super(isim,ifade,guc,hiz) //önce super() kullanılmalıdır } uc = () =>{ console.log("Zuuuuhaaaiiiihaaa!!") } } |
- Miras alınırken, tüm ebeveyn metotları ve özellikleri, çocuklar tarafından miras alınır. Bir ebeveyn sınıfından neyi miras alacağımıza karar veremiyoruz (aynı şekilde ebeveynlerimizden hangi erdemleri ve kusurları alacağımızı seçemiyoruz. 😅 Kompozisyon hakkında konuştuğumuzda buna geri döneceğiz).
- Alt sınıflar, ebeveynin özelliklerini ve yöntemlerini geçersiz kılabilir.
Örnek vermek gerekirse, önceki kodumuzda Yaratik sınıfı Dusman sınıfını genişletiyor ve ${this.guc}! gücüyle saldırdığımı kaydeden saldırı yöntemini devralıyor:
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 | class Karakter { constructor (hiz) { this.hiz = hiz } hareket = () => console.log(`${this.speed} hız ile gidiyorum!`) } class Dusman extends Karakter{ constructor(isim,ifade,guc,hiz) { super(hiz) this.isim = isim this.ifade = ifade this.guc = guc } konus = () =>{ console.log(this.ifade) } saldir = () => { console.log(`Şuan ${this.guc} ile saldırıyorum`)} } class Yaratik extends Dusman{ constructor(isim,ifade,guc,hiz){ super(isim,ifade,guc,hiz) this.tur = "alian" } uc = () =>{ console.log("Zuuuuhaaaiiiihaaa!!") } } |
Yaratik sınıfımızda saldir yönteminin farklı bir şey yapmasını istediğimizi varsayalım. Bunu tekrar bildirerek geçersiz kılabiliriz, şöyle:
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 | class Karakter { constructor (hiz) { this.hiz = hiz } hareket = () => console.log(`${this.speed} hız ile gidiyorum!`) } class Dusman extends Karakter{ constructor(isim,ifade,guc,hiz) { super(hiz) this.isim = isim this.ifade = ifade this.guc = guc } konus = () =>{ console.log(this.ifade) } saldir = () => { console.log(`Şuan ${this.guc} ile saldırıyorum`)} } class Yaratik extends Dusman{ constructor(isim,ifade,guc,hiz){ super(isim,ifade,guc,hiz) this.tur = "alian" } uc = () =>{ console.log("Zuuuuhaaaiiiihaaa!!") } saldir = () => console.log("Şimdi daha farklı bir saldırı yapıyorum :)") // Override method. } |
1 2 3 4 | const yaratik1 = new Yaratik("Predator", "Şimdi kaçmaya başla", 10, 50) yaratik1.saldir() // çıktı: "Şimdi daha farklı bir saldırı yapıyorum :)!" |
JavaScript Encapsulation – Kapsülleme
Kapsülleme, OOP’deki diğer bir anahtar kavramdır ve bir nesnenin hangi bilgileri “dışarıya” maruz bırakıp hangilerini göstermeyeceğine “karar verme” kapasitesini temsil eder. Kapsülleme, public ve private mülkler ve yöntemlerle uygulanır.
JavaScript’te tüm nesnelerin özellikleri ve yöntemleri varsayılan olarak herkese açıktır. “Public“, bir nesnenin özelliğine/yöntemine kendi gövdesinin dışından erişebileceğimiz anlamına gelir:
1 2 3 4 5 | const yaratik1 = new Yaratik("Predator", "Şimdi kaçmaya başla", 10, 50) console.log(yaratik1.isim) yaratik1.konus() |
Bunu daha açık hale getirmek için, özel mülklerin ve yöntemlerin nasıl göründüğüne bakalım.
Diyelim ki Yaratik sınıfımızın bir dogumYil özelliğine sahip olmasını istiyoruz ve bu özelliği bir yasHesap yöntemini yürütmek için kullanıyoruz, ancak bu özelliğin nesnenin kendisinden başka herhangi bir yerden erişilebilir olmasını istemiyoruz. Bunu şöyle uygulayabiliriz:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | class Yaratik extends Dusman{ #dogumYil //private mülkleri # ile tanımlıyoruz.... constructor(isim,ifade,guc,hiz,dogumYil){ super(isim,ifade,guc,hiz) this.tur = "alian" this.#dogumYil = dogumYil } uc = () =>{ console.log("Zuuuuhaaaiiiihaaa!!") } saldir = () => console.log("Şimdi daha farklı bir saldırı yapıyorum :)") // Override method. yasHesap=()=>{console.log(`Doğum yılım: ${this.#dogumYil}`)} } |
Ardından yasHesap yöntemine şu şekilde erişebiliriz:
1 2 3 4 | const yaratik1 = new Yaratik("Predator", "Şimdi kaçmaya başla", 10, 50,10500) yaratik1.yasHesap() |
Ancak mülke doğrudan erişmeye çalışırsak bir hata alırız. Ve nesneyi günlüğe kaydedersek özel mülk görünmeyecektir.
1 2 3 4 | const yaratik1 = new Yaratik("Predator", "Şimdi kaçmaya başla", 10, 50,10500) console.log(yaratik1) |

Kapsülleme, nesnenin iç işleyişi için belirli özelliklere veya yöntemlere ihtiyaç duyduğumuz durumlarda yararlıdır, ancak bunu dışarıya maruz bırakmak istemiyoruz. Özel mülklere/yöntemlere sahip olmak, istemediğimiz bilgileri “yanlışlıkla” ifşa etmememizi sağlar.
JavaScript Soyutlama -Abstraction
Soyutlama, bir sınıfın yalnızca problemin bağlamıyla ilgili bilgileri temsil etmesi gerektiğini söyleyen bir ilkedir. Sade İngilizce olarak, kullanacağınız özellikleri ve yöntemleri yalnızca dışarıya gösterin. Gerekli değilse ifşa etmeyin.
Neyin açığa çıkıp çıkmayacağını belirlemek için kamuya açık ve özel mülkleri/yöntemleri kullanabileceğimizden, bu ilke kapsüllemeyle yakından ilgilidir.
JavaScript Polimorfizm – Polymorphism
Sonra polimorfizm var (kulağa gerçekten karmaşık geliyor, değil mi? OOP isimleri en havalı… 🙃). Polimorfizm “birçok form” anlamına gelir ve aslında basit bir kavramdır. Belirli koşullara göre bir yöntemin farklı değerler döndürme yeteneğidir.
Örneğin Dusman sınıfının konus yöntemine sahip olduğunu gördük. Ve tüm tür sınıflarımız Dusman sınıfından miras alır; bu, hepsinin de konus yöntemine sahip olduğu anlamına gelir.
Ancak yöntemi farklı türler üzerinde çağırdığımızda farklı sonuçlar aldığımızı görebiliriz:
1 2 3 4 5 6 7 | const yaratik1 = new Yaratik("Lien", "Yaşamak için kaç", 15, 60) const bocek1 = new Bocek("Buggy", "Hata ayıklayıcınız benimle çalışmıyor!", 25, 100) yaratik1.konus() //Yaşamak için kaç bocek1.konus() //Hata ayıklayıcınız benimle çalışmıyor! |
Bunun nedeni, örnekleme sırasında her sınıfa farklı bir parametre iletmiş olmamızdır. Bu, parametre tabanlı bir tür polimorfizmdir. 👌
Başka bir polimorfizm türü kalıtım tabanlıdır ve bu, bir yöntem belirleyen bir ebeveyn sınıfımız olduğunda ve çocuğun onu bir şekilde değiştirmek için bu yöntemi geçersiz kıldığı anlamına gelir. Daha önce gördüğümüz örnek burada da mükemmel şekilde geçerlidir:
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 | class Karakter { constructor (hiz) { this.hiz = hiz } hareket = () => console.log(`${this.speed} hız ile gidiyorum!`) } class Dusman extends Karakter{ constructor(isim,ifade,guc,hiz) { super(hiz) this.isim = isim this.ifade = ifade this.guc = guc } konus = () =>{ console.log(this.ifade) } saldir = () => { console.log(`Şuan ${this.guc} ile saldırıyorum`)} } class Yaratik extends Dusman{ #dogumYil //private mülkleri # ile tanımlıyoruz.... constructor(isim,ifade,guc,hiz,dogumYil){ super(isim,ifade,guc,hiz) this.tur = "alian" this.#dogumYil = dogumYil } uc = () =>{ console.log("Zuuuuhaaaiiiihaaa!!") } saldir = () => console.log("Şimdi daha farklı bir saldırı yapıyorum :)") // Override method. yasHesap=()=>{console.log(`Doğum yılım: ${this.#dogumYil}`)} } |
Bu uygulama polimorfiktir, çünkü Yaratik sınıfındaki saldir yöntemini yorumlasaydık, yine de onu nesne üzerinde çağırabilirdik:
1 2 3 4 5 | const yaratik1 = new Yaratik("Lien", "Yaşamak için kaç!", 15, 60) yaratik1.saldir() |
Geçersiz kılınmış olup olmamasına bağlı olarak bir şeyi veya başka bir şeyi yapabilen aynı yöntemi aldık. polimorfik.👌👌
Nesne Bileşimi ( Nesne Kompozisyonu)
Nesne kompozisyonu, kalıtımın alternatifi olarak çalışan bir tekniktir.
Kalıtım hakkında konuştuğumuzda, alt sınıfların her zaman tüm ebeveyn yöntemlerini ve özelliklerini devraldığından bahsetmiştik. Pekala, kompozisyonu kullanarak, kalıtımın izin verdiğinden daha esnek bir şekilde nesnelere özellikler ve yöntemler atayabiliriz, böylece nesneler yalnızca ihtiyaç duyduklarını alır, başka bir şey değil.
Bunu, nesneyi parametre olarak alan ve ona istenen özelliği/yöntemi atayan işlevleri kullanarak oldukça basit bir şekilde uygulayabiliriz. Bir örnekte görelim.
Şimdi, böcek karakterlerimize uçma yeteneğini eklemek istediğimizi söyleyin. Kodumuzda gördüğümüz gibi, sadece uzaylılar uc yöntemine sahiptir. Bu nedenle, bir seçenek aynı yöntemi Bocek sınıfında çoğaltmak olabilir:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | class Yaratik extends Dusman{ #dogumYil //private mülkleri # ile tanımlıyoruz.... constructor(isim,ifade,guc,hiz,dogumYil){ super(isim,ifade,guc,hiz) this.tur = "alian" this.#dogumYil = dogumYil } uc = () =>{ console.log("Zuuuuhaaaiiiihaaa!!") } } class Bocek extends Dusman{ constructor(isim,ifade,guc,hiz){ super(isim,ifade,guc,hiz) this.tur = "bug" } gizlen = () => { console.log("Beni şimdi yakalayamazsın!")} uc = () =>{ console.log("Zuuuuhaaaiiiihaaa!!") } //tekrar yazdık :( } |
Diğer bir seçenek de uc yöntemini Dusman sınıfına taşımaktır, böylece hem Yaratik hem de Bocek sınıfları tarafından miras alınabilir. Ancak bu, aynı zamanda, Robot gibi, buna ihtiyaç duymayan sınıflar için de yöntemi kullanılabilir hale getirir.
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 | class Dusman extends Karakter{ constructor(isim,ifade,guc,hiz) { super(hiz) this.isim = isim this.ifade = ifade this.guc = guc } konus = () =>{ console.log(this.ifade) } saldir = () => { console.log(`Şuan ${this.guc} ile saldırıyorum`)} uc = () =>{ console.log("Zuuuuhaaaiiiihaaa!!") } } class Yaratik extends Dusman{ #dogumYil //private mülkleri # ile tanımlıyoruz.... constructor(isim,ifade,guc,hiz,dogumYil){ super(isim,ifade,guc,hiz) this.tur = "alian" this.#dogumYil = dogumYil } } class Bocek extends Dusman{ constructor(isim,ifade,guc,hiz){ super(isim,ifade,guc,hiz) this.tur = "bug" } gizlen = () => { console.log("Beni şimdi yakalayamazsın!")} } class Robot extends Dusman{ constructor(isim,ifade,guc,hiz){ super(isim,ifade,guc,hiz) this.tur = "robot" } donus = () => { console.log("Optimus prime!")} //uc() metoduna buradan da ulaşabilir hale geldik :( } |
Gördüğünüz gibi, kalıtım, sınıflarımız için sahip olduğumuz başlangıç planımız değiştiğinde (gerçek dünyada hemen hemen her zaman böyledir) sorunlara neden olur. Nesne bileşimi, nesnelerin yalnızca ihtiyaç duyduklarında atanan özellikleri ve yöntemleri aldığı bir yaklaşım önerir.
Örneğimizde, bir fonksiyon yaratabilirdik ve onun tek sorumluluğu, parametre olarak alan herhangi bir nesneye uc metodu eklemek olacaktır:
1 2 3 4 5 6 7 8 9 10 11 | const ucmaYetenegiEkle = nesne =>{ nesne.uc = () => { console.log("Ben de artık uçabiliyorum!") } } const bocek1 = new Bocek("Buggy", "Hata yakalayıclarınız bende çalışmaz!", 25, 100) ucmaYetenegiEkle(bocek1) bocek1.uc() |
Ve canavarlarımızın sahip olmasını isteyebileceğimiz her güç veya yetenek için çok benzer işlevlere sahip olabiliriz.
Kesinlikle görebileceğiniz gibi, bu yaklaşım, kalıtım için sabit özelliklere ve yöntemlere sahip üst sınıflara sahip olmaktan çok daha esnektir. Bir nesne bir metoda ihtiyaç duyduğunda, sadece ilgili fonksiyonu çağırırız ve o kadar.
Son Sözler
OOP, varlıkların soyutlanmasını oluşturarak büyük projelerin üstesinden gelmemize yardımcı olabilecek çok güçlü bir programlama paradigmasıdır. Her varlık belirli bilgi ve eylemlerden sorumlu olacak ve varlıklar, gerçek dünyanın nasıl çalıştığına çok benzer şekilde birbirleriyle etkileşime girebilecekler.
Bu yazıda sınıflar, kalıtım, kapsülleme, soyutlama, polimorfizm ve kompozisyon hakkında bilgi edindik. Bunların hepsi OOP dünyasındaki anahtar kavramlardır. Ayrıca OOP’nin JavaScript’te nasıl uygulanabileceğine dair çeşitli örnekler gördük.
Sitemizi takipte kalın…












2 Yorum