JavaScript ile Design Patterns (Tasarım Kalıpları)


Giriş

Yazılım geliştirme sürecinde tasarım kalıpları (design patterns), sık karşılaşılan problemleri çözmek için kullanılan, tekrarlanabilir ve test edilmiş çözümlerdir. JavaScript gibi esnek ve dinamik bir dilde, doğru tasarım kalıplarını kullanmak, kodun okunabilirliğini, sürdürülebilirliğini ve ölçeklenebilirliğini artırır.

Bu kapsamlı rehberde, JavaScript'teki en yaygın tasarım kalıplarını detaylıca inceleyecek, her birini örnek kodlarla açıklayacak ve hangi durumlarda kullanmanız gerektiğini öğreneceksiniz.


1. Creational (Yaratıcı) Tasarım Kalıpları

1.1 Singleton Pattern

Amaç: Uygulamada tek bir nesne örneği oluşturmak ve bu nesneye global erişim sağlamak.

const Singleton = (function () {
  let instance;

  function createInstance() {
    return { name: "Singleton Instance" };
  }

  return {
    getInstance: function () {
      if (!instance) {
        instance = createInstance();
      }
      return instance;
    }
  };
})();

// Kullanım
const instance1 = Singleton.getInstance();
const instance2 = Singleton.getInstance();

console.log(instance1 === instance2); // true

Ne Zaman Kullanılır?

  • Tek bir global nesneye ihtiyaç duyulduğunda (örneğin, bir konfigürasyon yöneticisi).

1.2 Factory Pattern

Amaç: Nesne oluşturma sürecini soyutlayarak, hangi sınıfın örneğinin oluşturulacağını dinamik olarak belirlemek.

class Car {
  constructor() {
    this.type = 'Car';
  }
}

class Truck {
  constructor() {
    this.type = 'Truck';
  }
}

class VehicleFactory {
  static createVehicle(type) {
    switch (type) {
      case 'car':
        return new Car();
      case 'truck':
        return new Truck();
      default:
        throw new Error('Invalid vehicle type');
    }
  }
}

const myCar = VehicleFactory.createVehicle('car');
console.log(myCar.type); // Car

Ne Zaman Kullanılır?

  • Nesne oluşturma sürecinin soyutlanması gerektiğinde.

2. Structural (Yapısal) Tasarım Kalıpları

2.1 Module Pattern

Amaç: Değişken ve fonksiyonları kapsülleştirerek, global scope’u kirletmeden veri saklamak.

const Module = (function () {
  let privateVar = 'Gizli Veri';

  return {
    getPrivateVar: function () {
      return privateVar;
    },
    setPrivateVar: function (value) {
      privateVar = value;
    }
  };
})();

console.log(Module.getPrivateVar()); // Gizli Veri
Module.setPrivateVar('Yeni Veri');
console.log(Module.getPrivateVar()); // Yeni Veri

Ne Zaman Kullanılır?

  • Global değişkenlerin kirlenmesini önlemek için.

2.2 Decorator Pattern

Amaç: Mevcut bir nesnenin davranışını, alt sınıf oluşturmadan dinamik olarak genişletmek.

function car() {
  this.cost = function () {
    return 20000;
  };
}

function sunroof(carInstance) {
  const originalCost = carInstance.cost();
  carInstance.cost = function () {
    return originalCost + 1500;
  };
}

const myCar = new car();
sunroof(myCar);
console.log(myCar.cost()); // 21500

Ne Zaman Kullanılır?

  • Bir nesnenin davranışını dinamik olarak değiştirmek gerektiğinde.

3. Behavioral (Davranışsal) Tasarım Kalıpları

3.1 Observer Pattern

Amaç: Bir nesnedeki değişiklikleri, ona bağlı diğer nesnelere otomatik olarak bildirmek.

class Subject {
  constructor() {
    this.observers = [];
  }

  subscribe(observer) {
    this.observers.push(observer);
  }

  unsubscribe(observer) {
    this.observers = this.observers.filter(obs => obs !== observer);
  }

  notify(data) {
    this.observers.forEach(observer => observer.update(data));
  }
}

class Observer {
  update(data) {
    console.log(`Observer received data: ${data}`);
  }
}

const subject = new Subject();
const observer1 = new Observer();
const observer2 = new Observer();

subject.subscribe(observer1);
subject.subscribe(observer2);

subject.notify('Yeni Veri'); // İki observer da bildirimi alır.

Ne Zaman Kullanılır?

  • Bir nesne değiştiğinde, bu değişikliğin diğer nesnelere bildirilmesi gerektiğinde.

3.2 Strategy Pattern

Amaç: Algoritmaların birbirinin yerine kullanılabilmesini sağlamak.

class Shipping {
  setStrategy(strategy) {
    this.strategy = strategy;
  }

  calculate(package) {
    return this.strategy.calculate(package);
  }
}

class FedEx {
  calculate(package) {
    return package.weight * 10;
  }
}

class UPS {
  calculate(package) {
    return package.weight * 12;
  }
}

const package = { weight: 5 };
const shipping = new Shipping();

shipping.setStrategy(new FedEx());
console.log(shipping.calculate(package)); // 50

shipping.setStrategy(new UPS());
console.log(shipping.calculate(package)); // 60

Ne Zaman Kullanılır?

  • Değişken algoritmaların dinamik olarak seçilmesi gerektiğinde.

4. JavaScript Tasarım Kalıpları Kullanımında İpuçları

  • DRY Prensibini Kullanın: Kod tekrarından kaçının.
  • Kapsülleme Sağlayın: Gereksiz dış erişimlerden kaçının.
  • Modüler Düşünün: Her kalıbı bağımsız modüller şeklinde düşünün.
  • Test Edilebilir Kod Yazın: Her tasarım kalıbı için uygun birimler oluşturun.

Sonuç

JavaScript'te tasarım kalıpları kullanmak, uygulamalarınızın sürdürülebilirliğini ve ölçeklenebilirliğini artırır. Doğru kalıpları doğru yerlerde kullanarak daha temiz, daha bakımı kolay ve daha profesyonel kodlar yazabilirsiniz.

Bu rehberde incelediğimiz kalıplar, JavaScript projelerinizde daha etkili kodlar yazmanız için temel bir başlangıç olacaktır.


Sizin Favori Tasarım Kalıbınız Hangisi?

Yorumlar kısmında paylaşın! ????