Введение
Паттерны представляют собой шаблоны и подходы для решения типичных задач разработки. Они способствуют созданию кода, который легче понимать, модифицировать и расширять. По сути, паттерны — это соглашения о стандартном стиле программирования.
Зачем нужны паттерны проектирования?
Паттерны помогают разработчикам создавать понятный и качественный код. Они облегчают поддержку и развитие проектов спустя годы после написания, предотвращая проблемы с устаревшим кодом.
Кроме того, паттерны обеспечивают общую терминологию между разработчиками. При работе с кодом других программистов знание паттернов ускорит освоение его структуры. Командная работа становится эффективнее благодаря единому стилю.
Паттерн Модуль
Модуль — наиболее распространённый паттерн в 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 и добавляя свою стоимость. Результат объединяет функциональность исходного объекта и всех декораторов.
Выводы
Паттерны проектирования — полезные инструменты для решения типичных проблем и улучшения качества кода. При правильном применении они позволяют создавать гибкий, масштабируемый и модульный код, легко поддерживаемый годы после написания.
Выбор паттерна зависит от конкретных требований приложения и должен использоваться с понимаем преимуществ и ограничений.