/ Gists / JS OOP Design Patterns
On gists

JS OOP Design Patterns

JavaScript-OOP JavaScript JS Patterns

patterns.js Raw #

/*

https://blog.carlosrojas.dev/quick-reference-guide-to-design-patterns-in-js-1ebeb1e1c605

*/


/* Singleton */
class Animal {
  constructor() {
    if (typeof Animal.instance === 'object') {
      return Animal.instance;
    }

    Animal.instance = this;

    return this;
  }
}
export default Animal;


/* Prototype */
class Fruit {
  constructor(name, weight) {
    this.name = name;
    this.weight = weight;
  }

  clone() {
    return new Fruit(this.name, this.weight);
  }
}
export default Fruit;

const apple = new Fruit('Apple', 150);
const clonedApple = apple.clone();
console.log(apple);        // Originální ovoce
console.log(clonedApple);  // Klonované ovoce


/* Factory */
class MovieFactory {
  create(genre) {
    if (genre === 'Adventure') return new Movie(genre, 10000);
    if (genre === 'Action') return new Movie(genre, 11000);
  }
}

class Movie {
  constructor(type, price) {
    this.type = type;
    this.price = price;
  }
}
export default MovieFactory;


/* Abstract Factory */
function foodProducer(kind) {
  if (kind === 'protein') return proteinPattern;
  if (kind === 'fat') return fatPattern;
  return carbohydratesPattern;
}

function proteinPattern() {
  return new Protein();
}

function fatPattern() {
  return new Fat();
}

function carbohydratesPattern() {
  return new Carbohydrates();
}

class Protein {
  info() {
    return 'I am Protein.';
  }
}

class Fat {
  info() {
    return 'I am Fat.';
  }
}

class Carbohydrates {
  info() {
    return 'I am carbohydrates.';
  }
}
export default foodProducer;


/* Adapter */
class Soldier {
  constructor(level) {
    this.level = level;
  }

  attack() {
    return this.level * 1;
  }
}

class SuperSoldier {
  constructor(level) {
    this.level = level;
  }

  attackWithShield() {
    return this.level * 10;
  }
}

class SoldierAdapter {
  constructor(superSoldier) {
    this.superSoldier = superSoldier;
  }

  attack() {
    return this.superSoldier.attackWithShield();
  }
}
export { Soldier, SuperSoldier, SoldierAdapter };

// Vytvoření instance SuperSoldier
const superSoldier = new SuperSoldier(5);

// Vytvoření instance SoldierAdapter, který přijímá SuperSoldier jako argument
const soldierAdapter = new SoldierAdapter(superSoldier);

// Volání metody attack() na SoldierAdapteru, která skutečně volá attackWithShield() u SuperSoldiera
const result = soldierAdapter.attack();

console.log(result);  // Vypíše: 50 (5 * 10)



/* Bridge */


class Soldier {
  constructor(weapon) {
    this.weapon = weapon;
  }
}

class SuperSoldier extends Soldier {
  constructor(weapon) {
    super(weapon);
  }

  attack() {
    return 'SuperSoldier, Weapon: ' + this.weapon.get();
  }
}

class IronMan extends Soldier {
  constructor(weapon) {
    super(weapon);
  }

  attack() {
    return 'Ironman, Weapon: ' + this.ink.get();
  }
}

class Weapon {
  constructor(type) {
    this.type = type;
  }

  get() {
    return this.type;
  }
}

class Shield extends Weapon {
  constructor() {
    super('shield');
  }
}

class Rocket extends Weapon {
  constructor() {
    super('rocket');
  }
}
export { SuperSoldier, IronMan, Shield, Rocket };

// Vytvoření instancí zbraní
const shield = new Shield();
const rocket = new Rocket();

// Vytvoření instancí vojáků s různými zbraněmi
const superSoldier = new SuperSoldier(shield);
const ironMan = new IronMan(rocket);

// Útoky vojáků
console.log(superSoldier.attack());  // Vypíše: SuperSoldier, Weapon: shield
console.log(ironMan.attack());       // Vypíše: Ironman, Weapon: rocket


/* Composite */
//Equipment
class Equipment {
  getPrice() {
    return this.price || 0;
  }

  getName() {
    return this.name;
  }

  setName(name) {
    this.name = name;
  }
}

class Pattern extends Equipment {
  constructor() {
    super();
    this.equipments = [];
  }

  add(equipment) {
    this.equipments.push(equipment);
  }

  getPrice() {
    return this.equipments
      .map(equipment => {
        return equipment.getPrice();
      })
      .reduce((a, b) => {
        return a + b;
      });
  }
}

class Cabbinet extends Pattern {
  constructor() {
    super();
    this.setName('cabbinet');
  }
}

// --- leafs ---
class FloppyDisk extends Equipment {
  constructor() {
    super();
    this.setName('Floppy Disk');
    this.price = 70;
  }
}

class HardDrive extends Equipment {
  constructor() {
    super();
    this.setName('Hard Drive');
    this.price = 250;
  }
}

class Memory extends Equipment {
  constructor() {
    super();
    this.setName('Memory');
    this.price = 280;
  }
}

export { Cabbinet, FloppyDisk, HardDrive, Memory };

// Vytvoření jednotlivých listových tříd (FloppyDisk, HardDrive, Memory)
const floppyDisk = new FloppyDisk();
const hardDrive = new HardDrive();
const memory = new Memory();

// Vytvoření skříně a přidání jednotlivých listových tříd do skříně
const cabbinet = new Cabbinet();
cabbinet.add(floppyDisk);
cabbinet.add(hardDrive);
cabbinet.add(memory);

// Získání celkové ceny skříně
const totalPrice = cabbinet.getPrice();
console.log(`Total Price: ${totalPrice}`);  // Vypíše: Total Price: 600


/* Decorator */
class Notification {
  constructor(kind) {
    this.kind = kind || "Generic";
  }
  
  getInfo() {
    return `I'm a ${this.kind} Notification`;
  }
}

class FacebookNotification extends Notification {
  constructor() {
    super("Facebook");
  }
  
  setNotification(msg) {
    this.message = msg;
  }
  
  getInfo() {
    return `${super.getInfo()} with the message: ${this.message}`;
  }
}

class SMSNotification extends Notification {
  constructor() {
    super("SMS");
  }
  
  getInfo() {
    return super.getInfo();
  }
}
export { FacebookNotification, SMSNotification };

// Vytvoření instance SMSNotification
const smsNotification = new SMSNotification();
// Dekorace SMSNotification pomocí FacebookNotificationDecorator
const decoratedNotification = new FacebookNotificationDecorator(smsNotification);
// Získání informací o dekorované notifikaci
console.log(decoratedNotification.getInfo());
// Vypíše: I'm a SMS Notification decorated with Facebook feature


/* Facade */
class Cart {
  constructor() {
    this.discount = new Discount();
    this.shipping = new Shipping();
    this.fees = new Fees();
  }

  calc(price) {
    price = this.discount.calc(price);
    price = this.fees.calc(price);
    price += this.shipping.calc();

    return price;
  }
}

class Discount {
  calc(value) {
    return value * 0.85;
  }
}

class Shipping {
  calc() {
    return 500;
  }
}

class Fees {
  calc(value) {
    return value * 1.1;
  }
}

export default Cart;


/* FlyWeight */
class Ingredient {
  constructor(name) {
    this.name = name;
  }
  
  getInfo() {
    return `I'm a ${this.name}`
  }
}

class Ingredients {
  constructor() {
    this.ingredients = {};
  }

  create(name) {
    let ingredient = this.ingredients[name];
    if (ingredient) return ingredient;

    this.ingredients[name] = new Ingredient(name);

    return this.ingredients[name];
  }
}
export { Ingredients };


/* Proxy */
class Plane {
  fly() {
    return 'flying';
  }
}

class PilotProxy {
  constructor(pilot) {
    this.pilot = pilot;
  }

  fly() {
    return this.pilot.age < 18 ? `too young to fly` : new Plane().fly();
  }
}

class Pilot {
  constructor(age) {
    this.age = age;
  }
}

export { Plane, PilotProxy, Pilot };


/* Iterator */
class Iterator {
  constructor(el) {
    this.index = 0;
    this.elements = el;
  }

  next() {
    return this.elements[this.index++];
  }

  hasNext() {
    return this.index < this.elements.length;
  }
}

export default Iterator;

// Importujte váš Iterator
import Iterator from './Iterator';

// Vytvořte nějakou kolekci dat (v tomto případě pole).
const collection = ['Prvek 1', 'Prvek 2', 'Prvek 3'];

// Vytvořte instanci Iteratoru, předávejte kolekci jako argument konstruktoru.
const iterator = new Iterator(collection);

// Projděte kolekci pomocí Iteratoru.
while (iterator.hasNext()) {
  const element = iterator.next();
  console.log(element);
}



/* Mediator */

class TrafficTower {
  constructor() {
    this.airplanes = [];
  }

  getPositions() {
    return this.airplanes.map(airplane => {
      return airplane.position.showPosition();
    });
  }
}

class Airplane {
  constructor(position, trafficTower) {
    this.position = position;
    this.trafficTower = trafficTower;
    this.trafficTower.airplanes.push(this);
  }

  getPositions() {
    return this.trafficTower.getPositions();
  }
}

class Position {
  constructor(x,y) {
    this.x = x;
    this.y = y;
  }
  
  showPosition() {
    return `My Position is ${x} and ${y}`;
  }
}

export { TrafficTower, Airplane, Position };

const trafficTower = new TrafficTower();

const airplane1 = new Airplane(new Position(10, 20), trafficTower);
const airplane2 = new Airplane(new Position(30, 40), trafficTower);

// Informovat TrafficTower o pozici letadla.
airplane1.getPositions();

// Získat informace o pozicích ostatních letadel pomocí TrafficTower.
airplane2.getPositions();



/* State */
class OrderStatus {
  constructor(name, nextStatus) {
    this.name = name;
    this.nextStatus = nextStatus;
  }

  next() {
    return new this.nextStatus();
  }
}

class WaitingForPayment extends OrderStatus {
  constructor() {
    super('waitingForPayment', Shipping);
  }
}

class Shipping extends OrderStatus {
  constructor() {
    super('shipping', Delivered);
  }
}

class Delivered extends OrderStatus {
  constructor() {
    super('delivered', Delivered);
  }
}

class Order {
  constructor() {
    this.state = new WaitingForPayment();
  }

  nextPattern() {
    this.state = this.state.next();
  }
}

export default Order;


// Vytvořte instanci objednávky
const order = new Order();

// Zjistěte aktuální stav
console.log(order.state.name); // Výstup: 'waitingForPayment'

// Přejděte na další stav
order.nextPattern();

// Zjistěte nový stav
console.log(order.state.name); // Výstup: 'shipping'

// Přejděte na další stav
order.nextPattern();

// Zjistěte nový stav
console.log(order.state.name); // Výstup: 'delivered'



/* Strategy */
class ShoppingCart {
  constructor(discount) {
    this.discount = discount;
    this.amount = 0;
  }

  checkout() {
    return this.discount(this.amount);
  }

  setAmount(amount) {
    this.amount = amount;
  }
}

function guest(amount) {
  return amount;
}

function regular(amount) {
  return amount * 0.9;
}

function premium(amount) {
  return amount * 0.8;
}

export { ShoppingCart, guest, regular, premium };

// Importujte třídu ShoppingCart a slevové strategie
import { ShoppingCart, guest, regular, premium } from './ShoppingCart';

// Vytvořte instanci nákupního košíku s různými slevovými strategiemi
const cartGuest = new ShoppingCart(guest);
const cartRegular = new ShoppingCart(regular);
const cartPremium = new ShoppingCart(premium);

// Nastavte množství v košíku
cartGuest.setAmount(100);
cartRegular.setAmount(100);
cartPremium.setAmount(100);

// Proveďte platbu s různými slevovými strategiemi
console.log(cartGuest.checkout()); // Výstup: 100 (žádná sleva)
console.log(cartRegular.checkout()); // Výstup: 90 (10% sleva pro běžného zákazníka)
console.log(cartPremium.checkout()); // Výstup: 80 (20% sleva pro prémiového zákazníka)



/* Template */
class Tax {
  calc(value) {
    if (value >= 1000) value = this.overThousand(value);

    return this.complementaryFee(value);
  }

  complementaryFee(value) {
    return value + 10;
  }
}

class VAT extends Tax {
  constructor() {
    super();
  }

  overThousand(value) {
    return value * 1.1;
  }
}

class GST extends Tax {
  constructor() {
    super();
  }

  overThousand(value) {
    return value * 1.2;
  }
}

export { VAT, GST };


// Importujte třídy VAT a GST
import { VAT, GST } from './Tax';

// Vytvořte instanci třídy VAT a provedte výpočet daně
const vatTax = new VAT();
console.log(vatTax.calc(1200)); // Výstup: 1320 (včetně přirážky pro hodnoty nad 1000)

// Vytvořte instanci třídy GST a provedte výpočet daně
const gstTax = new GST();
console.log(gstTax.calc(1200)); // Výstup: 1440 (včetně přirážky pro hodnoty nad 1000)