阿里云主机折上折
  • 微信号
Current Site:Index > Design patterns implement the translation of this sentence into English.

Design patterns implement the translation of this sentence into English.

Author:Chuan Chen 阅读数:50192人阅读 分类: JavaScript

Design patterns are reusable solutions to common problems in software development. In JavaScript, design patterns help developers build more flexible and maintainable code structures. Below, we explore several common design patterns and their implementations.

Singleton Pattern

The Singleton pattern ensures that a class has only one instance and provides a global access point to it. In JavaScript, it can be implemented using closures or modules.

const Singleton = (function() {
  let instance;

  function createInstance() {
    return {
      name: 'Singleton Instance',
      log: function() {
        console.log(this.name);
      }
    };
  }

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

const instance1 = Singleton.getInstance();
const instance2 = Singleton.getInstance();
console.log(instance1 === instance2); // true

Observer Pattern

The Observer pattern defines a one-to-many dependency between objects, where all dependent objects are notified when the state of one object changes.

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(`Received data: ${data}`);
  }
}

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

subject.subscribe(observer1);
subject.subscribe(observer2);
subject.notify('Hello Observers!');

Factory Pattern

The Factory pattern provides an interface for creating objects, allowing subclasses to decide which class to instantiate. In JavaScript, it can be implemented using functions or classes.

class Car {
  constructor(options) {
    this.type = options.type || 'sedan';
    this.color = options.color || 'white';
  }
}

class Truck {
  constructor(options) {
    this.type = options.type || 'truck';
    this.color = options.color || 'black';
  }
}

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

const factory = new VehicleFactory();
const myCar = factory.createVehicle({
  vehicleType: 'car',
  color: 'red'
});

Strategy Pattern

The Strategy pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. This pattern allows the algorithm to vary independently from clients that use it.

const strategies = {
  add: (a, b) => a + b,
  subtract: (a, b) => a - b,
  multiply: (a, b) => a * b,
  divide: (a, b) => a / b
};

class Calculator {
  constructor(strategy) {
    this.strategy = strategy;
  }

  execute(a, b) {
    return strategies[this.strategy](a, b);
  }
}

const calc = new Calculator('add');
console.log(calc.execute(5, 3)); // 8

Decorator Pattern

The Decorator pattern dynamically adds responsibilities to an object, offering more flexibility than inheritance. In JavaScript, it can be implemented using higher-order functions or ES7 decorator syntax.

function withLogging(fn) {
  return function(...args) {
    console.log(`Calling function with args: ${args}`);
    const result = fn.apply(this, args);
    console.log(`Function returned: ${result}`);
    return result;
  };
}

function add(a, b) {
  return a + b;
}

const loggedAdd = withLogging(add);
loggedAdd(2, 3);

Adapter Pattern

The Adapter pattern converts the interface of a class into another interface clients expect, enabling classes with incompatible interfaces to work together.

class OldCalculator {
  operations(a, b, operation) {
    switch(operation) {
      case 'add':
        return a + b;
      case 'sub':
        return a - b;
      default:
        return NaN;
    }
  }
}

class NewCalculator {
  add(a, b) {
    return a + b;
  }
  subtract(a, b) {
    return a - b;
  }
}

class CalculatorAdapter {
  constructor() {
    this.calculator = new NewCalculator();
  }

  operations(a, b, operation) {
    switch(operation) {
      case 'add':
        return this.calculator.add(a, b);
      case 'sub':
        return this.calculator.subtract(a, b);
      default:
        return NaN;
    }
  }
}

const oldCalc = new OldCalculator();
console.log(oldCalc.operations(5, 3, 'add'));

const adapter = new CalculatorAdapter();
console.log(adapter.operations(5, 3, 'add'));

Proxy Pattern

The Proxy pattern provides a surrogate or placeholder for another object to control access to it. In JavaScript, the Proxy object natively supports this pattern.

const person = {
  name: 'John',
  age: 30
};

const personProxy = new Proxy(person, {
  get(target, property) {
    console.log(`Getting ${property}`);
    return target[property];
  },
  set(target, property, value) {
    console.log(`Setting ${property} to ${value}`);
    target[property] = value;
    return true;
  }
});

console.log(personProxy.name);
personProxy.age = 31;

State Pattern

The State pattern allows an object to alter its behavior when its internal state changes, making it appear as if the object changed its class.

class TrafficLight {
  constructor() {
    this.states = {
      red: new RedLight(this),
      yellow: new YellowLight(this),
      green: new GreenLight(this)
    };
    this.currentState = this.states.red;
  }

  change(state) {
    this.currentState = this.states[state];
  }

  show() {
    this.currentState.show();
  }
}

class LightState {
  constructor(light) {
    this.light = light;
  }
}

class RedLight extends LightState {
  show() {
    console.log('Red Light - Stop');
    setTimeout(() => {
      this.light.change('green');
    }, 3000);
  }
}

class GreenLight extends LightState {
  show() {
    console.log('Green Light - Go');
    setTimeout(() => {
      this.light.change('yellow');
    }, 3000);
  }
}

class YellowLight extends LightState {
  show() {
    console.log('Yellow Light - Wait');
    setTimeout(() => {
      this.light.change('red');
    }, 1000);
  }
}

const trafficLight = new TrafficLight();
trafficLight.show();

Composite Pattern

The Composite pattern composes objects into tree structures to represent part-whole hierarchies, allowing clients to treat individual objects and compositions uniformly.

class Component {
  constructor(name) {
    this.name = name;
  }

  add(component) {
    throw new Error('Not implemented');
  }

  remove(component) {
    throw new Error('Not implemented');
  }

  display(depth) {
    throw new Error('Not implemented');
  }
}

class Leaf extends Component {
  constructor(name) {
    super(name);
  }

  display(depth) {
    console.log('-'.repeat(depth) + this.name);
  }
}

class Composite extends Component {
  constructor(name) {
    super(name);
    this.children = [];
  }

  add(component) {
    this.children.push(component);
  }

  remove(component) {
    const index = this.children.indexOf(component);
    if (index !== -1) {
      this.children.splice(index, 1);
    }
  }

  display(depth) {
    console.log('-'.repeat(depth) + this.name);
    this.children.forEach(child => {
      child.display(depth + 2);
    });
  }
}

const root = new Composite('Root');
const branch1 = new Composite('Branch 1');
const branch2 = new Composite('Branch 2');
const leaf1 = new Leaf('Leaf 1');
const leaf2 = new Leaf('Leaf 2');

root.add(branch1);
root.add(branch2);
branch1.add(leaf1);
branch2.add(leaf2);

root.display(0);

Command Pattern

The Command pattern encapsulates a request as an object, thereby allowing parameterization of clients with different requests, queuing, or logging, and supporting undoable operations.

class Calculator {
  constructor() {
    this.currentValue = 0;
    this.history = [];
  }

  executeCommand(command) {
    this.currentValue = command.execute(this.currentValue);
    this.history.push(command);
  }

  undo() {
    const command = this.history.pop();
    if (command) {
      this.currentValue = command.undo(this.currentValue);
    }
  }
}

class AddCommand {
  constructor(value) {
    this.value = value;
  }

  execute(currentValue) {
    return currentValue + this.value;
  }

  undo(currentValue) {
    return currentValue - this.value;
  }
}

class SubtractCommand {
  constructor(value) {
    this.value = value;
  }

  execute(currentValue) {
    return currentValue - this.value;
  }

  undo(currentValue) {
    return currentValue + this.value;
  }
}

const calculator = new Calculator();
calculator.executeCommand(new AddCommand(10));
calculator.executeCommand(new SubtractCommand(5));
console.log(calculator.currentValue); // 5
calculator.undo();
console.log(calculator.currentValue); // 10

Template Method Pattern

The Template Method pattern defines the skeleton of an algorithm in an operation, deferring some steps to subclasses. It lets subclasses redefine certain steps without changing the algorithm's structure.

class DataProcessor {
  process() {
    this.loadData();
    this.validateData();
    this.analyzeData();
    this.reportResults();
  }

  loadData() {
    throw new Error('loadData must be implemented');
  }

  validateData() {
    console.log('Default validation');
  }

  analyzeData() {
    throw new Error('analyzeData must be implemented');
  }

  reportResults() {
    console.log('Default reporting');
  }
}

class CSVProcessor extends DataProcessor {
  loadData() {
    console.log('Loading CSV data');
  }

  analyzeData() {
    console.log('Analyzing CSV data');
  }
}

class JSONProcessor extends DataProcessor {
  loadData() {
    console.log('Loading JSON data');
  }

  analyzeData() {
    console.log('Analyzing JSON data');
  }

  reportResults() {
    console.log('Generating JSON report');
  }
}

const csvProcessor = new CSVProcessor();
csvProcessor.process();

const jsonProcessor = new JSONProcessor();
jsonProcessor.process();

Iterator Pattern

The Iterator pattern provides a way to access the elements of an aggregate object sequentially without exposing its underlying representation.

class Iterator {
  constructor(items) {
    this.index = 0;
    this.items = items;
  }

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

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

class Collection {
  constructor() {
    this.items = [];
  }

  addItem(item) {
    this.items.push(item);
  }

  getIterator() {
    return new Iterator(this.items);
  }
}

const collection = new Collection();
collection.addItem('Item 1');
collection.addItem('Item 2');
collection.addItem('Item 3');

const iterator = collection.getIterator();
while(iterator.hasNext()) {
  console.log(iterator.next());
}

Mediator Pattern

The Mediator pattern defines an object that encapsulates how a set of objects interact, promoting loose coupling by keeping objects from referring to each other explicitly.

class ChatRoom {
  showMessage(user, message) {
    const time = new Date();
    const sender = user.getName();
    console.log(`${time} [${sender}]: ${message}`);
  }
}

class User {
  constructor(name, chatMediator) {
    this.name = name;
    this.chatMediator = chatMediator;
  }

  getName() {
    return this.name;
  }

  send(message) {
    this.chatMediator.showMessage(this, message);
  }
}

const chatRoom = new ChatRoom();
const user1 = new User('John', chatRoom);
const user2 = new User('Jane', chatRoom);

user1.send('Hi there!');
user2.send('Hey!');

Memento Pattern

The Memento pattern captures and externalizes an object's internal state without violating encapsulation, allowing the object to be restored to this state later.

class EditorMemento {
  constructor(content) {
    this.content = content;
  }

  getContent() {
    return this.content;
  }
}

class Editor {
  constructor() {
    this.content = '';
    this.history = [];
    this.currentState = -1;
  }

  type(text) {
    this.content += text;
  }

  save() {
    const memento = new EditorMemento(this.content);
    this.history.push(memento);
    this.currentState = this.history.length - 1;
    return memento;
  }

  restore(memento) {
    if (memento) {
      this.content = memento.getContent();
    }
  }

  undo() {
    if (this.currentState > 0) {
      this.currentState--;
      this.content = this.history[this.currentState].getContent();
    }
  }

  redo() {
    if (this.currentState < this.history.length - 1) {
      this.currentState++;
      this.content = this.history[this.currentState].getContent();
    }
  }
}

const editor = new Editor();
editor.type('First line\n');
editor.save();
editor.type('Second line\n');
editor.save();
editor.type('Third line\n');

console.log(editor.content);
editor.undo();
console.log(editor.content);
editor.redo();
console.log(editor.content);

Visitor Pattern

The Visitor pattern represents an operation to be performed on elements of an object structure, letting you define new operations without changing the classes of the elements.

class Employee {
  constructor(name, salary) {
    this.name = name;
    this.salary = salary;
  }

  accept(visitor) {
    visitor.visit(this);
  }
}

class Department {
  constructor() {
    this.employees = [];
  }

  addEmployee(employee) {
    this.employees.push(employee);
  }

  accept(visitor) {
    this.employees.forEach(employee => {
      employee.accept(visitor);
    });
  }
}

class SalaryVisitor {
  visit(employee) {
    employee.salary = Math.floor(employee.salary * 1.1);
    console.log(`${employee.name}'s new salary: ${employee.salary}`);
  }
}

class NameVisitor {
  visit(employee) {
    console.log(`Employee name: ${employee.name}`);
  }
}

const dept = new Department();
dept.addEmployee(new Employee('John', 50000));
dept.addEmployee(new Employee('Jane', 60000));

const salaryVisitor = new SalaryVisitor();
dept.accept(salaryVisitor);

const nameVisitor = new NameVisitor();
dept.accept(nameVisitor);

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

上一篇:函数式编程基础

下一篇:性能优化技巧

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.