Главная/Статьи/Паттерны проектирования в JavaScript c примерами
JavaScript

Паттерны проектирования в JavaScript c примерами

Введение

Паттерны представляют собой шаблоны и подходы для решения типичных задач разработки. Они способствуют созданию кода, который легче понимать, модифицировать и расширять. По сути, паттерны — это соглашения о стандартном стиле программирования.

Зачем нужны паттерны проектирования?

Паттерны помогают разработчикам создавать понятный и качественный код. Они облегчают поддержку и развитие проектов спустя годы после написания, предотвращая проблемы с устаревшим кодом.

Кроме того, паттерны обеспечивают общую терминологию между разработчиками. При работе с кодом других программистов знание паттернов ускорит освоение его структуры. Командная работа становится эффективнее благодаря единому стилю.

Паттерн Модуль

Модуль — наиболее распространённый паттерн в JavaScript. Он позволяет инкапсулировать код с закрытыми и открытыми методами.

Пример с корзиной покупок:

var ShoppingCart = (function() {
  var items = [];

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

  function removeItem(item) {
    var index = items.indexOf(item);
    if (index !== -1) {
      items.splice(index, 1);
    }
  }

  function getItems() {
    return items;
  }

  function getTotal() {
    return items.reduce(function(total, item) {
      return total + item.price;
    }, 0);
  }

  // Раскрытие публичных методов
  return {
    addItem: addItem,
    removeItem: removeItem,
    getItems: getItems,
    getTotal: getTotal
  };
})();

// Использование:
ShoppingCart.addItem({ name: 'Product 1', price: 10 });
ShoppingCart.addItem({ name: 'Product 2', price: 20 });
console.log(ShoppingCart.getItems()); // возвращает [{ name: 'Product 1', price: 10 }, { name: 'Product 2', price: 20 }]
console.log(ShoppingCart.getTotal()); // возвращает 30

Модуль содержит приватные данные (items) и публичные методы API: addItem(), removeItem(), getItems(), getTotal().

Методы addItem() и removeItem() работают с массивом товаров. getItems() возвращает список товаров, а getTotal() вычисляет их общую стоимость.

В реальной реализации может быть намного больше методов и логики: хранение в LocalStorage или Redis, работа с опциями товаров и т.д.

Паттерн Открытый модуль

Открытый модуль идентичен предыдущему паттерну, но позволяет выборочно раскрывать определённые свойства как публичные, скрывая остальные.

Пример:

var myModule = (function() {
  var privateVariable = "Hello, world!";

  function privateMethod() {
    console.log(privateVariable);
  }

  function publicMethod() {
    privateMethod();
  }

  return {
    publicMethod: publicMethod
  };
})();

myModule.publicMethod();

Паттерн Конструктор

Паттерн конструктора позволяет создавать объекты с собственными уникальными свойствами и методами. Он используется для создания множества экземпляров с одинаковым поведением и разными данными.

Функция конструктора определяет свойства и методы объекта. Ключевое слово new создаёт новые экземпляры.

Пример:

function Person(name, age) {
  this.name = name;
  this.age = age;
}

Person.prototype.sayHello = function() {
  console.log("Hello, my name is " + this.name);
};

var john = new Person("John", 30);
var jane = new Person("Jane", 25);

john.sayHello(); // возвращает "Hello, my name is John"
jane.sayHello(); // возвращает "Hello, my name is Jane"

Паттерн Фабрика

Фабрика похожа на конструктор, но создаёт объекты на основе критериев, таких как входные параметры или конфигурация. Функция фабрики возвращает новый объект.

Пример:

function createPerson(name, age, gender) {
  var person = {};

  person.name = name;
  person.age = age;
  person.gender = gender;

  person.sayHello = function() {
    console.log("Hello, my name is " + this.name);
  };

  if (gender === "male") {
    person.favoriteColor = "blue";
  } else if (gender === "female") {
    person.favoriteColor = "pink";
  } else {
    person.favoriteColor = "green";
  }

  return person;
}

var john = createPerson("John", 30, "male");
var jane = createPerson("Jane", 25, "female");

john.sayHello(); // возвращает "Hello, my name is John"
console.log(john.favoriteColor); // возвращает "blue"
jane.sayHello(); // возвращает "Hello, my name is Jane"
console.log(jane.favoriteColor); // возвращает "pink"

Различие между Конструктором и Фабрикой:

Фабрика обеспечивает большую гибкость, когда тип создаваемого объекта заранее неизвестен. Конструктор более жесткий и применяется, когда тип объекта чётко определен.

Паттерн Синглтон

Синглтон обеспечивает создание только одного экземпляра объекта и предоставляет глобальную точку доступа к нему.

Паттерн идеален для хранения глобальных настроек приложения и подключения к базе данных.

Пример для конфигурации:

var Config = (function() {
  var instance;

  function init() {
    // private properties and methods
    var config = {
      apiUrl: "http://api.example.com",
      maxRetries: 3
    };

    // public properties and methods
    return {
      getApiUrl: function() {
        return config.apiUrl;
      },
      getMaxRetries: function() {
        return config.maxRetries;
      }
    };
  }

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

// Usage:
var config1 = Config.getInstance();
var config2 = Config.getInstance();

console.log(config1 === config2); // возвращает true
console.log(config1.getApiUrl()); // возвращает "http://api.example.com"
console.log(config1.getMaxRetries()); // возвращает 3

Функция init() определяет приватные и публичные свойства объекта-одиночки. Методы getApiUrl() и getMaxRetries() получают значения конфига. Синглтон обеспечивает согласованность параметров конфигурации по всему коду.

Паттерн Декоратор

Декоратор добавляет новую функциональность к существующему объекту. Объект-декоратор оборачивается вокруг исходного объекта и вызывает его методы, добавляя собственные.

Пример:

// оригинальный объект
function Coffee() {
  this.cost = function() {
    return 2.0;
  };
}

// декоратор
function Milk(coffee) {
  this.cost = function() {
    return coffee.cost() + 0.5;
  };
}

// декоратор
function Sugar(coffee) {
  this.cost = function() {
    return coffee.cost() + 0.2;
  };
}

// использование
var coffee = new Coffee();
coffee = new Milk(coffee);
coffee = new Sugar(coffee);

console.log(coffee.cost()); // возвращает 2.7

Декораторы Milk и Sugar оборачиваются вокруг объекта Coffee, вызывая исходный метод cost и добавляя свою стоимость. Результат объединяет функциональность исходного объекта и всех декораторов.

Выводы

Паттерны проектирования — полезные инструменты для решения типичных проблем и улучшения качества кода. При правильном применении они позволяют создавать гибкий, масштабируемый и модульный код, легко поддерживаемый годы после написания.

Выбор паттерна зависит от конкретных требований приложения и должен использоваться с понимаем преимуществ и ограничений.