← Back

TypeScript ile Nesne Yönelimli Programlama (OOP)

July 28, 2024 (3mo ago)

Merhaba, yeniliklerin peşinde koşanlar! TypeScript dünyasında nesne yönelimli programlamayı (OOP) keşfetmeye hazır mısınız? TypeScript ile OOP, kod yazmayı daha eğlenceli ve etkili hale getiriyor. Hem de bu yazıda sıkıcı teknik terimlere boğulmadan, basit ve anlaşılır bir şekilde OOP'nin temel prensiplerini inceleyeceğiz. Hazırsanız başlayalım! 🚀

OOP Nedir? 🎓

OOP, yani Nesne Yönelimli Programlama, yazılım geliştirmenin bir yolu. Aslında hayatımızda sıkça karşılaştığımız bir kavram. Örneğin, arabalar, telefonlar veya insanlar gibi nesneleri düşünün. Her nesnenin kendine ait özellikleri (properties) ve yapabileceği işler (methods) vardır. İşte OOP de yazılımda bu mantığı kullanır.

TypeScript ve OOP 💻

TypeScript, JavaScript'in üzerine inşa edilmiş ve statik tip kontrolü ekleyen bir dil. TypeScript'in sunduğu tip güvenliği ve güçlü OOP desteği sayesinde, kodlarımız daha sağlam ve okunabilir oluyor. Hadi şimdi, OOP'nin prensiplerini TypeScript ile nasıl kullanacağımıza bakalım.

1. Classes (Sınıflar) 🏫

Classes, nesnelerin planı gibidir. Bir sınıf, nesnenin özelliklerini ve davranışlarını tanımlar. Mesela, bir Araba sınıfı oluşturalım:

class Araba {
  marka: string;
  model: string;
  yil: number;
 
  constructor(marka: string, model: string, yil: number) {
    this.marka = marka;
    this.model = model;
    this.yil = yil;
  }
 
  sur() {
    console.log(`${this.marka} ${this.model} sürülüyor! 🚗`);
  }
}
 
const benimArabam = new Araba("Toyota", "Supra", 2020);
benimArabam.sur(); // Toyota Supra sürülüyor! 🚗

2. Inheritance (Kalıtım) 👪

Inheritance, bir sınıfın başka bir sınıftan özellik ve davranışları miras almasıdır. Mesela, ElektrikliAraba sınıfı Araba sınıfından miras alabilir:

class ElektrikliAraba extends Araba {
  bataryaKapasitesi: number;
 
  constructor(
    marka: string,
    model: string,
    yil: number,
    bataryaKapasitesi: number
  ) {
    super(marka, model, yil);
    this.bataryaKapasitesi = bataryaKapasitesi;
  }
 
  sarjEt() {
    console.log(`${this.marka} ${this.model} şarj ediliyor! 🔋`);
  }
}
 
const tesla = new ElektrikliAraba("Tesla", "Model S", 2021, 100);
tesla.sur(); // Tesla Model S sürülüyor! 🚗
tesla.sarjEt(); // Tesla Model S şarj ediliyor! 🔋

3. Encapsulation (Kapsülleme) 🔒

Encapsulation, verilerin dışarıdan erişilmesini engellemek ve sadece belirli yöntemlerle erişilmesini sağlamaktır. TypeScript'te private ve protected anahtar kelimeleri ile kapsülleme yapabiliriz:

class BankaHesabi {
  private bakiye: number;
 
  constructor(bakiye: number) {
    this.bakiye = bakiye;
  }
 
  paraYatir(miktar: number) {
    this.bakiye += miktar;
    console.log(`Yeni bakiye: ${this.bakiye} 💰`);
  }
 
  bakiyeGoruntule() {
    console.log(`Mevcut bakiye: ${this.bakiye} 💰`);
  }
}
 
const hesap = new BankaHesabi(1000);
hesap.paraYatir(500); // Yeni bakiye: 1500 💰
hesap.bakiyeGoruntule(); // Mevcut bakiye: 1500 💰

4. Polymorphism (Çok Biçimlilik) 🦸‍♂️

Polymorphism, aynı yöntem adının farklı sınıflarda farklı işlevler gerçekleştirebilmesidir. Örneğin:

class Hayvan {
  sesCikar() {
    console.log("Hayvan sesi çıkarıyor.");
  }
}
 
class Kedi extends Hayvan {
  sesCikar() {
    console.log("Miyav! 🐱");
  }
}
 
class Kopek extends Hayvan {
  sesCikar() {
    console.log("Hav! 🐶");
  }
}
 
const hayvanlar: Hayvan[] = [new Kedi(), new Kopek()];
 
hayvanlar.forEach((hayvan) => {
  hayvan.sesCikar();
});
 
// Miyav! 🐱
// Hav! 🐶

5. Interfaces (Arabirimler) 📄

Interfaces, sınıfların belirli bir yapıya sahip olmasını sağlar ve kodun daha tutarlı olmasına yardımcı olur. Mesela, Ucan arayüzünü inceleyelim:

interface Ucan {
  kanatSayisi: number;
  uc(): void;
}
 
class Kus implements Ucan {
  kanatSayisi: number;
 
  constructor(kanatSayisi: number) {
    this.kanatSayisi = kanatSayisi;
  }
 
  uc() {
    console.log("Kuş uçuyor! 🕊️");
  }
}
 
const serce = new Kus(2);
serce.uc(); // Kuş uçuyor! 🕊️

6. Abstraction (Soyutlama) 🔍

Abstraction, karmaşıklığı azaltmak ve sadece gerekli detayları ortaya çıkarmaktır. Soyut sınıflar (abstract classes) bu prensibi destekler:

abstract class Sekil {
  abstract alan(): number;
  abstract cevre(): number;
 
  tanit() {
    console.log(`Bu bir şekil.`);
  }
}
 
class Dikdortgen extends Sekil {
  private en: number;
  private boy: number;
 
  constructor(en: number, boy: number) {
    super();
    this.en = en;
    this.boy = boy;
  }
 
  alan() {
    return this.en * this.boy;
  }
 
  cevre() {
    return 2 * (this.en + this.boy);
  }
}
 
const dikdortgen = new Dikdortgen(5, 10);
dikdortgen.tanit(); // Bu bir şekil.
console.log(`Alan: ${dikdortgen.alan()}`); // Alan: 50
console.log(`Çevre: ${dikdortgen.cevre()}`); // Çevre: 30

7. Method Overloading (Metot Aşırı Yükleme) 🔄

Method overloading, aynı isimde birden fazla metot tanımlayarak farklı parametreler ile kullanılmasını sağlar:

class HesapMakinesi {
  topla(a: number, b: number): number;
  topla(a: string, b: string): string;
  topla(a: any, b: any): any {
    return a + b;
  }
}
 
const hesap = new HesapMakinesi();
console.log(hesap.topla(1, 2)); // 3
console.log(hesap.topla("Hello", "World")); // HelloWorld

8. Access Modifiers (Erişim Belirleyiciler) 🛡️

TypeScript'te access modifiers (public, private, protected) ile üyelerin erişim seviyelerini belirleyebiliriz:

class Kisi {
  public ad: string;
  private yas: number;
  protected meslek: string;
 
  constructor(ad: string, yas: number, meslek: string) {
    this.ad = ad;
    this.yas = yas;
    this.meslek = meslek;
  }
 
  public bilgiVer() {
    console.log(`Ad: ${this.ad}, Yaş: ${this.yas}, Meslek: ${this.meslek}`);
  }
 
  protected meslekGuncelle(yeniMeslek: string) {
    this.meslek = yeniMeslek;
  }
}
 
class Calisan extends Kisi {
  constructor(ad: string, yas: number, meslek: string) {
    super(ad, yas, meslek);
  }
 
  yeniMeslek(meslek: string) {
    this.meslekGuncelle(meslek);
  }
}
 
const ali = new Calisan("Ali", 30, "Mühendis");
ali.bilgiVer(); // Ad: Ali, Yaş: 30, Meslek: Mühendis
ali.yeniMeslek("Yönetici");
ali.bilgiVer(); // Ad: Ali, Yaş: 30, Meslek: Yönetici

9. Static Methods and Properties (Statik Metotlar ve Özellikler) ⚙️

Static methods and properties, sınıfın örneği olmadan doğrudan sınıf üzerinden erişilebilir:

class Matematik {
  static PI: number = 3.14;
 
  static daireAlani(yaricap: number): number {
    return this.PI * yaricap * yaricap;
  }
}
 
console.log(`Pi: ${Matematik.PI}`); // Pi: 3.14
console.log(`Daire Alanı: ${Matematik.daireAlani(5)}`); // Daire Alanı: 78.5

10. Generics (Genel Tipler) 🧩

Generics, tip güvenliğini koruyarak kodun yeniden kullanılabilirliğini artırır:

class Depo<T> {
  private esyalar: T[] = [];
 
  ekle(esya: T) {
    this.esyalar.push(esya);
  }
 
  al(): T {
    return this.esyalar.pop();
  }
}
 
const sayiDepo = new Depo<number>();
sayiDepo.ekle(5);
sayiDepo.ekle(10);
console.log(sayiDepo.al()); // 10
 
const kelimeDepo = new Depo<string>();
kelimeDepo.ekle("Merhaba");
kelimeDepo.ekle("Dünya");
console.log(kelimeDepo.al()); // Dünya

11. Mixins (Karışımlar) 🎨

Mixins, birden fazla sınıfın özelliklerini ve metodlarını bir sınıfta birleştirmek için kullanılır. TypeScript'te mixins kullanarak sınıfları genişletebiliriz:

class Canli {
  yas: number;
 
  constructor(yas: number) {
    this.yas = yas;
  }
 
  yaslandir() {
    this.yas++;
  }
}
 
class Yuruyebilir {
  yuru() {
    console.log("Yürüyor... 🚶‍♂️");
  }
}
 
class Konusabilir {
  konus() {
    console.log("Konuşuyor... 🗣️");
  }
}
 
class Insan implements Canli, Yuruyebilir, Konusabilir {
  yas: number = 0;
 
  constructor(yas: number) {
    // Canli constructor'ını çağır
    this.yas = yas;
  }
 
  yaslandir: () => void;
  yuru: () => void;
  konus: () => void;
}
 
// Mixins işlevini ekle
applyMixins(Insan, [Canli, Yuruyebilir, Konusabilir]);
 
function applyMixins(derivedCtor: any, baseCtors: any[]) {
  baseCtors.forEach((baseCtor) => {
    Object.getOwnPropertyNames(baseCtor.prototype).forEach((name) => {
      derivedCtor.prototype[name] = baseCtor.prototype[name];
    });
  });
}
 
const ali = new Insan(30);
ali.yuru(); // Yürüyor... 🚶‍♂️
ali.konus(); // Konuşuyor... 🗣️
ali.yaslandir();
console.log(ali.yas); // 31

12. Decorators (Dekoratörler) 🎁

Decorators, sınıfları ve metotları değiştirmek için kullanılan özel türde fonksiyonlardır. TypeScript'te dekoratörlerle meta-veri ekleyebilir veya mevcut işlevselliği genişletebiliriz:

function log(target: any, key: string, descriptor: PropertyDescriptor) {
  const original = descriptor.value;
 
  descriptor.value = function (...args: any[]) {
    console.log(`Çağrılan metot: ${key}, Argümanlar: ${args}`);
    return original.apply(this, args);
  };
 
  return descriptor;
}
 
class Hesaplama {
  @log
  topla(a: number, b: number) {
    return a + b;
  }
}
 
const hesaplama = new Hesaplama();
console.log(hesaplama.topla(5, 3)); // Çağrılan metot: topla, Argümanlar: 5,3 | 8

13. Namespaces (Ad Alanları) 🏷️

Namespaces, kodu modüllere ayırarak daha düzenli ve yönetilebilir hale getirir. TypeScript'te namespace kullanarak kodumuzu bölümlere ayırabiliriz:

namespace Matematik {
  export function topla(a: number, b: number): number {
    return a + b;
  }
 
  export function carp(a: number, b: number): number {
    return a * b;
  }
}
 
console.log(Matematik.topla(5, 3)); // 8
console.log(Matematik.carp(5, 3)); // 15

14. Module Resolution (Modül Çözümlemesi) 🔍

TypeScript, modülleri çözümlemek ve dışa aktarılan fonksiyonlar, sınıflar veya değişkenler arasında bağlantı kurmak için çeşitli stratejiler sunar. Bu, büyük projelerde kodu organize etmek için önemlidir:

// math.ts
export function topla(a: number, b: number): number {
  return a + b;
}
 
// main.ts
import { topla } from "./math";
 
console.log(topla(5, 3)); // 8

15. Advanced Types (İleri Seviye Tipler) 🧬

TypeScript, birleşim (union) ve kesişim (intersection) tipleri, mapped tipler ve conditional tipler gibi ileri seviye tip sistemleri sunar:

// Union Types
function printId(id: number | string) {
  console.log(`ID: ${id}`);
}
 
printId(101); // ID: 101
printId("202"); // ID: 202
 
// Intersection Types
interface Kisi {
  ad: string;
}
 
interface Calisan {
  sirket: string;
}
 
type CalisanKisi = Kisi & Calisan;
 
const yeniCalisan: CalisanKisi = {
  ad: "Ali",
  sirket: "ABC Şirketi",
};
 
console.log(yeniCalisan); // { ad: 'Ali', sirket: 'ABC Şirketi' }

16. Type Guards (Tip Koruyucuları) 🛡️

Type guards, belirli bir tipte olup olmadığını kontrol eden fonksiyonlardır. Bu, kodun güvenli bir şekilde çalışmasını sağlar:

function isNumber(value: any): value is number {
  return typeof value === "number";
}
 
function printValue(value: number | string) {
  if (isNumber(value)) {
    console.log(`Sayı: ${value}`);
  } else {
    console.log(`Metin: ${value}`);
  }
}
 
printValue(123); // Sayı: 123
printValue("Merhaba"); // Metin: Merhaba

17. Conditional Types (Koşullu Tipler) 🛠️

Conditional types, bir tipin başka bir tipe bağlı olarak nasıl belirlendiğini tanımlar:

type IsString<T> = T extends string ? "Evet" : "Hayır";
 
type A = IsString<string>; // 'Evet'
type B = IsString<number>; // 'Hayır'

18. Decorator Factories (Dekoratör Fabrikaları) 🏭

Decorator factories, parametre kabul eden ve bir dekoratör fonksiyonu döndüren fonksiyonlardır. Bu, dekoratörleri daha dinamik hale getirir:

function log(message: string) {
  return function (target: any, key: string, descriptor: PropertyDescriptor) {
    const original = descriptor.value;
 
    descriptor.value = function (...args: any[]) {
      console.log(`${message} - Metot: ${key}, Argümanlar: ${args}`);
      return original.apply(this, args);
    };
 
    return descriptor;
  };
}
 
class Hesaplama {
  @log("Toplama işlemi")
  topla(a: number, b: number) {
    return a + b;
  }
}
 
const hesaplama = new Hesaplama();
console.log(hesaplama.topla(5, 3)); // Toplama işlemi - Metot: topla, Argümanlar: 5,3 | 8

19. Reflect Metadata (Yansıma Meta Verisi) 📊

TypeScript, dekoratörlerle birlikte kullanılabilen yansıma (reflection) API'si sağlar. Bu, meta veri eklemeyi ve erişmeyi sağlar:

import "reflect-metadata";
 
const metakey = Symbol("metaKey");
 
function logType(target: any, key: string) {
  const type = Reflect.getMetadata("design:type", target, key);
  console.log(`${key} türü: ${type.name}`);
}
 
class Kisi {
  @logType
  ad: string;
 
  constructor(ad: string) {
    this.ad = ad;
  }
}
 
const ali = new Kisi("Ali");
// Konsolda: ad türü: String

20. Async/Await ve Promises ile OOP 🚀

TypeScript, asenkron programlamayı async/await ve Promises ile destekler. Bu, özellikle asenkron işlemleri yönetmek için çok önemlidir:

class VeriCekici {
  async veriCek(url: string): Promise<any> {
    const response = await fetch(url);
    const data = await response.json();
    return data;
  }
}
 
const cekici = new VeriCekici();
cekici
  .veriCek("https://api.example.com/data")
  .then((data) => console.log(data))
  .catch((error) => console.error(error));

21. Modular Structure (Modüler Yapı) 🏗️

Büyük projelerde kodun daha yönetilebilir olması için modüler yapı çok önemlidir. TypeScript'te kodu modüller halinde organize edebiliriz:

// moduler yapıda hesap.ts dosyası
export class Hesap {
  topla(a: number, b: number): number {
    return a + b;
  }
}
 
// moduler yapıda main.ts dosyası
import { Hesap } from "./hesap";
 
const hesap = new Hesap();
console.log(hesap.topla(10, 20)); // 30

22. Unit Testing (Birim Testleri) 🧪

Kodun güvenilirliğini sağlamak için birim testleri yazmak önemlidir. TypeScript, birim testleri için jest gibi test araçları ile uyumludur:

// hesap.test.ts
import { Hesap } from "./hesap";
 
test("topla metodu doğru şekilde çalışmalı", () => {
  const hesap = new Hesap();
  expect(hesap.topla(1, 2)).toBe(3);
});

23. Abstract Classes (Soyut Sınıflar) 🏛️

Abstract classes , temel işlevselliği tanımlar ancak kendileri doğrudan örneklenemez. Alt sınıflar, bu soyut sınıfı genişleterek somutlaştırır:

abstract class Hayvan {
  abstract sesCikar(): void;
 
  hareketEt() {
    console.log("Hareket ediyor...");
  }
}
 
class Kedi extends Hayvan {
  sesCikar() {
    console.log("Miyav! 🐱");
  }
}
 
class Kopek extends Hayvan {
  sesCikar() {
    console.log("Hav! 🐶");
  }
}
 
const kedi = new Kedi();
const kopek = new Kopek();
 
kedi.sesCikar(); // Miyav! 🐱
kopek.sesCikar(); // Hav! 🐶
kedi.hareketEt(); // Hareket ediyor...

24. Access Modifiers (Erişim Belirleyiciler) 🔒

TypeScript'te erişim belirleyiciler (public, private, protected) kullanarak sınıf üyelerinin erişim seviyesini kontrol edebiliriz:

class Araba {
  public marka: string;
  private hiz: number;
  protected yil: number;
 
  constructor(marka: string, hiz: number, yil: number) {
    this.marka = marka;
    this.hiz = hiz;
    this.yil = yil;
  }
 
  public hizlandir() {
    this.hiz += 10;
    console.log(`Yeni hız: ${this.hiz}`);
  }
 
  private hizGoster() {
    console.log(`Hız: ${this.hiz}`);
  }
 
  protected yilGoster() {
    console.log(`Yıl: ${this.yil}`);
  }
}
 
class SporAraba extends Araba {
  constructor(marka: string, hiz: number, yil: number) {
    super(marka, hiz, yil);
  }
 
  yilBilgisiGoster() {
    this.yilGoster(); // protected üye erişilebilir
  }
}
 
const araba = new Araba("Toyota", 120, 2020);
araba.hizlandir(); // Yeni hız: 130
// araba.hizGoster(); // Hata: private metoda erişilemez
// araba.yilGoster(); // Hata: protected metoda erişilemez
 
const sporAraba = new SporAraba("Ferrari", 200, 2021);
sporAraba.yilBilgisiGoster(); // Yıl: 2021

25. Polymorphism (Çok Biçimlilik) 🧩

Polymorphism, aynı metot ismi ile farklı işlevsellikler sunmamızı sağlar. Bu, alt sınıfların kendi özel davranışlarını tanımlayabilmesine olanak tanır:

class Sekil {
  alan(): number {
    return 0;
  }
}
 
class Daire extends Sekil {
  private yaricap: number;
 
  constructor(yaricap: number) {
    super();
    this.yaricap = yaricap;
  }
 
  alan(): number {
    return Math.PI * this.yaricap * this.yaricap;
  }
}
 
class Dikdortgen extends Sekil {
  private genislik: number;
  private yukseklik: number;
 
  constructor(genislik: number, yukseklik: number) {
    super();
    this.genislik = genislik;
    this.yukseklik = yukseklik;
  }
 
  alan(): number {
    return this.genislik * this.yukseklik;
  }
}
 
const sekiller: Sekil[] = [new Daire(5), new Dikdortgen(10, 20)];
 
sekiller.forEach((sekil) => {
  console.log(`Alan: ${sekil.alan()}`);
});
// Alan: 78.53981633974483
// Alan: 200

26. Singleton Pattern (Tekil Örnek Deseni) 🏢

Singleton Pattern, bir sınıfın yalnızca bir örneğinin oluşturulmasını garanti eder ve bu örneğe global erişim sağlar:

class Singleton {
  private static instance: Singleton;
 
  private constructor() {
    // private constructor, dışarıdan erişim engellenir
  }
 
  static getInstance(): Singleton {
    if (!Singleton.instance) {
      Singleton.instance = new Singleton();
    }
    return Singleton.instance;
  }
 
  islemYap() {
    console.log("İşlem yapılıyor...");
  }
}
 
const ornek1 = Singleton.getInstance();
const ornek2 = Singleton.getInstance();
 
ornek1.islemYap(); // İşlem yapılıyor...
console.log(ornek1 === ornek2); // true

27. Composition (Bileşim) 🧱

Composition, sınıflar arasında güçlü bir ilişki kurmak yerine, nesnelerin işlevselliğini birleştirerek daha esnek ve yeniden kullanılabilir kod yazmayı sağlar:

class Motor {
  calistir() {
    console.log("Motor çalıştırıldı. 🚗");
  }
}
 
class Araba {
  private motor: Motor;
 
  constructor(motor: Motor) {
    this.motor = motor;
  }
 
  sur() {
    this.motor.calistir();
    console.log("Araba sürülüyor...");
  }
}
 
const motor = new Motor();
const araba = new Araba(motor);
 
araba.sur(); // Motor çalıştırıldı. 🚗 | Araba sürülüyor...

28. SOLID Prensipleri 🏗️

SOLID prensipleri, yazılım geliştirmede daha iyi tasarımlar yapmamıza yardımcı olan beş prensibi içerir: Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation, ve Dependency Inversion. Bu prensipleri uygulayarak daha sürdürülebilir ve esnek yazılımlar geliştirebiliriz:

TypeScript ile OOP'nin ne kadar geniş ve kapsamlı olabileceğini gördük. Temel prensiplerden ileri düzey konulara kadar birçok konuyu ele aldık. Bu makale, TypeScript ve OOP hakkında kapsamlı bir bilgi edinmenize yardımcı olmayı hedefliyor. Umarım bu yazı, projelerinizde OOP tekniklerini daha ileri seviyede kullanmanız için size ilham verir. Bu yazıda öğrendiklerimizi burada noktalıyoruz. Bir sonraki keşifte buluşmak üzere! 🖥️👨‍💻👩‍💻

TypeScript
OOP
Nesne Yönelimli Programlama