Design Patterns: Yazılım Tasarım Kalıpları

Yazılım 📖 4 dk okuma
#design#javascript#kariyer

Design patterns, yazılım geliştirmede karşılaşılan yaygın problemlere kanıtlanmış çözümlerdir.

Creational Patterns

Singleton

Tek bir instance garanti eder:

class Database {
  static instance = null;
  
  constructor() {
    if (Database.instance) {
      return Database.instance;
    }
    
    this.connection = this.connect();
    Database.instance = this;
  }
  
  connect() {
    console.log('Connecting to database...');
    return { connected: true };
  }
}

const db1 = new Database();
const db2 = new Database();
console.log(db1 === db2); // true

Factory

Object oluşturmayı abstraktlar:

class UserFactory {
  createUser(type, data) {
    switch (type) {
      case 'admin':
        return new AdminUser(data);
      case 'customer':
        return new CustomerUser(data);
      case 'guest':
        return new GuestUser(data);
      default:
        throw new Error('Unknown user type');
    }
  }
}

const factory = new UserFactory();
const admin = factory.createUser('admin', { name: 'John' });

Builder

Complex object construction:

class QueryBuilder {
  constructor() {
    this.query = {};
  }
  
  select(fields) {
    this.query.select = fields;
    return this;
  }
  
  from(table) {
    this.query.from = table;
    return this;
  }
  
  where(condition) {
    this.query.where = condition;
    return this;
  }
  
  build() {
    return `SELECT ${this.query.select} FROM ${this.query.from} WHERE ${this.query.where}`;
  }
}

const query = new QueryBuilder()
  .select('*')
  .from('users')
  .where('age > 18')
  .build();

Structural Patterns

Decorator

Dinamik olarak functionality ekler:

class Coffee {
  cost() {
    return 5;
  }
  
  description() {
    return 'Coffee';
  }
}

class MilkDecorator {
  constructor(coffee) {
    this.coffee = coffee;
  }
  
  cost() {
    return this.coffee.cost() + 2;
  }
  
  description() {
    return this.coffee.description() + ', Milk';
  }
}

class SugarDecorator {
  constructor(coffee) {
    this.coffee = coffee;
  }
  
  cost() {
    return this.coffee.cost() + 1;
  }
  
  description() {
    return this.coffee.description() + ', Sugar';
  }
}

let myCoffee = new Coffee();
myCoffee = new MilkDecorator(myCoffee);
myCoffee = new SugarDecorator(myCoffee);

console.log(myCoffee.description()); // Coffee, Milk, Sugar
console.log(myCoffee.cost()); // 8

Adapter

Incompatible interface’leri uyumlu hale getirir:

// Old API
class OldPaymentSystem {
  processOldPayment(amount) {
    console.log(`Processing $${amount} via old system`);
  }
}

// New API
class NewPaymentSystem {
  processPayment({ amount, currency }) {
    console.log(`Processing ${amount} ${currency}`);
  }
}

// Adapter
class PaymentAdapter {
  constructor(oldSystem) {
    this.oldSystem = oldSystem;
  }
  
  processPayment({ amount, currency }) {
    // Convert new format to old format
    this.oldSystem.processOldPayment(amount);
  }
}

const oldSystem = new OldPaymentSystem();
const adapter = new PaymentAdapter(oldSystem);
adapter.processPayment({ amount: 100, currency: 'USD' });

Behavioral Patterns

Observer (Pub/Sub)

Event-driven communication:

class EventEmitter {
  constructor() {
    this.events = {};
  }
  
  on(event, callback) {
    if (!this.events[event]) {
      this.events[event] = [];
    }
    this.events[event].push(callback);
  }
  
  emit(event, data) {
    if (this.events[event]) {
      this.events[event].forEach(callback => callback(data));
    }
  }
  
  off(event, callback) {
    if (this.events[event]) {
      this.events[event] = this.events[event].filter(cb => cb !== callback);
    }
  }
}

const emitter = new EventEmitter();

emitter.on('userLoggedIn', (user) => {
  console.log(`Welcome ${user.name}!`);
});

emitter.on('userLoggedIn', (user) => {
  console.log('Logging user activity...');
});

emitter.emit('userLoggedIn', { name: 'John' });

Strategy

Algorithm’ları runtime’da değiştir:

class PaymentStrategy {
  pay(amount) {
    throw new Error('Must implement pay method');
  }
}

class CreditCardPayment extends PaymentStrategy {
  pay(amount) {
    console.log(`Paid $${amount} with credit card`);
  }
}

class PayPalPayment extends PaymentStrategy {
  pay(amount) {
    console.log(`Paid $${amount} with PayPal`);
  }
}

class CryptoPayment extends PaymentStrategy {
  pay(amount) {
    console.log(`Paid $${amount} with cryptocurrency`);
  }
}

class PaymentProcessor {
  constructor(strategy) {
    this.strategy = strategy;
  }
  
  setStrategy(strategy) {
    this.strategy = strategy;
  }
  
  processPayment(amount) {
    this.strategy.pay(amount);
  }
}

const processor = new PaymentProcessor(new CreditCardPayment());
processor.processPayment(100);

processor.setStrategy(new PayPalPayment());
processor.processPayment(200);

Command

Request’leri object olarak encapsulate et:

class Command {
  execute() {}
  undo() {}
}

class AddTextCommand extends Command {
  constructor(editor, text) {
    super();
    this.editor = editor;
    this.text = text;
  }
  
  execute() {
    this.editor.content += this.text;
  }
  
  undo() {
    this.editor.content = this.editor.content.slice(0, -this.text.length);
  }
}

class TextEditor {
  constructor() {
    this.content = '';
    this.history = [];
  }
  
  executeCommand(command) {
    command.execute();
    this.history.push(command);
  }
  
  undo() {
    const command = this.history.pop();
    if (command) command.undo();
  }
}

const editor = new TextEditor();
editor.executeCommand(new AddTextCommand(editor, 'Hello '));
editor.executeCommand(new AddTextCommand(editor, 'World!'));
console.log(editor.content); // Hello World!

editor.undo();
console.log(editor.content); // Hello

Design patterns ile maintainable kod yazın!